fix: harden against confirmed review findings - #2
Conversation
Fixes four confirmed review findings, each reproduced against CPython
urllib.parse ground truth before and after the change, and covered by
new fixtures/regression cases (fixtures.txt regenerated from CPython).
1. unquote: reject overlong UTF-8 (security). `unquote("%E0%80%AF")`
previously passed the raw invalid bytes `e0 80 af` through; CPython
emits three U+FFFD. The UTF-8 decoder now enforces the lead-byte-
specific second-byte range (RFC 3629 / Unicode Table 3-7), so
overlongs, surrogates (ED A0..BF), and out-of-range 4-byte leads are
rejected instead of decoding to raw/invalid bytes.
2. unquote: Unicode maximal-subpart replacement. A truncated multibyte
escape (e.g. `%E6%97`, `%F0%9F%98`) emitted one U+FFFD per byte;
CPython emits exactly one U+FFFD and resumes past the maximal valid
subpart. The decoder now advances by the consumed subpart length.
3. urljoin: digit-led pseudo-scheme. `urljoin("http://a/b/",
"10:30.html")` returned "10:30.html" (treating "10" as a scheme);
CPython returns "http://a/b/10:30.html". urljoin is now a faithful
port of CPython's algorithm and defers scheme detection to urlparse,
which already requires an alpha-led scheme.
4. urljoin: CPython backward-compat + absolute-ref dot segments.
`urljoin(base, "http:g")` returned "http:g"; CPython resolves a
same-scheme reference relatively -> "http://a/b/c/g". An absolute
reference with a differing scheme is now returned verbatim (no dot-
segment removal), matching CPython (e.g. "http://x/../y" stays as-is).
The RFC 3986 5.4 table row for http:g (dropped to keep 41/41) is
restored with CPython's value, so the table is now 42/42.
Also fixes the pixi Mojo pin (`>=1.0.0b3` sorts below dev nightlies, so
pixi install fails to solve) to `>=1.0.0b3.dev0,<2` as a build
prerequisite. README conformance/fixture counts updated where a fix made
them false (41/41 -> 42/42, 161 -> 173 fixtures) and the urljoin scheme
note corrected to describe the new CPython-matching behavior.
Co-Authored-By: Claude <noreply@anthropic.com>
|
🤖 Independent Claude review: Ready to mark for review — the four target fixes are all correct and well-tested. One low-severity parity gap noted below (non-blocking). Verified (differential-tested against CPython 3.9):
Non-blocking findings:
|
|
Code review (opus, static — verify CI): SHIP-WITH-NITS All four fixes correct. Nits: |
From an automated multi-agent review (personal-context#62); implemented + verified by Claude Code. (PR auto-recovered — the agent completed and pushed this branch but a transient API error interrupted it before it could open the PR.)
fix: harden unquote UTF-8 decode and urljoin CPython parity
Fixes four confirmed review findings, each reproduced against CPython
urllib.parse ground truth before and after the change, and covered by
new fixtures/regression cases (fixtures.txt regenerated from CPython).
unquote: reject overlong UTF-8 (security).
unquote("%E0%80%AF")previously passed the raw invalid bytes
e0 80 afthrough; CPythonemits three U+FFFD. The UTF-8 decoder now enforces the lead-byte-
specific second-byte range (RFC 3629 / Unicode Table 3-7), so
overlongs, surrogates (ED A0..BF), and out-of-range 4-byte leads are
rejected instead of decoding to raw/invalid bytes.
unquote: Unicode maximal-subpart replacement. A truncated multibyte
escape (e.g.
%E6%97,%F0%9F%98) emitted one U+FFFD per byte;CPython emits exactly one U+FFFD and resumes past the maximal valid
subpart. The decoder now advances by the consumed subpart length.
urljoin: digit-led pseudo-scheme.
urljoin("http://a/b/", "10:30.html")returned "10:30.html" (treating "10" as a scheme);CPython returns "http://a/b/10:30.html". urljoin is now a faithful
port of CPython's algorithm and defers scheme detection to urlparse,
which already requires an alpha-led scheme.
urljoin: CPython backward-compat + absolute-ref dot segments.
urljoin(base, "http:g")returned "http:g"; CPython resolves asame-scheme reference relatively -> "http://a/b/c/g". An absolute
reference with a differing scheme is now returned verbatim (no dot-
segment removal), matching CPython (e.g. "http://x/../y" stays as-is).
The RFC 3986 5.4 table row for http:g (dropped to keep 41/41) is
restored with CPython's value, so the table is now 42/42.
Also fixes the pixi Mojo pin (
>=1.0.0b3sorts below dev nightlies, sopixi install fails to solve) to
>=1.0.0b3.dev0,<2as a buildprerequisite. README conformance/fixture counts updated where a fix made
them false (41/41 -> 42/42, 161 -> 173 fixtures) and the urljoin scheme
note corrected to describe the new CPython-matching behavior.
Co-Authored-By: Claude noreply@anthropic.com