Fix IPv6 Zone ID decoding to correctly handle RFC 6874 %25 separator#1653
Conversation
Per RFC 6874, an IPv6 Zone ID in a URI is encoded as: IPv6addrz = IPv6address "%25" ZoneID So in 'http://[fe80::1%251]/', the zone ID is '1', not '251'. Previously, _encode_host() split the host on bare '%', treating '251' as the zone ID. The host property also returned the raw (encoded) value unchanged for IP addresses, so %25 was never decoded. Fix _encode_host() to partition on '%25' (RFC 6874 separator) when present, preserving it verbatim in raw_host / str(url), and update the host property to decode '%25' -> '%' so callers receive the human-readable zone identifier (e.g. 'fe80::1%1' / 'fe80::1%eth0'). Tests added for: - Numeric zone ID: http://[fe80::1%251]/ -> host='fe80::1%1' - String zone ID: http://[fe80::1%25eth0]/ -> host='fe80::1%eth0'
a9bd0c5 to
97ac79d
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1653 +/- ##
=======================================
Coverage 99.81% 99.81%
=======================================
Files 21 21
Lines 4249 4286 +37
Branches 250 251 +1
=======================================
+ Hits 4241 4278 +37
Misses 5 5
Partials 3 3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
The codecov/project/typing check is failing at 97.63% against a 100% target, but this is a pre-existing issue unrelated to this PR. |
|
@aiolibsbot review |
PR Review — Fix IPv6 Zone ID decoding to correctly handle RFC 6874 %25 separatorFocused, well-scoped RFC 6874 bugfix. The dual-path encoder cleanly preserves backward compatibility for programmatically constructed hosts while teaching the parser the correct 🟢 Suggestions1. Comment understates what the replace actually does (`yarl/_url.py`, L792-794)The comment says 2. Consider also exercising the bare-% encoder fallback (`tests/test_url.py`, L478-522)The PR description and Checklist
SummaryFocused, well-scoped RFC 6874 bugfix. The dual-path encoder cleanly preserves backward compatibility for programmatically constructed hosts while teaching the parser the correct |
|
@aiolibsbot review |
- Collapse the %25/% partition selection in _encode_host into a single ternary, per @bdraco's suggestion. - Add an inline rationale: %25 is the RFC 6874 separator; bare % is still accepted for hosts constructed programmatically (e.g. via with_host('fe80::1%1')). - Document the %25 -> % decoding in the .host docstring so callers see that the encoded form remains available via raw_host / host_subcomponent. - Broaden tests to cover named zones, zone + port, host_port_subcomponent, authority, human_repr, round-tripping through str(url), and a percent-encoded byte inside the zone identifier.
|
Thanks @rodrigobnogueira |
What do these changes do?
Fixes incorrect decoding of IPv6 Zone IDs in URLs that use the
:rfc:
6874%25zone separator.Two call sites in
yarl/_url.pyare updated:_encode_host(): when%25is present in the host string,partition on
%25(the RFC 6874 separator) instead of bare%. The raw form (raw_host/host_subcomponent/str(url)) is preserved verbatim..hostproperty: when an IP-literal host contains%25,decode it back to
%so callers receive the human-readablezone identifier (e.g.
fe80::1%1rather thanfe80::1%251).The same decoding flows through
.authorityand.human_repr().The pre-fix behaviour, where
_encode_hostsplit on bare%,is still accepted as a fallback so that hosts constructed
programmatically (e.g.
with_host("fe80::1%1")) keep working..raw_host.hostbefore.hostafterhttp://[fe80::1%251]/fe80::1%251fe80::1%251fe80::1%1http://[fe80::1%25eth0]/fe80::1%25eth0fe80::1%25eth0fe80::1%eth0Are there changes in behavior for the user?
Yes.
.host,.authority, and.human_repr()now returnthe decoded zone identifier (
%instead of%25) for IPv6URLs that carry an RFC 6874 zone separator.
raw_host,host_subcomponent, andstr(url)are unchanged, so theon-the-wire representation is preserved.
Is it a substantial burden for the maintainers to support this?
No. The change is localised to two small spots in
yarl/_url.py;the dual-path encoder fallback preserves backward compatibility for
programmatically constructed hosts. While not a practical SSRF
vector (zone IDs are link-local scope only), the previous decoding
disagreed with other RFC 6874-aware parsers.
Related issue number
No linked issue; surfaced while reviewing :rfc:
6874complianceof the host parser.
Checklist
CONTRIBUTORS.txt(N/A; noCONTRIBUTORS.txtin this repo)CHANGES/folder1653.bugfix.rstbugfix