Repository navigation
Conversation
ipv6() hands its value to ipaddress, which accepts any non-empty zone ID without a "%", so "fe80::1%\0", "fe80::1%\n" and "fe80::1%a b" validated, and through hostname() so did "[fe80::1%a b]:22". RFC 4007 sets no character set for a zone ID, and RFC 9844 (section 6) leaves the checks to each implementation, naming NUL as one to reject. Reject zone IDs containing ASCII or Unicode control characters or whitespace. Numeric and interface-name zone IDs such as "eth0.100" still validate. email() accepted an IPv6 address literal with a zone ID, such as "user@[fe80::1%eth0]". RFC 5321 (4.1.3) defines IPv6-addr without a zone, so a "%" in the domain part is now rejected.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ipv6()passes its value toipaddress, which accepts any zone ID that is non-empty and has no%(docs). So NUL, tab, newline and spaces all validate, andhostname()inherits them:RFC 4007 defines no character set for a zone ID. RFC 9844 section 6, which obsoleted RFC 6874 in August 2025, leaves the checks to each implementation, "appropriate for the environment it is used in", and gives NUL as an example to reject.
email()also accepts an IPv6 address literal with a zone ID, such asuser@[fe80::1%eth0]withipv6_address=True. TheIPv6-addrrule in RFC 5321 section 4.1.3 has no zone.Changes
ipv6()rejects a zone ID containing a control character (Unicode category Cc, which covers C0, DEL and C1) or whitespace (str.isspace()). Numeric and interface-name zone IDs like1,eth0,eth0.100andbr-lanstill validate, with and withoutcidr.hostname("[fe80::1%eth0]:22")is unchanged.email()rejects a%in the domain part. A domain name can't contain one anyway, so only zone IDs in address literals are affected. The check sits right after the split, so it doesn't overlap Accept tagged IPv6 email address literals #526 or Require exactly one pair of brackets around IP literals #535.url()isn't affected: it already rejects raw control characters and whitespace anywhere in the URL before parsing it.Tests
The 27 new rejection cases in
test_ip_address.py,test_hostname.pyandtest_email.pyfail on master and pass here, and the 14 positive cases pass on both. The full suite (1615 tests),ruff check,ruff format,pyrightand bandit pass. A fuzz comparison against master coveredipv6()(with and withoutcidr),hostname()(bare and bracketed with a port),url()andemail(), over 40,000 mutated addresses. It found nothing newly accepted and no change forurl(). Every newly rejected value either has a control or whitespace character in its zone ID, or is an email with a%in its domain part.