From e3323f57df1f4a44093a2d25fee33513325cbb86 Mon Sep 17 00:00:00 2001 From: nrxr Date: Wed, 8 Apr 2020 10:21:40 +0000 Subject: [PATCH] net/url: add URL.Redacted to return a password scrubbed string Returning an URL.String() without the password is very useful for situations where the URL is supposed to be logged and the password is not useful to be shown. This method re-uses URL.String() but with the password scrubbed and substituted for a "xxxxx" in order to make it obvious that there was a password. If the URL had no password then no "xxxxx" will be shown. Fixes #34855 Change-Id: I7f17d81aa09a7963d2731d16fe15c6ae8e2285fc GitHub-Last-Rev: 46d06dbc4f9e30a57667bb8d0627bc1abed83bdc GitHub-Pull-Request: golang/go#35578 Reviewed-on: https://go-review.googlesource.com/c/go/+/207082 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Emmanuel Odeke --- src/net/url/url.go | 14 +++++++++ src/net/url/url_test.go | 67 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/src/net/url/url.go b/src/net/url/url.go index 2880e820c3..d811016f26 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -821,6 +821,20 @@ func (u *URL) String() string { return buf.String() } +// Redacted is like String but replaces any password with "xxxxx". +// Only the password in u.URL is redacted. +func (u *URL) Redacted() string { + if u == nil { + return "" + } + + ru := *u + if _, has := ru.User.Password(); has { + ru.User = UserPassword(ru.User.Username(), "xxxxx") + } + return ru.String() +} + // Values maps a string key to a list of values. // It is typically used for query parameters and form values. // Unlike in the http.Header map, the keys in a Values map diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index 79fd3d5c79..c9f8a03f75 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -765,6 +765,73 @@ func TestURLString(t *testing.T) { } } +func TestURLRedacted(t *testing.T) { + cases := []struct { + name string + url *URL + want string + }{ + { + name: "non-blank Password", + url: &URL{ + Scheme: "http", + Host: "host.tld", + Path: "this:that", + User: UserPassword("user", "password"), + }, + want: "http://user:xxxxx@host.tld/this:that", + }, + { + name: "blank Password", + url: &URL{ + Scheme: "http", + Host: "host.tld", + Path: "this:that", + User: User("user"), + }, + want: "http://user@host.tld/this:that", + }, + { + name: "nil User", + url: &URL{ + Scheme: "http", + Host: "host.tld", + Path: "this:that", + User: UserPassword("", "password"), + }, + want: "http://:xxxxx@host.tld/this:that", + }, + { + name: "blank Username, blank Password", + url: &URL{ + Scheme: "http", + Host: "host.tld", + Path: "this:that", + }, + want: "http://host.tld/this:that", + }, + { + name: "empty URL", + url: &URL{}, + want: "", + }, + { + name: "nil URL", + url: nil, + want: "", + }, + } + + for _, tt := range cases { + t := t + t.Run(tt.name, func(t *testing.T) { + if g, w := tt.url.Redacted(), tt.want; g != w { + t.Fatalf("got: %q\nwant: %q", g, w) + } + }) + } +} + type EscapeTest struct { in string out string -- 2.50.0