Skip to content

Commit 9003f66

Browse files
committed
Remove secrets from presigned URL when logging
1 parent a8b6409 commit 9003f66

File tree

3 files changed

+101
-15
lines changed

3 files changed

+101
-15
lines changed

client.go

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
658658
if logger != nil {
659659
switch v := logger.(type) {
660660
case LeveledLogger:
661-
v.Debug("performing request", "method", req.Method, "url", redactURL(req.URL))
661+
v.Debug("performing request", "method", req.Method, "url", redactedURL{req.URL})
662662
case Logger:
663663
v.Printf("[DEBUG] %s %s", req.Method, redactURL(req.URL))
664664
}
@@ -715,7 +715,7 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
715715
if err != nil {
716716
switch v := logger.(type) {
717717
case LeveledLogger:
718-
v.Error("request failed", "error", err, "method", req.Method, "url", redactURL(req.URL))
718+
v.Error("request failed", "error", err, "method", req.Method, "url", redactedURL{req.URL})
719719
case Logger:
720720
v.Printf("[ERR] %s %s request failed: %v", req.Method, redactURL(req.URL), err)
721721
}
@@ -910,16 +910,3 @@ func (c *Client) StandardClient() *http.Client {
910910
}
911911
}
912912

913-
// Taken from url.URL#Redacted() which was introduced in go 1.15.
914-
// We can switch to using it directly if we'll bump the minimum required go version.
915-
func redactURL(u *url.URL) string {
916-
if u == nil {
917-
return ""
918-
}
919-
920-
ru := *u
921-
if _, has := ru.User.Password(); has {
922-
ru.User = url.UserPassword(ru.User.Username(), "xxxxx")
923-
}
924-
return ru.String()
925-
}

redact_url.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package retryablehttp
2+
3+
import (
4+
"log/slog"
5+
"net/url"
6+
)
7+
8+
type redactedURL struct{ url *url.URL }
9+
10+
func (r redactedURL) LogValue() slog.Value {
11+
return slog.StringValue(r.String())
12+
}
13+
14+
func (r *redactedURL) String() string {
15+
return redactURL(r.url)
16+
}
17+
18+
var sensitiveParams = []string{
19+
"X-Amz-Signature",
20+
"X-Amz-Security-Token",
21+
"X-Amz-Credential",
22+
}
23+
24+
func redactURL(u *url.URL) string {
25+
if u == nil {
26+
return ""
27+
}
28+
29+
ru := *u
30+
31+
// Taken from url.URL#Redacted() which was introduced in go 1.15.
32+
// We can switch to using it directly if we'll bump the minimum required go version.
33+
if _, has := ru.User.Password(); has {
34+
ru.User = url.UserPassword(ru.User.Username(), "xxxxx")
35+
}
36+
37+
params := ru.Query()
38+
for _, param := range sensitiveParams {
39+
if params.Has(param) {
40+
params.Set(param, "REDACTED")
41+
}
42+
}
43+
ru.RawQuery = params.Encode()
44+
45+
return ru.String()
46+
}

redact_url_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package retryablehttp
2+
3+
import (
4+
"bytes"
5+
"log/slog"
6+
"net/http"
7+
"net/http/httptest"
8+
"strings"
9+
"sync/atomic"
10+
"testing"
11+
)
12+
13+
func TestPresignedURLRedacted(t *testing.T) {
14+
ts := serveFailOnceServer()
15+
defer ts.Close()
16+
17+
logBuffer, logger := createBuffLogger()
18+
client := NewClient()
19+
client.Logger = logger
20+
21+
resp, err := client.Get(ts.URL + "?X-Amz-Credential=SECRET&X-Amz-Signature=SECRET&X-Amz-Security-Token=SECRET")
22+
if err != nil {
23+
t.Fatalf("err: %v", err)
24+
}
25+
resp.Body.Close()
26+
27+
actualLogs := string(logBuffer.Bytes())
28+
if strings.Contains(actualLogs, "SECRET") {
29+
t.Fatalf("log contains SECRET that should have been redacted: %s", actualLogs)
30+
}
31+
}
32+
33+
func serveFailOnceServer() *httptest.Server {
34+
var retries int32 = 0
35+
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
36+
if atomic.LoadInt32(&retries) == 0 {
37+
w.WriteHeader(500)
38+
} else {
39+
w.WriteHeader(200)
40+
}
41+
42+
atomic.AddInt32(&retries, 1)
43+
}))
44+
}
45+
46+
func createBuffLogger() (bytes.Buffer, *slog.Logger) {
47+
logBuffer := bytes.Buffer{}
48+
textHandler := slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
49+
Level: slog.LevelDebug,
50+
})
51+
logger := slog.New(textHandler)
52+
return logBuffer, logger
53+
}

0 commit comments

Comments
 (0)