fix(fetch): accept what a host label may be, not a list of what it may not #732

Merged
coilyco-ops merged 1 commit from fix/every-label-in-a-wildcard-prefix-is-a-label into main 2026-08-13 21:13:37 +00:00
Member

Nine invalid host-label shapes matched a wildcard allowlist entry. Measured and specified by the filer; this applies it.

-.mozilla.com     -a.mozilla.com    a-.mozilla.com     hyphen at either end
_.mozilla.com     "a b.mozilla.com"                    not hostname characters
a/b.mozilla.com   a:80.mozilla.com                     URL delimiters in a host
*.mozilla.com                                          a star is not a label
aaaa..(64)..a.mozilla.com                              one octet over the limit

The shape of the fix

The guard named bad shapes one at a time: empty prefix, leading dot, doubled dot. Three exclusions left nine standing. validHostLabel accepts what a label may be instead, so the next unlisted shape is refused rather than admitted.

Not exploitable today, and that is not why it ships

allowedURL is the only caller and passes parsed.Hostname(), which strips the port and cannot contain a slash. The filer declined to raise its priority and I agree. What justifies it is the standard already in TestTheHostIsNormalisedInside:

hostAllowed reads as a general predicate, so it must not depend on its caller having lowercased. allowedURL does; the next caller might not.

The slash and colon rows are that sentence one field over.

The landmine, which now has a test rather than a comment

validHostLabel has no uppercase arm because hostAllowed lowercases first. The filer flagged that reordering the two would silently refuse every capitalised host. TestALabelCheckThatRunsAfterLowercasing holds it from both sides: capitalised hosts still match through hostAllowed, and validHostLabel("WWW") is false on purpose.

Verification

ward exec gate PASS on all six steps. The nine rows flip and their issue fields clear, so hostlabelshape_test.go reports 0 rows still disagree. The five must-allow rows stay green, xn--a.mozilla.com included.

Two mutations, both in runs where the package compiles:

  • validHostLabel returning true always: fails TestEveryLabelBeforeTheSuffixIsReal, TestALabelCheckThatRunsAfterLowercasing, TestAnInvalidLabelShape.
  • dropping the lowercase normalisation: fails TestALabelCheckThatRunsAfterLowercasing by name, plus three existing normalisation rows.

closes #726

Nine invalid host-label shapes matched a wildcard allowlist entry. Measured and specified by the filer; this applies it. ``` -.mozilla.com -a.mozilla.com a-.mozilla.com hyphen at either end _.mozilla.com "a b.mozilla.com" not hostname characters a/b.mozilla.com a:80.mozilla.com URL delimiters in a host *.mozilla.com a star is not a label aaaa..(64)..a.mozilla.com one octet over the limit ``` ## The shape of the fix The guard named bad shapes one at a time: empty prefix, leading dot, doubled dot. Three exclusions left nine standing. `validHostLabel` accepts what a label may be instead, so the next unlisted shape is refused rather than admitted. ## Not exploitable today, and that is not why it ships `allowedURL` is the only caller and passes `parsed.Hostname()`, which strips the port and cannot contain a slash. The filer declined to raise its priority and I agree. What justifies it is the standard already in `TestTheHostIsNormalisedInside`: > hostAllowed reads as a general predicate, so it must not depend on its caller having lowercased. allowedURL does; the next caller might not. The slash and colon rows are that sentence one field over. ## The landmine, which now has a test rather than a comment `validHostLabel` has no uppercase arm because `hostAllowed` lowercases first. The filer flagged that reordering the two would silently refuse every capitalised host. `TestALabelCheckThatRunsAfterLowercasing` holds it from both sides: capitalised hosts still match through `hostAllowed`, and `validHostLabel("WWW")` is false on purpose. ## Verification `ward exec gate` PASS on all six steps. The nine rows flip and their `issue` fields clear, so `hostlabelshape_test.go` reports **0 rows still disagree**. The five must-allow rows stay green, `xn--a.mozilla.com` included. Two mutations, both in runs where the package compiles: - `validHostLabel` returning true always: fails `TestEveryLabelBeforeTheSuffixIsReal`, `TestALabelCheckThatRunsAfterLowercasing`, `TestAnInvalidLabelShape`. - dropping the lowercase normalisation: fails `TestALabelCheckThatRunsAfterLowercasing` by name, plus three existing normalisation rows. closes #726
fix(fetch): accept what a host label may be, not a list of what it may not
All checks were successful
ci / image-build (pull_request) Successful in 23s
ci / test (pull_request) Successful in 38s
ci / publish-echo-image (pull_request) Has been skipped
ci / publish-observed (pull_request) Has been skipped
38309ae7c1
Naming bad shapes one at a time left nine standing: hyphen at either end,
underscore, space, slash, colon, a bare star, and a 64-octet label. Three
exclusions, nine survivors. validHostLabel accepts the permitted shape, so an
unlisted one is refused rather than admitted.

Not reachable through allowedURL, which passes parsed.Hostname(). It ships on
the standard already written into TestTheHostIsNormalisedInside: the predicate
must not depend on its caller.

The no-uppercase-arm ordering the filer warned about now has a test rather
than a comment, because its failure mode is silent.

closes #726

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/sirens-echo!732
No description provided.