From 309cad9d9fa846a788cbe46cc5d7c9ef91d0cd9b Mon Sep 17 00:00:00 2001 From: Daniel Hu Date: Tue, 4 Jul 2023 22:06:23 +0800 Subject: [PATCH] fix: add log sanitization for Secret kind in updateResource - Added a new function `desensitizeLog` to sanitize logs by replacing sensitive data in a Secret with "***". - Modified the `updateResource` function to use `desensitizeLog` when the kind is "Secret". - Added a test case for the `desensitizeLog` function to ensure it works as expected. Signed-off-by: Daniel Hu --- pkg/kube/client.go | 46 ++++++++++++++++++++++++++++++++++++++++- pkg/kube/client_test.go | 16 ++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 7b3c803f9..7e0416331 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -687,7 +687,11 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, c.Log("Patch %s %q in namespace %s", kind, target.Name, target.Namespace) obj, err = helper.Patch(target.Namespace, target.Name, patchType, patch, nil) if err != nil { - return errors.Wrapf(err, "cannot patch %q with kind %s", target.Name, kind) + sanitizeLog := err.Error() + if kind == "Secret" { + sanitizeLog = desensitizeLog(err.Error()) + } + return errors.Wrapf(errors.New(sanitizeLog), "cannot patch %q with kind %s", target.Name, kind) } } @@ -695,6 +699,46 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, return nil } +// desensitizeLog replaces the data in a Secret with {"key": "***"}. +// e.g. "data": {"username": "admin", "password": "password"} becomes "data": {"username": "***", "password": "***"} +func desensitizeLog(log string) string { + start := strings.Index(log, `{\"apiVersion`) + end := strings.Index(log, `,\"kind\":\"Secret\"`) + if start == -1 || end == -1 { + return log + } + + // Extract the JSON string and add a } at the end + jsonStr := log[start:end] + "}" + jsonStr = strings.ReplaceAll(jsonStr, "\\\"", "\"") + + // Parse the JSON string into a map + var data map[string]interface{} + err := json.Unmarshal([]byte(jsonStr), &data) + if err != nil { + return log + } + + // Desensitize the values in the data map + if dataMap, ok := data["data"].(map[string]interface{}); ok { + for k := range dataMap { + dataMap[k] = "***" + } + } + + // Convert the map back to JSON string + newJsonStr, err := json.Marshal(data) + if err != nil { + return log + } + + // Replace the original JSON string with the new one in the log + newJsonStr = []byte(strings.ReplaceAll(string(newJsonStr), "\"", "\\\"")) + newLog := log[:start] + string(newJsonStr[:len(newJsonStr)-1]) + log[end:] + + return newLog +} + func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) error { kind := info.Mapping.GroupVersionKind.Kind switch kind { diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index 55aa5d8ed..2b452d6fd 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -558,3 +558,19 @@ spec: ports: - containerPort: 80 ` + +func TestDesensitizeLog(t *testing.T) { + log := `Error: UPGRADE FAILED: cannot patch "mysecret" with kind Secret: "" is invalid: patch: Invalid value: "{\"apiVersion\":\"v1\",\"data\":{\"mykey1\":\"hello\", \"mykey2\":\"world\"},\"kind\":\"Secret\",\"metadata\"……,\"type\":\"Opaque\"}": illegal base64 data at input byte 12` + expected1 := `\"mykey1\":\"***\"` + expected2 := `\"mykey2\":\"***\"` + + result := desensitizeLog(log) + + if !strings.Contains(result, expected1) { + t.Errorf("Expected %s to contain %s", result, expected1) + } + + if !strings.Contains(result, expected2) { + t.Errorf("Expected %s to contain %s", result, expected2) + } +}