Skip to content

fix(uri): check escape sequence bounds on NormalizeEscapedPath slow path - #1732

Open
shuvamk wants to merge 1 commit into
ogen-go:mainfrom
shuvamk:fix/normalize-escaped-path-invalid-escape
Open

fix(uri): check escape sequence bounds on NormalizeEscapedPath slow path#1732
shuvamk wants to merge 1 commit into
ogen-go:mainfrom
shuvamk:fix/normalize-escaped-path-invalid-escape

Conversation

@shuvamk

@shuvamk shuvamk commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

A spec whose path key has an escape typo crashes ogen with a raw Go stack trace — but only when an earlier escape in the same path is lowercase or redundant.

"/report%" (no earlier escape) is reported the way a user expects:

  - spec2.json:5:5 -> parse path "/report%": parse "/report%": invalid URL escape "%"

"/caf%e9/100%" — the same trailing %, with a lowercase escape in front of it — instead prints:

panic: runtime error: index out of range [12] with length 12

with uri.NormalizeEscapedPath at uri/normalize.go:98 on top, called from openapi/parser/parser.go:243.

NormalizeEscapedPath documents that it "returns empty string and false" for an invalid escape sequence, and the fast loop does check for one. But once a lowercase hex digit or a redundant escape triggers goto slow, the slow loop reads s[i+1], s[i+2] with no bounds or hex check. openapi/parser/parser.go:243 passes raw spec path keys to it with no url.Parse in front, so the panic reaches the CLI.

The same gap also makes the function return true for input it should reject: on main, NormalizeEscapedPath("/foo%3fbar%zz") returns ("/foo%3Fbar%zz", true).

The fix checks bounds and hex digits before indexing, mirroring the fast loop. With it, "/caf%e9/100%" produces the same located parse error "/report%" already did.

Four rows added to the TestNormalizeEscapedPath table reach the slow path, which the three existing invalid rows do not. I checked they fail with only uri/normalize.go reverted — the truncated ones panic, the %zz one fails on Should be false — and pass with the fix.

go test ./..., ./go.test.sh, cd examples && go test ./... and golangci-lint run are clean locally, and make generate examples leaves a 0-file diff.

NormalizeEscapedPath documents that it returns an empty string and false when s
contains an invalid escape sequence, but only the fast path checks for one. Once
a lowercase hex digit or a redundant escape sends it to the slow path, the
remaining bytes are read without a bounds or hex check.

openapi/parser/parser.go:243 passes raw spec path keys straight to it, so a
document whose path key is "/caf%e9/100%" kills the generator at
uri/normalize.go:98 with a raw stack trace:

  panic: runtime error: index out of range [12] with length 12

while the same typo without the earlier escape, "/report%", is reported the way
a user expects:

  - spec2.json:5:5 -> parse path "/report%": parse "/report%": invalid URL escape "%"

Trailing "%zz" on the slow path did not panic, but returned ("/foo%3Fbar%zz",
true) — a silent accept of an invalid escape. Checking bounds and hex digits
before indexing covers both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant