From 79615acdec2b2afd062642cbb0f84a77517ebb62 Mon Sep 17 00:00:00 2001 From: Oscar Sanderson Date: Mon, 2 Nov 2026 16:16:08 +0800 Subject: [PATCH] test(fapi): expect FuzzParseEndpointURL to refuse an empty fragment Since v0.69.0 a URL ending in a bare "#" is refused, but the fuzz oracle still required an otherwise valid loopback http URL to be accepted when url.Parse showed no fragment, and url.Parse drops a bare "#". The nightly run found "http://loCAlhost#". The oracle now treats any "#" in the input as a fragment, both when it may be refused and when an accepted URL must have none. The input is kept as a seed. Co-Authored-By: Claude Opus 5.5 --- testdata/fuzz/FuzzParseEndpointURL/9df311e55a4487ec | 3 +++ url_fuzz_test.go | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 testdata/fuzz/FuzzParseEndpointURL/9df311e55a4487ec diff --git a/testdata/fuzz/FuzzParseEndpointURL/9df311e55a4487ec b/testdata/fuzz/FuzzParseEndpointURL/9df311e55a4487ec new file mode 100644 index 00000000..6f3412db --- /dev/null +++ b/testdata/fuzz/FuzzParseEndpointURL/9df311e55a4487ec @@ -0,0 +1,3 @@ +go test fuzz v1 +string("http://loCAlhost#") +bool(true) diff --git a/url_fuzz_test.go b/url_fuzz_test.go index fd9cf4b1..919fdd0c 100644 --- a/url_fuzz_test.go +++ b/url_fuzz_test.go @@ -13,8 +13,8 @@ import ( // on a client-supplied redirect_uri, which a pushed authorization // request now runs through it. Beyond "does not panic": // -// - an accepted URL is absolute, has no credentials or fragment, and -// has a lowercase scheme and host; +// - an accepted URL is absolute, has no credentials or fragment (not +// even an empty "#"), and has a lowercase scheme and host; // - it is https, or http only with AllowLoopbackHTTP and a host that // is genuinely loopback — judged by an oracle independent of // isLoopbackHost (url.Hostname, which strips the port and IPv6 @@ -85,8 +85,8 @@ func checkAcceptedURL(t *testing.T, raw string, u URL, allowLoopback bool) { if parsed.User != nil { t.Fatalf("accepted URL with credentials %q", raw) } - if parsed.Fragment != "" { - t.Fatalf("accepted URL with fragment %q", raw) + if parsed.Fragment != "" || strings.Contains(raw, "#") { + t.Fatalf("accepted URL with a fragment, or an empty one, %q", raw) } if parsed.Scheme != strings.ToLower(parsed.Scheme) || parsed.Host != strings.ToLower(parsed.Host) { t.Fatalf("accepted %q without lowercasing scheme/host: %q", raw, u.String()) @@ -115,7 +115,7 @@ func checkLoopbackHTTPNotWronglyRejected(t *testing.T, raw string, allowLoopback } parsed, err := url.Parse(raw) if err != nil || !strings.EqualFold(parsed.Scheme, "http") || !parsed.IsAbs() || parsed.Host == "" || - parsed.User != nil || parsed.Fragment != "" { + parsed.User != nil || strings.Contains(raw, "#") { return } if loopbackOracle(parsed.Hostname()) {