VYPR
Medium severity4.3GHSA Advisory· Published Oct 29, 2025· Updated Apr 15, 2026

CVE-2024-58269

CVE-2024-58269

Description

A vulnerability has been identified in Rancher Manager, where sensitive information, including secret data, cluster import URLs, and registration tokens, is exposed to any entity with access to Rancher audit logs.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/rancher/rancherGo
< 0.0.0-20251013203444-50dc516a19ea0.0.0-20251013203444-50dc516a19ea

Affected products

1

Patches

2
50dc516a19ea

Add additional redaction for some Request headers (#52297)

https://github.com/rancher/rancherDan P.Oct 13, 2025via ghsa
3 files changed · +216 7
  • pkg/auth/audit/default_test.go+25 5 modified
    @@ -43,11 +43,12 @@ func TestDefaultPolicies(t *testing.T) {
     	machineDataWant[len(machineDataWant)-1] = byte('}')
     
     	type testCase struct {
    -		Name         string
    -		Uri          string
    -		Headers      http.Header
    -		Body         []byte
    -		ExpectedBody []byte
    +		Name            string
    +		Uri             string
    +		Headers         http.Header
    +		ExpectedHeaders http.Header
    +		Body            []byte
    +		ExpectedBody    []byte
     	}
     
     	cases := []testCase{
    @@ -509,6 +510,20 @@ func TestDefaultPolicies(t *testing.T) {
     			Body:         []byte(`{"normalField": "some data", "manifestUrl": "https://localhost:8443/v3/import/abcd.yaml", "insecureWindowsNodeCommand": "curl https://localhost:8443/v3/import/abcd.yaml", "insecureNodeCommand": "curl https://localhost:8443/v3/import/abcd.yaml", "insecureCommand": "curl https://localhost:8443/v3/import/abcd.yaml", "command": "curl https://localhost:8443/v3/import/abcd.yaml", "windowsNodeCommand": "curl https://localhost:8443/v3/import/abcd.yaml"}`),
     			ExpectedBody: []byte(fmt.Sprintf(`{"normalField": "some data", "manifestUrl": "%s", "insecureWindowsNodeCommand": "%[1]s", "insecureNodeCommand": "%[1]s", "insecureCommand": "%[1]s", "command": "%[1]s", "windowsNodeCommand": "%[1]s"}`, redacted)),
     		},
    +		{
    +			Name:            "With redactable Referer header",
    +			Headers:         http.Header{"Content-Type": {contentTypeJSON}, "Referer": {"/v3/import/redactMe.yaml"}},
    +			ExpectedHeaders: http.Header{"Content-Type": {contentTypeJSON}, "Referer": {"/v3/import/[redacted]"}},
    +			Body:            []byte(`{"normalField": "some data", "manifestUrl": "https://localhost:8443/v3/import/abcd.yaml", "insecureWindowsNodeCommand": "curl https://localhost:8443/v3/import/abcd.yaml", "insecureNodeCommand": "curl https://localhost:8443/v3/import/abcd.yaml", "insecureCommand": "curl https://localhost:8443/v3/import/abcd.yaml", "command": "curl https://localhost:8443/v3/import/abcd.yaml", "windowsNodeCommand": "curl https://localhost:8443/v3/import/abcd.yaml"}`),
    +			ExpectedBody:    []byte(fmt.Sprintf(`{"normalField": "some data", "manifestUrl": "%s", "insecureWindowsNodeCommand": "%[1]s", "insecureNodeCommand": "%[1]s", "insecureCommand": "%[1]s", "command": "%[1]s", "windowsNodeCommand": "%[1]s"}`, redacted)),
    +		},
    +		{
    +			Name:            "With non-redactable Referrer header",
    +			Headers:         http.Header{"Content-Type": {contentTypeJSON}, "Referrer": {"/v3/import/redactMe.yaml"}},
    +			ExpectedHeaders: http.Header{"Content-Type": {contentTypeJSON}, "Referrer": {"/v3/import/redactMe.yaml"}},
    +			Body:            []byte(`{"normalField": "some data", "manifestUrl": "https://localhost:8443/v3/import/abcd.yaml", "insecureWindowsNodeCommand": "curl https://localhost:8443/v3/import/abcd.yaml", "insecureNodeCommand": "curl https://localhost:8443/v3/import/abcd.yaml", "insecureCommand": "curl https://localhost:8443/v3/import/abcd.yaml", "command": "curl https://localhost:8443/v3/import/abcd.yaml", "windowsNodeCommand": "curl https://localhost:8443/v3/import/abcd.yaml"}`),
    +			ExpectedBody:    []byte(fmt.Sprintf(`{"normalField": "some data", "manifestUrl": "%s", "insecureWindowsNodeCommand": "%[1]s", "insecureNodeCommand": "%[1]s", "insecureCommand": "%[1]s", "command": "%[1]s", "windowsNodeCommand": "%[1]s"}`, redacted)),
    +		},
     	}
     
     	buffer := bytes.NewBuffer(nil)
    @@ -540,6 +555,11 @@ func TestDefaultPolicies(t *testing.T) {
     
     			assert.Equal(t, expected, actual)
     
    +			if c.ExpectedHeaders != nil {
    +				actualHeaders := log.RequestHeader
    +				assert.Equal(t, c.ExpectedHeaders, actualHeaders)
    +			}
    +
     			buffer.Reset()
     		})
     	}
    
  • pkg/auth/audit/redact.go+46 2 modified
    @@ -8,6 +8,7 @@ import (
     
     	jsonpath "github.com/rancher/jsonpath/pkg"
     	auditlogv1 "github.com/rancher/rancher/pkg/apis/auditlog.cattle.io/v1"
    +	"github.com/rancher/rancher/pkg/settings"
     )
     
     const (
    @@ -231,10 +232,53 @@ func regexRedactor(patterns []string) (Redactor, error) {
     	}), nil
     }
     
    +const (
    +	redactPrefix      = "/v3/import"
    +	redactedImportUrl = redactPrefix + "/" + redacted
    +	refererHeader     = "Referer"
    +)
    +
     func redactImportUrl(l *log) error {
    -	if strings.HasPrefix(l.RequestURI, "/v3/import") {
    -		l.RequestURI = "/v3/import/" + redacted
    +	l.RequestURI = redactImportUrlPath(l.RequestURI)
    +
    +	if err := redactImportUrlHeader(l, refererHeader); err != nil {
    +		return err
    +	}
    +
    +	return nil
    +}
    +
    +func redactImportUrlPath(path string) string {
    +	if strings.HasPrefix(path, redactPrefix) {
    +		return redactedImportUrl
    +	}
    +
    +	return path
    +}
    +
    +func redactImportUrlHeader(l *log, headerName string) error {
    +	referrer, ok := l.RequestHeader[headerName]
    +	if !ok {
    +		return nil
    +	}
    +	l.RequestHeader.Del(headerName)
    +
    +	for _, ref := range referrer {
    +		l.RequestHeader.Add(headerName, redactImportUrlString(ref))
     	}
     
     	return nil
     }
    +
    +func redactImportUrlString(urlIn string) string {
    +	serverUrl := settings.ServerURL.Get()
    +	redactIndex := strings.Index(urlIn, serverUrl)
    +	if redactIndex == -1 {
    +		return urlIn
    +	}
    +
    +	pathIndex := redactIndex + len(serverUrl)
    +	urlPath := urlIn[pathIndex:]
    +
    +	return urlIn[:pathIndex] + redactImportUrlPath(urlPath)
    +}
    
  • pkg/auth/audit/redact_test.go+145 0 modified
    @@ -4,6 +4,7 @@ import (
     	"testing"
     
     	auditlogv1 "github.com/rancher/rancher/pkg/apis/auditlog.cattle.io/v1"
    +	"github.com/rancher/rancher/pkg/settings"
     	"github.com/stretchr/testify/assert"
     )
     
    @@ -152,3 +153,147 @@ func TestPolicyRedactor(t *testing.T) {
     		})
     	}
     }
    +
    +const (
    +	redactableV3URL       = "/v3/import/redactME.yaml"
    +	expectedV3RedactedURL = "/v3/import/[redacted]"
    +)
    +
    +func Test_redactImportUrlPath(t *testing.T) {
    +	cases := []struct {
    +		input    string
    +		expected string
    +	}{
    +		{
    +			"/v3/import",
    +			expectedV3RedactedURL,
    +		},
    +		{
    +			"/v3/import/",
    +			expectedV3RedactedURL,
    +		},
    +		{
    +			redactableV3URL,
    +			expectedV3RedactedURL,
    +		},
    +		{
    +			"/foo/bar" + redactableV3URL,
    +			"/foo/bar" + redactableV3URL,
    +		},
    +		{
    +			"/v3/import/yellow.yaml",
    +			expectedV3RedactedURL,
    +		},
    +		{
    +			"/v4/import/redactME.yaml",
    +			"/v4/import/redactME.yaml",
    +		},
    +	}
    +
    +	for _, tt := range cases {
    +		tt := tt
    +		t.Run(tt.input, func(t *testing.T) {
    +			assert.Equal(t, tt.expected, redactImportUrlPath(tt.input))
    +		})
    +	}
    +
    +	noRedaction := redactImportUrlPath("/v4/import/redactME.yaml")
    +	assert.Equal(t, noRedaction, "/v4/import/redactME.yaml")
    +	assert.NotEqual(t, noRedaction, expectedV3RedactedURL)
    +}
    +
    +const testingServerURL = "https://127.0.0.1.sslip.io:8443"
    +
    +func Test_redactImportUrlString(t *testing.T) {
    +	asserts := assert.New(t)
    +
    +	cases := []struct {
    +		input     string
    +		serverUrl string
    +		expected  string
    +	}{
    +		{
    +			testingServerURL + "/v3/import",
    +			testingServerURL,
    +			testingServerURL + expectedV3RedactedURL,
    +		},
    +		{
    +			testingServerURL + "/v3/import/",
    +			testingServerURL,
    +			testingServerURL + expectedV3RedactedURL,
    +		},
    +		{
    +			testingServerURL + redactableV3URL,
    +			testingServerURL,
    +			testingServerURL + expectedV3RedactedURL,
    +		},
    +		{
    +			testingServerURL + "/foo/bar" + redactableV3URL,
    +			testingServerURL,
    +			testingServerURL + "/foo/bar" + redactableV3URL,
    +		},
    +		{
    +			testingServerURL + "/v3/import/yellow.yaml",
    +			testingServerURL,
    +			testingServerURL + expectedV3RedactedURL,
    +		},
    +		{
    +			testingServerURL + "/v4/import/will-not-redactME.yaml",
    +			testingServerURL,
    +			testingServerURL + "/v4/import/will-not-redactME.yaml",
    +		},
    +		{
    +			"https://some-other-domain.localhost/v4/import/will-not-redactME.yaml",
    +			testingServerURL,
    +			"https://some-other-domain.localhost/v4/import/will-not-redactME.yaml",
    +		},
    +	}
    +
    +	for _, tt := range cases {
    +		tt := tt
    +		t.Run(tt.input, func(t *testing.T) {
    +			originalServerURL := settings.ServerURL.Get()
    +
    +			_ = settings.ServerURL.Set(tt.serverUrl)
    +			asserts.Equal(tt.expected, redactImportUrlString(tt.input))
    +
    +			t.Cleanup(func() {
    +				_ = settings.ServerURL.Set(originalServerURL)
    +			})
    +		})
    +	}
    +
    +}
    +
    +func Test_redactImportUrl(t *testing.T) {
    +	asserts := assert.New(t)
    +	originalServerURL := settings.ServerURL.Get()
    +
    +	testLog := sampleLog()
    +	asserts.Equal("", testLog.RequestURI)
    +
    +	referrerHeader := testLog.RequestHeader.Get(refererHeader)
    +	asserts.Equal("", referrerHeader)
    +
    +	_ = settings.ServerURL.Set(testingServerURL)
    +
    +	testLog.RequestURI = redactableV3URL
    +	asserts.Equal(redactableV3URL, testLog.RequestURI)
    +
    +	testLog.RequestHeader.Set(refererHeader, testingServerURL+redactableV3URL)
    +	referrerHeader = testLog.RequestHeader.Get(refererHeader)
    +	asserts.Equal(testingServerURL+redactableV3URL, referrerHeader)
    +
    +	err := redactImportUrl(&testLog)
    +	asserts.NoError(err)
    +	asserts.NotEqual(redactableV3URL, testLog.RequestURI)
    +	asserts.Equal(expectedV3RedactedURL, testLog.RequestURI)
    +
    +	referrerHeader = testLog.RequestHeader.Get(refererHeader)
    +	asserts.NotEqual(redactableV3URL, referrerHeader)
    +	asserts.Equal(testingServerURL+expectedV3RedactedURL, referrerHeader)
    +
    +	t.Cleanup(func() {
    +		_ = settings.ServerURL.Set(originalServerURL)
    +	})
    +}
    
26ad9216e94f

Improve how auditlog handles specific redactions (#52301)

https://github.com/rancher/rancherJulia BierOct 13, 2025via ghsa
3 files changed · +301 293
  • pkg/auth/audit/default.go+1 1 modified
    @@ -116,7 +116,7 @@ func DefaultPolicies() []auditlogv1.AuditPolicy {
     				AdditionalRedactions: []auditlogv1.Redaction{
     					{
     						Paths: []string{
    -							"$.metadata.annotations['kubectl.kubernetes.io/last-applied-configuration']",
    +							"$..metadata.annotations['kubectl.kubernetes.io/last-applied-configuration']",
     						},
     					},
     				},
    
  • pkg/auth/audit/default_test.go+299 291 modified
    @@ -43,463 +43,471 @@ func TestDefaultPolicies(t *testing.T) {
     	machineDataWant[len(machineDataWant)-1] = byte('}')
     
     	type testCase struct {
    -		Name     string
    -		Uri      string
    -		Headers  http.Header
    -		Body     []byte
    -		Expected []byte
    +		Name         string
    +		Uri          string
    +		Headers      http.Header
    +		Body         []byte
    +		ExpectedBody []byte
     	}
     
     	cases := []testCase{
     		{
    -			Name:     "password entry",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"password":"fake_password","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"password":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "password entry",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"password":"fake_password","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"password":"%s","user":"fake_user"}`, redacted)),
     		},
     		{
    -			Name:     "Password entry",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"Password":"fake_password","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"Password":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "Password entry",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"Password":"fake_password","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"Password":"%s","user":"fake_user"}`, redacted)),
     		},
     		{
    -			Name:     "password entry no space",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"password":"whatever you want","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"password":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "password entry no space",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"password":"whatever you want","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"password":"%s","user":"fake_user"}`, redacted)),
     		},
     		{
    -			Name:     "Password entry no space",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"Password":"A whole bunch of \"\"}{()","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"Password":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "Password entry no space",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"Password":"A whole bunch of \"\"}{()","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"Password":"%s","user":"fake_user"}`, redacted)),
     		},
     		{
    -			Name:     "currentPassword entry",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"currentPassword":"something super secret","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"currentPassword":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "currentPassword entry",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"currentPassword":"something super secret","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"currentPassword":"%s","user":"fake_user"}`, redacted)),
     		},
     		{
    -			Name:     "newPassword entry",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"newPassword":"don't share this","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"newPassword":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "newPassword entry",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"newPassword":"don't share this","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"newPassword":"%s","user":"fake_user"}`, redacted)),
     		},
     		{
    -			Name:     "Multiple password entries",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"currentPassword":"fake_password","newPassword":"new_fake_password","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"currentPassword":"%s","newPassword":"%[1]s","user":"fake_user"}`, redacted)),
    +			Name:         "Multiple password entries",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"currentPassword":"fake_password","newPassword":"new_fake_password","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"currentPassword":"%s","newPassword":"%[1]s","user":"fake_user"}`, redacted)),
     		},
     		{
    -			Name:     "No password entries",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"user":"fake_user","user_info":"some information about the user","request_info":"some info about the request"}`),
    -			Expected: []byte(`{"user":"fake_user","user_info":"some information about the user","request_info":"some info about the request"}`),
    +			Name:         "No password entries",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"user":"fake_user","user_info":"some information about the user","request_info":"some info about the request"}`),
    +			ExpectedBody: []byte(`{"user":"fake_user","user_info":"some information about the user","request_info":"some info about the request"}`),
     		},
     		{
    -			Name:     "Strategic password examples",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"anotherPassword":"\"password\"","currentPassword":"password\":","newPassword":"newPassword\\\":","shortPassword":"'","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"anotherPassword":"%s","currentPassword":"%[1]s","newPassword":"%[1]s","shortPassword":"%[1]s","user":"fake_user"}`, redacted)),
    +			Name:         "Strategic password examples",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"anotherPassword":"\"password\"","currentPassword":"password\":","newPassword":"newPassword\\\":","shortPassword":"'","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"anotherPassword":"%s","currentPassword":"%[1]s","newPassword":"%[1]s","shortPassword":"%[1]s","user":"fake_user"}`, redacted)),
     		},
     		{
    -			Name:     "Token entry",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"accessToken":"fake_access_token","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"accessToken":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "Token entry",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"accessToken":"fake_access_token","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"accessToken":"%s","user":"fake_user"}`, redacted)),
     		},
     		{
    -			Name:     "Token entry in slice",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"data":[{"accessToken":"fake_access_token","user":"fake_user"}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"data":[{"accessToken":"%s","user":"fake_user"}]}`, redacted)),
    +			Name:         "Token entry in slice",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"data":[{"accessToken":"fake_access_token","user":"fake_user"}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"data":[{"accessToken":"%s","user":"fake_user"}]}`, redacted)),
     		},
     		{
    -			Name:     "Token entry in args slice",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"data":{"commands":["--user","user","--token","sometoken"]}}`),
    -			Expected: []byte(fmt.Sprintf(`{"data":{"commands":["--user","user","--token","%s"]}}`, redacted)),
    +			Name:         "Token entry in args slice",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"data":{"commands":["--user","user","--token","sometoken"]}}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"data":{"commands":["--user","user","--token","%s"]}}`, redacted)),
     		},
     		{
    -			Name:     "Token entry in args slice but is last element of slice",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"data":{"commands":["--user","user","--token"]}}`),
    -			Expected: []byte(`{"data":{"commands":["--user","user","--token"]}}`),
    +			Name:         "Token entry in args slice but is last element of slice",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"data":{"commands":["--user","user","--token"]}}`),
    +			ExpectedBody: []byte(`{"data":{"commands":["--user","user","--token"]}}`),
     		},
     		{
    -			Name:     "With public fields",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"accessKey":"fake_access_key","secretKey":"fake_secret_key","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"accessKey":"fake_access_key","secretKey":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "With public fields",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"accessKey":"fake_access_key","secretKey":"fake_secret_key","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"accessKey":"fake_access_key","secretKey":"%s","user":"fake_user"}`, redacted)),
     		},
     		{
    -			Name:     "With secret data",
    -			Uri:      "/secrets",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"},"accessToken" :"fake_access_token"}`),
    -			Expected: []byte(fmt.Sprintf(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":"%s","accessToken" :"%[1]s"}`, redacted)),
    +			Name:         "With secret data",
    +			Uri:          "/secrets",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"},"accessToken" :"fake_access_token"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":"%s","accessToken" :"%[1]s"}`, redacted)),
     		},
     		{
    -			Name:     "With secret list data",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"type":"collection","data":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"}},{"type":"Opaque","metadata":{"namespace":"default","name":"my secret2"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"}}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"type":"collection","data":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":"%s"},{"type":"Opaque","metadata":{"namespace":"default","name":"my secret2"},"_type":"Opaque","data":"%[1]s"}]}`, redacted)),
    -			Uri:      "/v1/secrets",
    +			Name:         "With secret list data",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type":"collection","data":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"}},{"type":"Opaque","metadata":{"namespace":"default","name":"my secret2"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"}}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"type":"collection","data":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":"%s"},{"type":"Opaque","metadata":{"namespace":"default","name":"my secret2"},"_type":"Opaque","data":"%[1]s"}]}`, redacted)),
    +			Uri:          "/v1/secrets",
     		},
     		{
     			// norman transforms some secret subtypes to where their data fields cannot be distinguished from non-sensitive fields.
     			// In this case, all fields aside from id, created, and baseType should be redacted.
    -			Name:     "With secret list data but no data field for array elements",
    -			Uri:      "/secrets",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"type":"collection","data":[{"id":"p-12345:testsecret","baseType":"secret","type":"Opaque","_type":"Opaque","foo":"something","bar":"something","accessToken":"token"}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"data":[{"_type":"%s","accessToken":"%[1]s","bar":"%[1]s","baseType":"secret","foo":"%[1]s","id":"p-12345:testsecret","type":"%[1]s"}],"type":"collection"}`, redacted)),
    +			Name:         "With secret list data but no data field for array elements",
    +			Uri:          "/secrets",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type":"collection","data":[{"id":"p-12345:testsecret","baseType":"secret","type":"Opaque","_type":"Opaque","foo":"something","bar":"something","accessToken":"token"}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"data":[{"_type":"%s","accessToken":"%[1]s","bar":"%[1]s","baseType":"secret","foo":"%[1]s","id":"p-12345:testsecret","type":"%[1]s"}],"type":"collection"}`, redacted)),
     		},
     		{
    -			Name:     "With secret list data from k8s proxy",
    -			Uri:      "/k8s/clusters/local/api/v1/secrets?limit=500",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"kind":"SecretList","items":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"}}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"kind":"SecretList","items":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":"%s"}]}`, redacted)),
    +			Name:         "With secret list data from k8s proxy",
    +			Uri:          "/k8s/clusters/local/api/v1/secrets?limit=500",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"kind":"SecretList","items":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"}}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"kind":"SecretList","items":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":"%s"}]}`, redacted)),
     		},
     		{
    -			Name:     "With secret data and wrong URI",
    -			Uri:      "/not-secret",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"},"accessToken" :"fake_access_token"}`),
    -			Expected: []byte(fmt.Sprintf(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"},"accessToken" :"%s"}`, redacted)),
    +			Name:         "With secret data and wrong URI",
    +			Uri:          "/not-secret",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"},"accessToken" :"fake_access_token"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"},"accessToken" :"%s"}`, redacted)),
     		},
     		{
    -			Name:     "With nested sensitive information",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"sensitiveData": {"accessToken":"fake_access_token","user":"fake_user"}}`),
    -			Expected: []byte(fmt.Sprintf(`{"sensitiveData": {"accessToken":"%s","user":"fake_user"}}`, redacted)),
    +			Name:         "With nested sensitive information",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"sensitiveData": {"accessToken":"fake_access_token","user":"fake_user"}}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"sensitiveData": {"accessToken":"%s","user":"fake_user"}}`, redacted)),
     		},
     		{
    -			Name:     "With all machine driver fields",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     machineDataInput,
    -			Expected: machineDataWant,
    +			Name:         "With all machine driver fields",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         machineDataInput,
    +			ExpectedBody: machineDataWant,
     		},
     		{
    -			Name:     "With no secret uri but secret base type slice",
    -			Uri:      `/v3/project/local:p-12345/namespacedcertificates?limit=-1&sort=name`,
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"type":"collection","data":[{"baseType":"namespacedSecret","creatorId":null,"data":{"testfield":"somesecretencodeddata"},"id":"cattle-system:test","kind":"Opaque"}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"type":"collection","data":[{"baseType":"namespacedSecret","creatorId":null,"data":"%s","id":"cattle-system:test","kind":"Opaque"}]}`, redacted)),
    +			Name:         "With no secret uri but secret base type slice",
    +			Uri:          `/v3/project/local:p-12345/namespacedcertificates?limit=-1&sort=name`,
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type":"collection","data":[{"baseType":"namespacedSecret","creatorId":null,"data":{"testfield":"somesecretencodeddata"},"id":"cattle-system:test","kind":"Opaque"}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"type":"collection","data":[{"baseType":"namespacedSecret","creatorId":null,"data":"%s","id":"cattle-system:test","kind":"Opaque"}]}`, redacted)),
     		},
     		{
    -			Name:     "With kubeconfig from generateKubeconfig action",
    -			Uri:      `/v3/clusters/c-xxxxx?action=generateKubeconfig`,
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"baseType":"generateKubeConfigOutput","config":"apiVersion: v1\nkind: Config\nclusters:\n- name: \"somecluster-rke\"\n  cluster:\n    server: \"https://rancherurl.com/k8s/clusters/c-xxxxx\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  cluster:\n    server: \"https://34.211.205.110:6443\"\n    certificate-authority-data: \"somecadata\"\n\nusers:\n- name: \"somecluster-rke\"\n  user:\n    token: \"kubeconfig-user-12345:sometoken\"\n\n\ncontexts:\n- name: \"somecluster-rke\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke-somecluster-rke1\"\n\ncurrent-context: \"somecluster-rke\"\n","type":"generateKubeConfigOutput"}`),
    -			Expected: []byte(fmt.Sprintf(`{"baseType":"generateKubeConfigOutput","config":"%s","type":"generateKubeConfigOutput"}`, redacted)),
    +			Name:         "With kubeconfig from generateKubeconfig action",
    +			Uri:          `/v3/clusters/c-xxxxx?action=generateKubeconfig`,
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"baseType":"generateKubeConfigOutput","config":"apiVersion: v1\nkind: Config\nclusters:\n- name: \"somecluster-rke\"\n  cluster:\n    server: \"https://rancherurl.com/k8s/clusters/c-xxxxx\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  cluster:\n    server: \"https://34.211.205.110:6443\"\n    certificate-authority-data: \"somecadata\"\n\nusers:\n- name: \"somecluster-rke\"\n  user:\n    token: \"kubeconfig-user-12345:sometoken\"\n\n\ncontexts:\n- name: \"somecluster-rke\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke-somecluster-rke1\"\n\ncurrent-context: \"somecluster-rke\"\n","type":"generateKubeConfigOutput"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"baseType":"generateKubeConfigOutput","config":"%s","type":"generateKubeConfigOutput"}`, redacted)),
     		},
     		{
    -			Name:     "With kubeconfig from connect agent",
    -			Uri:      `/v3/connect/agent`,
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"kubeConfig":"apiVersion: v1\nkind: Config\nclusters:\n- name: \"somecluster-rke\"\n  cluster:\n    server: \"https://rancherurl.com/k8s/clusters/c-xxxxx\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  cluster:\n    server: \"https://34.211.205.110:6443\"\n    certificate-authority-data: \"somecadata\"\n\nusers:\n- name: \"somecluster-rke\"\n  user:\n    token: \"kubeconfig-user-12345:sometoken\"\n\n\ncontexts:\n- name: \"somecluster-rke\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke-somecluster-rke1\"\n\ncurrent-context: \"somecluster-rke\"\n","namespace":"testns","secretName":"secret-name"}`),
    -			Expected: []byte(fmt.Sprintf(`{"kubeConfig":"%s","namespace":"testns","secretName":"secret-name"}`, redacted)),
    +			Name:         "With kubeconfig from connect agent",
    +			Uri:          `/v3/connect/agent`,
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"kubeConfig":"apiVersion: v1\nkind: Config\nclusters:\n- name: \"somecluster-rke\"\n  cluster:\n    server: \"https://rancherurl.com/k8s/clusters/c-xxxxx\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  cluster:\n    server: \"https://34.211.205.110:6443\"\n    certificate-authority-data: \"somecadata\"\n\nusers:\n- name: \"somecluster-rke\"\n  user:\n    token: \"kubeconfig-user-12345:sometoken\"\n\n\ncontexts:\n- name: \"somecluster-rke\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke-somecluster-rke1\"\n\ncurrent-context: \"somecluster-rke\"\n","namespace":"testns","secretName":"secret-name"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"kubeConfig":"%s","namespace":"testns","secretName":"secret-name"}`, redacted)),
     		},
     		{
    -			Name:     "With kubeconfig from random request",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"kubeConfig":"apiVersion: v1\nkind: Config\nclusters:\n- name: \"somecluster-rke\"\n  cluster:\n    server: \"https://rancherurl.com/k8s/clusters/c-xxxxx\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  cluster:\n    server: \"https://34.211.205.110:6443\"\n    certificate-authority-data: \"somecadata\"\n\nusers:\n- name: \"somecluster-rke\"\n  user:\n    token: \"kubeconfig-user-12345:sometoken\"\n\n\ncontexts:\n- name: \"somecluster-rke\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke-somecluster-rke1\"\n\ncurrent-context: \"somecluster-rke\"\n","namespace":"testns","secretName":"secret-name"}`),
    -			Expected: []byte(fmt.Sprintf(`{"kubeConfig":"%s","namespace":"testns","secretName":"secret-name"}`, redacted)),
    +			Name:         "With kubeconfig from random request",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"kubeConfig":"apiVersion: v1\nkind: Config\nclusters:\n- name: \"somecluster-rke\"\n  cluster:\n    server: \"https://rancherurl.com/k8s/clusters/c-xxxxx\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  cluster:\n    server: \"https://34.211.205.110:6443\"\n    certificate-authority-data: \"somecadata\"\n\nusers:\n- name: \"somecluster-rke\"\n  user:\n    token: \"kubeconfig-user-12345:sometoken\"\n\n\ncontexts:\n- name: \"somecluster-rke\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke-somecluster-rke1\"\n\ncurrent-context: \"somecluster-rke\"\n","namespace":"testns","secretName":"secret-name"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"kubeConfig":"%s","namespace":"testns","secretName":"secret-name"}`, redacted)),
     		},
     		{
    -			Name:     "With items from sensitiveBodyFields",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"credentials":"{'fakeCredName': 'fakeCred'}","applicationSecret":"fakeAppSecret","oauthCredential":"fakeOauth","serviceAccountCredential":"fakeSACred","spKey":"fakeSPKey","spCert":"fakeSPCERT","certificate":"fakeCert","privateKey":"fakeKey"}`),
    -			Expected: []byte(fmt.Sprintf(`{"credentials":"%s","applicationSecret":"%[1]s","oauthCredential":"%[1]s","serviceAccountCredential":"%[1]s","spKey":"%[1]s","spCert":"%[1]s","certificate":"%[1]s","privateKey":"%[1]s"}`, redacted)),
    +			Name:         "With items from sensitiveBodyFields",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"credentials":"{'fakeCredName': 'fakeCred'}","applicationSecret":"fakeAppSecret","oauthCredential":"fakeOauth","serviceAccountCredential":"fakeSACred","spKey":"fakeSPKey","spCert":"fakeSPCERT","certificate":"fakeCert","privateKey":"fakeKey"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"credentials":"%s","applicationSecret":"%[1]s","oauthCredential":"%[1]s","serviceAccountCredential":"%[1]s","spKey":"%[1]s","spCert":"%[1]s","certificate":"%[1]s","privateKey":"%[1]s"}`, redacted)),
     		},
     		{
    -			Name:     "With malformed input",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"key":"value","response":}`),
    -			Expected: []byte(fmt.Sprintf(`{"%s":"failed to unmarshal request body: invalid character '}' looking for beginning of value"}`, auditLogErrorKey)),
    +			Name:         "With malformed input",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"key":"value","response":}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"%s":"failed to unmarshal request body: invalid character '}' looking for beginning of value"}`, auditLogErrorKey)),
     		},
     		{
    -			Name:     "With secret string data with last-applied-configuration annotation",
    -			Uri:      "/secrets",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"apiVersion": "v1", "stringData": {"secret": "02020202020202020202"}, "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"opaque-secret2\",\"namespace\":\"default\"},\"stringData\":{\"secret\":\"02020202020202020202\"}}"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`),
    -			Expected: []byte(fmt.Sprintf(`{"apiVersion": "v1", "stringData": "%s", "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "%[1]s"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`, redacted)),
    +			Name:         "With secret string data with last-applied-configuration annotation",
    +			Uri:          "/secrets",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"apiVersion": "v1", "stringData": {"secret": "02020202020202020202"}, "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"opaque-secret2\",\"namespace\":\"default\"},\"stringData\":{\"secret\":\"02020202020202020202\"}}"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"apiVersion": "v1", "stringData": "%s", "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "%[1]s"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`, redacted)),
     		},
     		{
    -			Name:     "With secret data with last-applied-configuration annotation",
    -			Uri:      "/secrets",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"apiVersion": "v1", "data": {"secret": "02020202020202020202"}, "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"opaque-secret2\",\"namespace\":\"default\"},\"data\":{\"secret\":\"02020202020202020202\"}}"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`),
    -			Expected: []byte(fmt.Sprintf(`{"apiVersion": "v1", "data": "%s", "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "%[1]s"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`, redacted)),
    +			Name:         "With secret data with last-applied-configuration annotation",
    +			Uri:          "/secrets",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"apiVersion": "v1", "data": {"secret": "02020202020202020202"}, "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"opaque-secret2\",\"namespace\":\"default\"},\"data\":{\"secret\":\"02020202020202020202\"}}"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"apiVersion": "v1", "data": "%s", "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "%[1]s"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`, redacted)),
     		},
     		{
    -			Name:     "password entry",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"password":"fake_password","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"password":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "password entry",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"password":"fake_password","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"password":"%s","user":"fake_user"}`, redacted)),
     		},
     
     		{
    -			Name:     "Password entry",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"Password":"fake_password","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"Password":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "Password entry",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"Password":"fake_password","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"Password":"%s","user":"fake_user"}`, redacted)),
     		},
     
     		{
    -			Name:     "password entry no space",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"password":"whatever you want","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"password":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "password entry no space",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"password":"whatever you want","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"password":"%s","user":"fake_user"}`, redacted)),
     		},
     
     		{
    -			Name:     "Password entry no space",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"Password":"A whole bunch of \"\"}{()","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"Password":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "Password entry no space",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"Password":"A whole bunch of \"\"}{()","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"Password":"%s","user":"fake_user"}`, redacted)),
     		},
     
     		{
    -			Name:     "currentPassword entry",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"currentPassword":"something super secret","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"currentPassword":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "currentPassword entry",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"currentPassword":"something super secret","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"currentPassword":"%s","user":"fake_user"}`, redacted)),
     		},
     
     		{
    -			Name:     "newPassword entry",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"newPassword":"don't share this","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"newPassword":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "newPassword entry",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"newPassword":"don't share this","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"newPassword":"%s","user":"fake_user"}`, redacted)),
     		},
     
     		{
    -			Name:     "Multiple password entries",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"currentPassword":"fake_password","newPassword":"new_fake_password","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"currentPassword":"%s","newPassword":"%[1]s","user":"fake_user"}`, redacted)),
    +			Name:         "Multiple password entries",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"currentPassword":"fake_password","newPassword":"new_fake_password","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"currentPassword":"%s","newPassword":"%[1]s","user":"fake_user"}`, redacted)),
     		},
     
     		{
    -			Name:     "No password entries",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"user":"fake_user","user_info":"some information about the user","request_info":"some info about the request"}`),
    -			Expected: []byte(`{"user":"fake_user","user_info":"some information about the user","request_info":"some info about the request"}`),
    +			Name:         "No password entries",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"user":"fake_user","user_info":"some information about the user","request_info":"some info about the request"}`),
    +			ExpectedBody: []byte(`{"user":"fake_user","user_info":"some information about the user","request_info":"some info about the request"}`),
     		},
     
     		{
    -			Name:     "Strategic password examples",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"anotherPassword":"\"password\"","currentPassword":"password\":","newPassword":"newPassword\\\":","shortPassword":"'","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"anotherPassword":"%s","currentPassword":"%[1]s","newPassword":"%[1]s","shortPassword":"%[1]s","user":"fake_user"}`, redacted)),
    +			Name:         "Strategic password examples",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"anotherPassword":"\"password\"","currentPassword":"password\":","newPassword":"newPassword\\\":","shortPassword":"'","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"anotherPassword":"%s","currentPassword":"%[1]s","newPassword":"%[1]s","shortPassword":"%[1]s","user":"fake_user"}`, redacted)),
     		},
     
     		{
    -			Name:     "Token entry",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"accessToken":"fake_access_token","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"accessToken":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "Token entry",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"accessToken":"fake_access_token","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"accessToken":"%s","user":"fake_user"}`, redacted)),
     		},
     
     		{
    -			Name:     "Token entry in slice",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"data":[{"accessToken":"fake_access_token","user":"fake_user"}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"data":[{"accessToken":"%s","user":"fake_user"}]}`, redacted)),
    +			Name:         "Token entry in slice",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"data":[{"accessToken":"fake_access_token","user":"fake_user"}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"data":[{"accessToken":"%s","user":"fake_user"}]}`, redacted)),
     		},
     
     		{
    -			Name:     "Token entry in args slice",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"data":{"commands":["--user","user","--token","sometoken"]}}`),
    -			Expected: []byte(fmt.Sprintf(`{"data":{"commands":["--user","user","--token","%s"]}}`, redacted)),
    +			Name:         "Token entry in args slice",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"data":{"commands":["--user","user","--token","sometoken"]}}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"data":{"commands":["--user","user","--token","%s"]}}`, redacted)),
     		},
     
     		{
    -			Name:     "Token entry in args slice but is last element of slice",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"data":{"commands":["--user","user","--token"]}}`),
    -			Expected: []byte(`{"data":{"commands":["--user","user","--token"]}}`),
    +			Name:         "Token entry in args slice but is last element of slice",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"data":{"commands":["--user","user","--token"]}}`),
    +			ExpectedBody: []byte(`{"data":{"commands":["--user","user","--token"]}}`),
     		},
     
     		{
    -			Name:     "With public fields",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"accessKey":"fake_access_key","secretKey":"fake_secret_key","user":"fake_user"}`),
    -			Expected: []byte(fmt.Sprintf(`{"accessKey":"fake_access_key","secretKey":"%s","user":"fake_user"}`, redacted)),
    +			Name:         "With public fields",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"accessKey":"fake_access_key","secretKey":"fake_secret_key","user":"fake_user"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"accessKey":"fake_access_key","secretKey":"%s","user":"fake_user"}`, redacted)),
     		},
     
     		{
    -			Name:     "With secret data",
    -			Uri:      "/secrets",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"},"accessToken" :"fake_access_token"}`),
    -			Expected: []byte(fmt.Sprintf(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":"%s","accessToken" :"%[1]s"}`, redacted)),
    +			Name:         "With secret data",
    +			Uri:          "/secrets",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"},"accessToken" :"fake_access_token"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":"%s","accessToken" :"%[1]s"}`, redacted)),
     		},
     
     		{
    -			Name:     "With secret list data",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"type":"collection","data":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"}},{"type":"Opaque","metadata":{"namespace":"default","name":"my secret2"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"}}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"type":"collection","data":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":"%s"},{"type":"Opaque","metadata":{"namespace":"default","name":"my secret2"},"_type":"Opaque","data":"%[1]s"}]}`, redacted)),
    -			Uri:      "/v1/secrets",
    +			Name:         "With secret list data",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type":"collection","data":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"}},{"type":"Opaque","metadata":{"namespace":"default","name":"my secret2"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"}}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"type":"collection","data":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":"%s"},{"type":"Opaque","metadata":{"namespace":"default","name":"my secret2"},"_type":"Opaque","data":"%[1]s"}]}`, redacted)),
    +			Uri:          "/v1/secrets",
     		},
     
     		{
     			// norman transforms some secret subtypes to where their data fields cannot be distinguished from non-sensitive fields.
     			// In this case, all fields aside from id, created, and baseType should be redacted.
    -			Name:     "With secret list data but no data field for array elements",
    -			Uri:      "/secrets",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"type":"collection","data":[{"id":"p-12345:testsecret","baseType":"secret","type":"Opaque","_type":"Opaque","foo":"something","bar":"something","accessToken":"token"}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"data":[{"_type":"%s","accessToken":"%[1]s","bar":"%[1]s","baseType":"secret","foo":"%[1]s","id":"p-12345:testsecret","type":"%[1]s"}],"type":"collection"}`, redacted)),
    +			Name:         "With secret list data but no data field for array elements",
    +			Uri:          "/secrets",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type":"collection","data":[{"id":"p-12345:testsecret","baseType":"secret","type":"Opaque","_type":"Opaque","foo":"something","bar":"something","accessToken":"token"}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"data":[{"_type":"%s","accessToken":"%[1]s","bar":"%[1]s","baseType":"secret","foo":"%[1]s","id":"p-12345:testsecret","type":"%[1]s"}],"type":"collection"}`, redacted)),
     		},
     
     		{
    -			Name:     "With secret list data from k8s proxy",
    -			Uri:      "/k8s/clusters/local/api/v1/secrets?limit=500",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"kind":"SecretList","items":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"}}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"kind":"SecretList","items":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":"%s"}]}`, redacted)),
    +			Name:         "With secret list data from k8s proxy",
    +			Uri:          "/k8s/clusters/local/api/v1/secrets?limit=500",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"kind":"SecretList","items":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"}}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"kind":"SecretList","items":[{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":"%s"}]}`, redacted)),
     		},
     
     		{
    -			Name:     "With secret data and wrong URI",
    -			Uri:      "/not-secret",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"},"accessToken" :"fake_access_token"}`),
    -			Expected: []byte(fmt.Sprintf(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"},"accessToken" :"%s"}`, redacted)),
    +			Name:         "With secret data and wrong URI",
    +			Uri:          "/not-secret",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"},"accessToken" :"fake_access_token"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"type":"Opaque","metadata":{"namespace":"default","name":"my secret"},"_type":"Opaque","data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\","bar":"U3VwZXIgU2VjcmV0IERhdGEK"},"accessToken" :"%s"}`, redacted)),
     		},
     
     		{
    -			Name:     "With nested sensitive information",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"sensitiveData": {"accessToken":"fake_access_token","user":"fake_user"}}`),
    -			Expected: []byte(fmt.Sprintf(`{"sensitiveData": {"accessToken":"%s","user":"fake_user"}}`, redacted)),
    +			Name:         "With nested sensitive information",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"sensitiveData": {"accessToken":"fake_access_token","user":"fake_user"}}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"sensitiveData": {"accessToken":"%s","user":"fake_user"}}`, redacted)),
     		},
     
     		{
    -			Name:     "With all machine driver fields",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     machineDataInput,
    -			Expected: machineDataWant,
    +			Name:         "With all machine driver fields",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         machineDataInput,
    +			ExpectedBody: machineDataWant,
     		},
     
     		{
    -			Name:     "With no secret uri but secret base type slice",
    -			Uri:      `/v3/project/local:p-12345/namespacedcertificates?limit=-1&sort=name`,
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"type":"collection","data":[{"baseType":"namespacedSecret","creatorId":null,"data":{"testfield":"somesecretencodeddata"},"id":"cattle-system:test","kind":"Opaque"}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"type":"collection","data":[{"baseType":"namespacedSecret","creatorId":null,"data":"%s","id":"cattle-system:test","kind":"Opaque"}]}`, redacted)),
    +			Name:         "With no secret uri but secret base type slice",
    +			Uri:          `/v3/project/local:p-12345/namespacedcertificates?limit=-1&sort=name`,
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type":"collection","data":[{"baseType":"namespacedSecret","creatorId":null,"data":{"testfield":"somesecretencodeddata"},"id":"cattle-system:test","kind":"Opaque"}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"type":"collection","data":[{"baseType":"namespacedSecret","creatorId":null,"data":"%s","id":"cattle-system:test","kind":"Opaque"}]}`, redacted)),
     		},
     
     		{
    -			Name:     "With kubeconfig from generateKubeconfig action",
    -			Uri:      `/v3/clusters/c-xxxxx?action=generateKubeconfig`,
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"baseType":"generateKubeConfigOutput","config":"apiVersion: v1\nkind: Config\nclusters:\n- name: \"somecluster-rke\"\n  cluster:\n    server: \"https://rancherurl.com/k8s/clusters/c-xxxxx\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  cluster:\n    server: \"https://34.211.205.110:6443\"\n    certificate-authority-data: \"somecadata\"\n\nusers:\n- name: \"somecluster-rke\"\n  user:\n    token: \"kubeconfig-user-12345:sometoken\"\n\n\ncontexts:\n- name: \"somecluster-rke\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke-somecluster-rke1\"\n\ncurrent-context: \"somecluster-rke\"\n","type":"generateKubeConfigOutput"}`),
    -			Expected: []byte(fmt.Sprintf(`{"baseType":"generateKubeConfigOutput","config":"%s","type":"generateKubeConfigOutput"}`, redacted)),
    +			Name:         "With kubeconfig from generateKubeconfig action",
    +			Uri:          `/v3/clusters/c-xxxxx?action=generateKubeconfig`,
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"baseType":"generateKubeConfigOutput","config":"apiVersion: v1\nkind: Config\nclusters:\n- name: \"somecluster-rke\"\n  cluster:\n    server: \"https://rancherurl.com/k8s/clusters/c-xxxxx\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  cluster:\n    server: \"https://34.211.205.110:6443\"\n    certificate-authority-data: \"somecadata\"\n\nusers:\n- name: \"somecluster-rke\"\n  user:\n    token: \"kubeconfig-user-12345:sometoken\"\n\n\ncontexts:\n- name: \"somecluster-rke\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke-somecluster-rke1\"\n\ncurrent-context: \"somecluster-rke\"\n","type":"generateKubeConfigOutput"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"baseType":"generateKubeConfigOutput","config":"%s","type":"generateKubeConfigOutput"}`, redacted)),
     		},
     
     		{
    -			Name:     "With kubeconfig from connect agent",
    -			Uri:      `/v3/connect/agent`,
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"kubeConfig":"apiVersion: v1\nkind: Config\nclusters:\n- name: \"somecluster-rke\"\n  cluster:\n    server: \"https://rancherurl.com/k8s/clusters/c-xxxxx\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  cluster:\n    server: \"https://34.211.205.110:6443\"\n    certificate-authority-data: \"somecadata\"\n\nusers:\n- name: \"somecluster-rke\"\n  user:\n    token: \"kubeconfig-user-12345:sometoken\"\n\n\ncontexts:\n- name: \"somecluster-rke\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke-somecluster-rke1\"\n\ncurrent-context: \"somecluster-rke\"\n","namespace":"testns","secretName":"secret-name"}`),
    -			Expected: []byte(fmt.Sprintf(`{"kubeConfig":"%s","namespace":"testns","secretName":"secret-name"}`, redacted)),
    +			Name:         "With kubeconfig from connect agent",
    +			Uri:          `/v3/connect/agent`,
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"kubeConfig":"apiVersion: v1\nkind: Config\nclusters:\n- name: \"somecluster-rke\"\n  cluster:\n    server: \"https://rancherurl.com/k8s/clusters/c-xxxxx\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  cluster:\n    server: \"https://34.211.205.110:6443\"\n    certificate-authority-data: \"somecadata\"\n\nusers:\n- name: \"somecluster-rke\"\n  user:\n    token: \"kubeconfig-user-12345:sometoken\"\n\n\ncontexts:\n- name: \"somecluster-rke\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke-somecluster-rke1\"\n\ncurrent-context: \"somecluster-rke\"\n","namespace":"testns","secretName":"secret-name"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"kubeConfig":"%s","namespace":"testns","secretName":"secret-name"}`, redacted)),
     		},
     
     		{
    -			Name:     "With kubeconfig from random request",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"kubeConfig":"apiVersion: v1\nkind: Config\nclusters:\n- name: \"somecluster-rke\"\n  cluster:\n    server: \"https://rancherurl.com/k8s/clusters/c-xxxxx\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  cluster:\n    server: \"https://34.211.205.110:6443\"\n    certificate-authority-data: \"somecadata\"\n\nusers:\n- name: \"somecluster-rke\"\n  user:\n    token: \"kubeconfig-user-12345:sometoken\"\n\n\ncontexts:\n- name: \"somecluster-rke\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke-somecluster-rke1\"\n\ncurrent-context: \"somecluster-rke\"\n","namespace":"testns","secretName":"secret-name"}`),
    -			Expected: []byte(fmt.Sprintf(`{"kubeConfig":"%s","namespace":"testns","secretName":"secret-name"}`, redacted)),
    +			Name:         "With kubeconfig from random request",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"kubeConfig":"apiVersion: v1\nkind: Config\nclusters:\n- name: \"somecluster-rke\"\n  cluster:\n    server: \"https://rancherurl.com/k8s/clusters/c-xxxxx\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  cluster:\n    server: \"https://34.211.205.110:6443\"\n    certificate-authority-data: \"somecadata\"\n\nusers:\n- name: \"somecluster-rke\"\n  user:\n    token: \"kubeconfig-user-12345:sometoken\"\n\n\ncontexts:\n- name: \"somecluster-rke\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke\"\n- name: \"somecluster-rke-somecluster-rke1\"\n  context:\n    user: \"somecluster-rke\"\n    cluster: \"somecluster-rke-somecluster-rke1\"\n\ncurrent-context: \"somecluster-rke\"\n","namespace":"testns","secretName":"secret-name"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"kubeConfig":"%s","namespace":"testns","secretName":"secret-name"}`, redacted)),
     		},
     
     		{
    -			Name:     "With items from sensitiveBodyFields",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"credentials":"{'fakeCredName': 'fakeCred'}","applicationSecret":"fakeAppSecret","oauthCredential":"fakeOauth","serviceAccountCredential":"fakeSACred","spKey":"fakeSPKey","spCert":"fakeSPCERT","certificate":"fakeCert","privateKey":"fakeKey"}`),
    -			Expected: []byte(fmt.Sprintf(`{"credentials":"%s","applicationSecret":"%[1]s","oauthCredential":"%[1]s","serviceAccountCredential":"%[1]s","spKey":"%[1]s","spCert":"%[1]s","certificate":"%[1]s","privateKey":"%[1]s"}`, redacted)),
    +			Name:         "With items from sensitiveBodyFields",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"credentials":"{'fakeCredName': 'fakeCred'}","applicationSecret":"fakeAppSecret","oauthCredential":"fakeOauth","serviceAccountCredential":"fakeSACred","spKey":"fakeSPKey","spCert":"fakeSPCERT","certificate":"fakeCert","privateKey":"fakeKey"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"credentials":"%s","applicationSecret":"%[1]s","oauthCredential":"%[1]s","serviceAccountCredential":"%[1]s","spKey":"%[1]s","spCert":"%[1]s","certificate":"%[1]s","privateKey":"%[1]s"}`, redacted)),
     		},
     
     		{
    -			Name:     "With malformed input",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"key":"value","response":}`),
    -			Expected: []byte(fmt.Sprintf(`{"%s":"failed to unmarshal request body: invalid character '}' looking for beginning of value"}`, auditLogErrorKey)),
    +			Name:         "With malformed input",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"key":"value","response":}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"%s":"failed to unmarshal request body: invalid character '}' looking for beginning of value"}`, auditLogErrorKey)),
     		},
     
     		{
    -			Name:     "With secret string data with last-applied-configuration annotation",
    -			Uri:      "/secrets",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"apiVersion": "v1", "stringData": {"secret": "02020202020202020202"}, "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"opaque-secret2\",\"namespace\":\"default\"},\"stringData\":{\"secret\":\"02020202020202020202\"}}"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`),
    -			Expected: []byte(fmt.Sprintf(`{"apiVersion": "v1", "stringData": "%s", "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "%[1]s"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`, redacted)),
    +			Name:         "With secret string data with last-applied-configuration annotation",
    +			Uri:          "/secrets",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"apiVersion": "v1", "stringData": {"secret": "02020202020202020202"}, "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"opaque-secret2\",\"namespace\":\"default\"},\"stringData\":{\"secret\":\"02020202020202020202\"}}"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"apiVersion": "v1", "stringData": "%s", "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "%[1]s"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`, redacted)),
     		},
     
     		{
    -			Name:     "With secret data with last-applied-configuration annotation",
    -			Uri:      "/secrets",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"apiVersion": "v1", "data": {"secret": "02020202020202020202"}, "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"opaque-secret2\",\"namespace\":\"default\"},\"data\":{\"secret\":\"02020202020202020202\"}}"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`),
    -			Expected: []byte(fmt.Sprintf(`{"apiVersion": "v1", "data": "%s", "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "%[1]s"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`, redacted)),
    +			Name:         "With secret data with last-applied-configuration annotation",
    +			Uri:          "/secrets",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"apiVersion": "v1", "data": {"secret": "02020202020202020202"}, "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"opaque-secret2\",\"namespace\":\"default\"},\"data\":{\"secret\":\"02020202020202020202\"}}"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"apiVersion": "v1", "data": "%s", "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "%[1]s"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}`, redacted)),
     		},
     
     		{
    -			Name:     "With configmap data",
    -			Uri:      "/configmaps",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"metadata":{"namespace":"default","name":"my_configmap"},"data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\", "bar": "U3VwZXIgU2VjcmV0IERhdGEK"}, "accessToken" : "fake_access_token"}`),
    -			Expected: []byte(fmt.Sprintf(`{"metadata":{"namespace":"default","name":"my_configmap"},"data":"%s", "accessToken" : "%[1]s"}`, redacted)),
    +			Name:         "With secret list with last-applied-configuration annotation",
    +			Uri:          "/secrets",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type": "collection", "data": [{"apiVersion": "v1", "data": {"secret": "02020202020202020202"}, "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"opaque-secret2\",\"namespace\":\"default\"},\"data\":{\"secret\":\"02020202020202020202\"}}"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"type": "collection", "data": [{"apiVersion": "v1", "data": "%s", "kind": "Secret", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "%[1]s"}, "name": "opaque-secret2", "namespace": "default"}, "type": "Opaque"}]}`, redacted)),
    +		},
    +
    +		{
    +			Name:         "With configmap data",
    +			Uri:          "/configmaps",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"metadata":{"namespace":"default","name":"my_configmap"},"data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\", "bar": "U3VwZXIgU2VjcmV0IERhdGEK"}, "accessToken" : "fake_access_token"}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"metadata":{"namespace":"default","name":"my_configmap"},"data":"%s", "accessToken" : "%[1]s"}`, redacted)),
     		},
     		{
    -			Name:     "With config map list data",
    -			Uri:      "/configmaps",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"type": "collection", "data":[{"metadata":{"namespace":"default","name":"my_configmap"},"data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\", "bar": "U3VwZXIgU2VjcmV0IERhdGEK"}},{"metadata":{"namespace":"default","name":"my_configmap_2"},"data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\", "bar": "U3VwZXIgU2VjcmV0IERhdGEK"}}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"type": "collection", "data":[{"metadata":{"namespace":"default","name":"my_configmap"},"data":"%s"},{"metadata":{"namespace":"default","name":"my_configmap_2"},"data":"%[1]s"}]}`, redacted)),
    +			Name:         "With config map list data",
    +			Uri:          "/configmaps",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type": "collection", "data":[{"metadata":{"namespace":"default","name":"my_configmap"},"data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\", "bar": "U3VwZXIgU2VjcmV0IERhdGEK"}},{"metadata":{"namespace":"default","name":"my_configmap_2"},"data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\", "bar": "U3VwZXIgU2VjcmV0IERhdGEK"}}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"type": "collection", "data":[{"metadata":{"namespace":"default","name":"my_configmap"},"data":"%s"},{"metadata":{"namespace":"default","name":"my_configmap_2"},"data":"%[1]s"}]}`, redacted)),
     		},
     		{
     			// norman transforms some configmap subtypes to where their data fields cannot be distinguished from non-sensitive fields.
     			// In this case, all fields aside from id, created, and baseType should be redacted.
    -			Name:     "With configmap list data but no data field for array elements",
    -			Uri:      "/configmaps",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"type": "collection", "data":[{"id":"p-12345:testconfigmap","baseType":"configmap","foo":"something","bar":"something","accessToken":"token"}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"data":[{"accessToken":"%[1]s","bar":"%[1]s","baseType":"configmap","foo":"%[1]s","id":"p-12345:testconfigmap"}],"type":"collection"}`, redacted)),
    +			Name:         "With configmap list data but no data field for array elements",
    +			Uri:          "/configmaps",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"type": "collection", "data":[{"id":"p-12345:testconfigmap","baseType":"configmap","foo":"something","bar":"something","accessToken":"token"}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"data":[{"accessToken":"%[1]s","bar":"%[1]s","baseType":"configmap","foo":"%[1]s","id":"p-12345:testconfigmap"}],"type":"collection"}`, redacted)),
     		},
     		{
    -			Name:     "With secret list data from k8s proxy",
    -			Uri:      "/k8s/clusters/local/api/v1/configmaps?limit=500",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"kind": "ConfigMapList", "items":[{"metadata":{"namespace":"default","name":"my_configmap"},"data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\", "bar": "U3VwZXIgU2VjcmV0IERhdGEK"}}]}`),
    -			Expected: []byte(fmt.Sprintf(`{"kind": "ConfigMapList", "items":[{"metadata":{"namespace":"default","name":"my_configmap"},"data":"%s"}]}`, redacted)),
    +			Name:         "With secret list data from k8s proxy",
    +			Uri:          "/k8s/clusters/local/api/v1/configmaps?limit=500",
    +			Headers:      http.Header{"Content-Type": {contentTypeJSON}},
    +			Body:         []byte(`{"kind": "ConfigMapList", "items":[{"metadata":{"namespace":"default","name":"my_configmap"},"data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\", "bar": "U3VwZXIgU2VjcmV0IERhdGEK"}}]}`),
    +			ExpectedBody: []byte(fmt.Sprintf(`{"kind": "ConfigMapList", "items":[{"metadata":{"namespace":"default","name":"my_configmap"},"data":"%s"}]}`, redacted)),
     		},
     		{
    -			Name:     "With configmap data and wrong URI",
    -			Uri:      "nont-configmap",
    -			Headers:  http.Header{"Content-Type": {contentTypeJSON}},
    -			Body:     []byte(`{"metadata":{"namespace":"default","name":"my_configmap"},"data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=\\", "bar": "U3VwZXIgU2VjcmV0IERhdGEK"}, "accessToken" : "fake_access_token"}`),
    -			Expected: []byte(fmt.Sprintf(`{"metadata":{"namespace":"default","name":"my_configmap"},"data":{"foo":"c3VwZXIgc2VjcmV0IGRhdGE=
    ... [truncated]
    
  • pkg/auth/audit/redact.go+1 1 modified
    @@ -168,7 +168,7 @@ func redactSecret(log *log) error {
     	}
     
     	if strings.Contains(log.RequestURI, "secrets") || pairMatches(log.ResponseBody, checkForBasetype(secretBaseType)) {
    -		redactDataFromBody(log, log.RequestBody, "SecretList")
    +		redactDataFromBody(log, log.ResponseBody, "SecretList")
     	}
     
     	return nil
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.