Skip to content

Reject a path that does not start with / after a URI authority - #87

Merged
quinnj merged 3 commits into
masterfrom
claude/authority-path
Oct 22, 2026
Merged

quinnj merged 3 commits into
masterfrom
claude/authority-path

Conversation

@quinnj

@quinnj quinnj commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

After an authority, a URI path must be empty or start with / (RFC 3986 §3.3). The URI(; host, path) constructor already enforces this. The string parser did not: whatever followed an authority its pattern could not split ended up in the path.

input before after
http://[localhost]/ host "", path "[localhost]/" ParseError
http://[a]:80/ host "", path "[a]:80/" ParseError
http://[::1]x/ host "::1", path "x/" ParseError
http://foo[bar]/ host "foo", path "[bar]/" ParseError
//[x]y host "", path "[x]y" ParseError

parse_uri_reference now throws ParseError("Invalid URI authority: …") in these cases, so tryparse returns nothing. IPv6 literals followed by a port, path, query, fragment, or nothing still parse, as before. Follows #74.

Error messages. Parse errors no longer show the userinfo, the whole input, or a host or port that a raw @ or / in a password split off. Before, URI("http://user:s3cr3t/x@host/") reported Invalid URI port: s3cr3t. Messages now show the input with everything from the authority up to its last @ replaced by ***, e.g. Invalid URI port in http://***@host/. The keyword constructor's userinfo error and splitfilepath's scheme error no longer echo their argument. The strict-mode round-trip check (parse_uri_reference(str; strict=true)) now throws ParseError with the redacted input, instead of an AssertionError that printed it.

Verification. Cases added to "Invalid Authorities" in test/uri.jl, plus three that must keep parsing. Without the change, 14 of the new assertions fail. A further loop checks that sprint(showerror, err) never contains the password, for each rewritten message; 6 of those fail on the previous messages. The full suite passes on Julia 1.12 and 1.10. The --trim=safe native job's build and checks pass locally on Julia 1.13.2. The first version of the redaction used findlast, which the trim verifier rejects; that is fixed in 2d12258.

Review.

  • The check sits in parse_uri_reference, which every string path goes through: URI(str), parse, tryparse, parse_uri, resolvereference/absuri on strings, and Serialization's legacy reparse, which falls back to the stored fields when parsing fails.
  • It only fires when the host is present and the path is non-empty without a leading /. Those components could not be produced by the keyword constructor.
  • Scheme-only forms such as mailto:, urn:, and http:g have no authority and are unaffected, as are file:///C:/x and file://C:/x (host C, empty port, path /x).
  • IPvFuture literals without a colon, such as http://[v1.x]/, already parsed to host "" and now throw instead.
  • Messages: I checked every throw in src/. The keyword constructor's host and port errors still echo what the caller passed as host or port. ensurevalid (strict mode) runs only after host validation, which rejects @, so its host message keeps the host. _redacted uses only concrete string operations, so the --trim=safe native job's parse path stays resolvable.

🤖 Generated with Claude Code

RFC 3986 section 3.3 requires the path after an authority to be empty or to
start with "/". The parser's pattern instead left whatever followed an
authority it could not split in the path: `http://[localhost]/` parsed with
host "" and path "[localhost]/", `http://[::1]x/` with host "::1" and path
"x/", and `http://a[b]/` with host "a". These now throw `ParseError`, as the
`URI(; host, path)` constructor already did for the same components.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.93%. Comparing base (d9689ac) to head (2d12258).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master      #87      +/-   ##
==========================================
+ Coverage   93.75%   93.93%   +0.18%     
==========================================
  Files           5        5              
  Lines         416      429      +13     
==========================================
+ Hits          390      403      +13     
  Misses         26       26              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

quinnj and others added 2 commits October 22, 2026 01:57
Parse errors showed the userinfo, the whole input, or a host or port that a
raw '@' or '/' in a password had split off ("Invalid URI port: s3cr3t" for
`http://user:s3cr3t/x@host/`). They now show the input with everything from
the authority up to its last '@' replaced by "***". The constructor's userinfo
error and `splitfilepath`'s scheme error no longer echo their argument, and
the strict-mode round-trip check throws a ParseError with the redacted input
instead of an AssertionError that printed it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`findlast(f, ::String)` passes `f` on to `findprev` without specializing on it,
which the --trim=safe native job reports as an unresolved call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@quinnj
quinnj merged commit 2ba760b into master Oct 22, 2026
12 checks passed
@quinnj
quinnj deleted the claude/authority-path branch October 22, 2026 12:25
@quinnj quinnj mentioned this pull request Oct 22, 2026
quinnj referenced this pull request Oct 22, 2026
Co-Authored-By: Claude Opus 5.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