From edc369565eef92161b56b8f1e39ef53629c05489 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Sat, 17 Oct 2026 04:17:46 +0200 Subject: [PATCH] Reject trailing text after a bracketed IPv6 host in Url::parse Found while testing crates/fetch/src/http.rs:87-102 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/fetch/src/http.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/fetch/src/http.rs b/crates/fetch/src/http.rs index c2b5bee1e..f9a7ba6a8 100644 --- a/crates/fetch/src/http.rs +++ b/crates/fetch/src/http.rs @@ -88,6 +88,9 @@ impl Url { let end = v6.find(']').ok_or_else(|| bad("unterminated IPv6 address"))?; let host = v6.get(..end).unwrap_or_default(); let after = v6.get(end + 1..).unwrap_or_default(); + if !after.is_empty() && !after.starts_with(':') { + return Err(bad("unexpected text after IPv6 address")); + } (host.to_string(), after.strip_prefix(':')) } else { match authority.rsplit_once(':') { @@ -537,6 +540,17 @@ mod tests { } } + #[test] + fn rejects_text_after_a_bracketed_ipv6_host() { + for bad in ["http://[::1]junk/x", "https://[::1]junk/x", "http://[::1]8080/x", "http://[::1]8080"] { + assert!(matches!(Url::parse(bad), Err(HttpError::BadUrl(_))), "{bad}"); + } + let u = Url::parse("http://[::1]:8080/x").unwrap(); + assert_eq!((u.host.as_str(), u.port), ("::1", 8080)); + let u = Url::parse("http://[::1]/x").unwrap(); + assert_eq!((u.host.as_str(), u.port), ("::1", 80)); + } + #[test] fn resolves_redirects() { let u = Url::parse("https://a.example/x/y/file?q").unwrap();