diff --git a/api/server/middleware/debug.go b/api/server/middleware/debug.go index b931f1906b9..a9a94e7f332 100644 --- a/api/server/middleware/debug.go +++ b/api/server/middleware/debug.go @@ -41,7 +41,7 @@ func DebugRequestMiddleware(handler func(ctx context.Context, w http.ResponseWri var postForm map[string]interface{} if err := json.Unmarshal(b, &postForm); err == nil { - maskSecretKeys(postForm) + maskSecretKeys(postForm, r.RequestURI) formStr, errMarshal := json.Marshal(postForm) if errMarshal == nil { logrus.Debugf("form data: %s", string(formStr)) @@ -54,13 +54,22 @@ func DebugRequestMiddleware(handler func(ctx context.Context, w http.ResponseWri } } -func maskSecretKeys(inp interface{}) { +func maskSecretKeys(inp interface{}, path string) { + // Remove any query string from the path + idx := strings.Index(path, "?") + if idx != -1 { + path = path[:idx] + } + // Remove trailing / characters + path = strings.TrimRight(path, "/") + if arr, ok := inp.([]interface{}); ok { for _, f := range arr { - maskSecretKeys(f) + maskSecretKeys(f, path) } return } + if form, ok := inp.(map[string]interface{}); ok { loop0: for k, v := range form { @@ -70,7 +79,16 @@ func maskSecretKeys(inp interface{}) { continue loop0 } } - maskSecretKeys(v) + maskSecretKeys(v, path) + } + + // Route-specific redactions + if strings.HasSuffix(path, "/secrets/create") { + for k := range form { + if k == "Data" { + form[k] = "*****" + } + } } } } diff --git a/api/server/middleware/debug_test.go b/api/server/middleware/debug_test.go new file mode 100644 index 00000000000..87ecafd145c --- /dev/null +++ b/api/server/middleware/debug_test.go @@ -0,0 +1,58 @@ +package middleware + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMaskSecretKeys(t *testing.T) { + tests := []struct { + path string + input map[string]interface{} + expected map[string]interface{} + }{ + { + path: "/v1.30/secrets/create", + input: map[string]interface{}{"Data": "foo", "Name": "name", "Labels": map[string]interface{}{}}, + expected: map[string]interface{}{"Data": "*****", "Name": "name", "Labels": map[string]interface{}{}}, + }, + { + path: "/v1.30/secrets/create//", + input: map[string]interface{}{"Data": "foo", "Name": "name", "Labels": map[string]interface{}{}}, + expected: map[string]interface{}{"Data": "*****", "Name": "name", "Labels": map[string]interface{}{}}, + }, + + { + path: "/secrets/create?key=val", + input: map[string]interface{}{"Data": "foo", "Name": "name", "Labels": map[string]interface{}{}}, + expected: map[string]interface{}{"Data": "*****", "Name": "name", "Labels": map[string]interface{}{}}, + }, + { + path: "/v1.30/some/other/path", + input: map[string]interface{}{ + "password": "pass", + "other": map[string]interface{}{ + "secret": "secret", + "jointoken": "jointoken", + "unlockkey": "unlockkey", + "signingcakey": "signingcakey", + }, + }, + expected: map[string]interface{}{ + "password": "*****", + "other": map[string]interface{}{ + "secret": "*****", + "jointoken": "*****", + "unlockkey": "*****", + "signingcakey": "*****", + }, + }, + }, + } + + for _, testcase := range tests { + maskSecretKeys(testcase.input, testcase.path) + assert.Equal(t, testcase.expected, testcase.input) + } +}