From c3993bef73ca8c8590ba0fbba6dd5d7aa50b768d Mon Sep 17 00:00:00 2001 From: riccardom Date: Wed, 23 Sep 2026 12:34:40 +0200 Subject: [PATCH] [client] Keep redacting URLs that carry a bracketed IPv6 host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Excluding square brackets from the match stopped the pattern from eating the prose after a URL, but brackets are also the IPv6 host delimiter. For `https://[2001:db8::1]/k?X-Amz-Signature=...` the pattern found nothing after the scheme and matched at all, so the URL survived whole — signed query included — in UploadFailureReason, the daemon log, and a remote job's failure record on the management server. Take a bracketed host in a leading group, then continue with the delimiter-free class as before. Two cases added, one with a port. Reported by cubic (P1) and CodeRabbit (CWE-200) on #7514. --- client/internal/debug/upload.go | 6 +++++- client/internal/debug/upload_redact_test.go | 13 +++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/client/internal/debug/upload.go b/client/internal/debug/upload.go index 30aa73b91..4166ef1e6 100644 --- a/client/internal/debug/upload.go +++ b/client/internal/debug/upload.go @@ -183,7 +183,11 @@ func getURLHash(url string) string { // backticks, angle brackets, parens and braces — so the match does not run past // the URL and swallow the prose after it. TrimRight below then drops trailing // sentence punctuation, which a bare URL at the end of a clause picks up. -var urlInText = regexp.MustCompile("https?://[^\\s\"'`<>\\[\\]{}()]+") +// +// Square brackets are both a wrapper and part of the syntax: they delimit an +// IPv6 host. The leading group takes a bracketed host when there is one, so an +// IPv6 URL is still matched and redacted rather than left whole. +var urlInText = regexp.MustCompile("https?://(?:\\[[^\\]\\s]+\\])?[^\\s\"'`<>\\[\\]{}()]*") // redactedError keeps the original error reachable for errors.Is/As while // presenting a message with every URL cut down to scheme://host. diff --git a/client/internal/debug/upload_redact_test.go b/client/internal/debug/upload_redact_test.go index bea5227e9..b873f6784 100644 --- a/client/internal/debug/upload_redact_test.go +++ b/client/internal/debug/upload_redact_test.go @@ -68,6 +68,19 @@ func TestRedactURLsInError(t *testing.T) { err: errors.New("use `https://b.example.com/p` instead"), want: "use `https://b.example.com` instead", }, + { + // Square brackets delimit an IPv6 host, so excluding them from the + // match entirely leaves an IPv6 URL — signed query and all — + // untouched in the message. + name: "bracketed IPv6 host is still redacted", + err: errors.New(`upload failed: Put "https://[2001:db8::1]/k?X-Amz-Signature=abc123": timeout`), + want: `upload failed: Put "https://[2001:db8::1]": timeout`, + }, + { + name: "IPv6 host with a port is still redacted", + err: errors.New(`Get "https://[2001:db8::1]:8443/upload-url?id=deadbeef": no such host`), + want: `Get "https://[2001:db8::1]:8443": no such host`, + }, } for _, tc := range tests {