Skip to content

Reject set_host("") on non-special URLs with credentials or a port (#1144) - #1148

Open
youdie006 wants to merge 1 commit into
servo:mainfrom
youdie006:fix/1144-set-host-empty-nonspecial
Open

Reject set_host("") on non-special URLs with credentials or a port (#1144)#1148
youdie006 wants to merge 1 commit into
servo:mainfrom
youdie006:fix/1144-set-host-empty-nonspecial

Conversation

@youdie006

Copy link
Copy Markdown

Fixes #1144.

Root cause

Url::set_host's empty-host guard only rejected an empty host for special, non-file schemes. For a non-special scheme, an empty host fell through to Host::parse_opaque_cow("") and was accepted, even when the URL carried credentials or a port. The result is a serialization that the crate's own parser then rejects with EmptyHost -- a broken parse/serialize roundtrip:

let mut u = Url::parse("foo://user@host/").unwrap();
u.set_host(Some("")).unwrap();          // returned Ok
assert_eq!(u.as_str(), "foo://user@/"); // ...but this no longer parses
assert!(Url::parse(u.as_str()).is_err()); // EmptyHost

The port case (foo://host:1/ -> foo://:1/) behaved the same way.

Fix

Mirror the parser, which is the oracle here: it rejects foo://user@/ and foo://:1/ but accepts foo:/// (empty host, no credentials, no port). The guard now also rejects an empty host on a non-special URL when it has a username, a password, or a port. Empty host on a non-special scheme with no credentials/port still succeeds and roundtrips (foo://host/path -> foo:///path), and special/file schemes are unchanged.

Tests

Added test_set_empty_host_non_special covering the three credentials/port cases (asserting Err(EmptyHost) with the URL left unchanged) and the empty-host-roundtrip case. Verified red-green (the test fails without the guard change). The full test suite including the WHATWG URL (WPT) conformance suite still passes; cargo fmt --check and cargo clippy -D warnings are clean.

Thanks @DRMacIver for the clear report and minimal repro -- nice catch via the hegel fuzzer. Happy to fold in or collaborate on additional tests per your offer.


This change was prepared with AI assistance and reviewed by me before submission.

Url::set_host's empty-host guard only rejected an empty host for special,
non-file schemes. For a non-special scheme, an empty host fell through to
Host::parse_opaque_cow("") and was accepted, even when the URL carried
credentials or a port. The result is a serialization that the crate's own
parser then rejects with EmptyHost - a broken parse/serialize roundtrip
(e.g. `foo://user@host/` set to an empty host yields `foo://user@/`, which
no longer parses; likewise `foo://host:1/` -> `foo://:1/`).

Mirror the parser, which is the oracle: it rejects `foo://user@/` and
`foo://:1/` but accepts `foo:///`. The guard now also rejects an empty host
on a non-special URL when it has a username, a password, or a port. Empty
host on a non-special scheme with no credentials/port still succeeds and
roundtrips; special/file schemes are unchanged.

Fixes servo#1144.
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.

set_host(Some("")) produces a URL that then fails to re-parse

1 participant