The 'HTTP Request Parameters' audit event now logs params as a JSON object

This commit is contained in:
Joshua Casey
2024-11-27 13:53:01 -06:00
parent dc6faa33bb
commit 18d3ab3d15
8 changed files with 376 additions and 96 deletions
+31 -11
View File
@@ -31,19 +31,39 @@ const (
// SanitizeParams can be used to redact all params not included in the allowedKeys set.
// Useful when audit logging AuditEventHTTPRequestParameters events.
func SanitizeParams(params url.Values, allowedKeys sets.Set[string]) string {
if len(params) == 0 {
return ""
func SanitizeParams(inputParams url.Values, allowedKeys sets.Set[string]) []any {
params := make(map[string]string)
multiValueParams := make(url.Values)
transform := func(key, value string) string {
if !allowedKeys.Has(key) {
return "redacted"
}
unescape, err := url.QueryUnescape(value)
if err != nil {
// ignore these errors and just use the original query parameter
unescape = value
}
return unescape
}
sanitized := url.Values{}
for key := range params {
if allowedKeys.Has(key) {
sanitized[key] = params[key]
} else {
for range params[key] {
sanitized.Add(key, "redacted")
for key := range inputParams {
for i, p := range inputParams[key] {
transformed := transform(key, p)
if i == 0 {
params[key] = transformed
}
if len(inputParams[key]) > 1 {
multiValueParams[key] = append(multiValueParams[key], transformed)
}
}
}
return sanitized.Encode()
if len(multiValueParams) > 0 {
return []any{"params", params, "multiValueParams", multiValueParams}
}
return []any{"params", params}
}
+108 -12
View File
@@ -16,60 +16,156 @@ func TestSanitizeParams(t *testing.T) {
name string
params url.Values
allowedKeys sets.Set[string]
want string
want []any
}{
{
name: "nil values",
params: nil,
allowedKeys: nil,
want: "",
want: []any{
"params",
map[string]string{},
},
},
{
name: "empty values",
params: url.Values{},
allowedKeys: nil,
want: "",
want: []any{
"params",
map[string]string{},
},
},
{
name: "all allowed values",
params: url.Values{"foo": []string{"a", "b", "c"}, "bar": []string{"d", "e", "f"}},
allowedKeys: sets.New("foo", "bar"),
want: "bar=d&bar=e&bar=f&foo=a&foo=b&foo=c",
want: []any{
"params",
map[string]string{
"bar": "d",
"foo": "a",
},
"multiValueParams",
url.Values{
"bar": []string{"d", "e", "f"},
"foo": []string{"a", "b", "c"},
},
},
},
{
name: "all allowed values with single values",
params: url.Values{"foo": []string{"a"}, "bar": []string{"d"}},
allowedKeys: sets.New("foo", "bar"),
want: "bar=d&foo=a",
want: []any{
"params",
map[string]string{
"foo": "a",
"bar": "d",
},
},
},
{
name: "some allowed values",
params: url.Values{"foo": []string{"a", "b", "c"}, "bar": []string{"d", "e", "f"}},
allowedKeys: sets.New("foo"),
want: "bar=redacted&bar=redacted&bar=redacted&foo=a&foo=b&foo=c",
want: []any{
"params",
map[string]string{
"bar": "redacted",
"foo": "a",
},
"multiValueParams",
url.Values{
"bar": []string{"redacted", "redacted", "redacted"},
"foo": []string{"a", "b", "c"},
},
},
},
{
name: "some allowed values with single values",
params: url.Values{"foo": []string{"a"}, "bar": []string{"d"}},
allowedKeys: sets.New("foo"),
want: "bar=redacted&foo=a",
want: []any{
"params",
map[string]string{
"bar": "redacted",
"foo": "a",
},
},
},
{
name: "no allowed values",
params: url.Values{"foo": []string{"a", "b", "c"}, "bar": []string{"d", "e", "f"}},
allowedKeys: sets.New[string](),
want: "bar=redacted&bar=redacted&bar=redacted&foo=redacted&foo=redacted&foo=redacted",
want: []any{
"params",
map[string]string{
"bar": "redacted",
"foo": "redacted",
},
"multiValueParams",
url.Values{
"bar": {"redacted", "redacted", "redacted"},
"foo": {"redacted", "redacted", "redacted"},
},
},
},
{
name: "nil allowed values",
params: url.Values{"foo": []string{"a", "b", "c"}, "bar": []string{"d", "e", "f"}},
allowedKeys: nil,
want: "bar=redacted&bar=redacted&bar=redacted&foo=redacted&foo=redacted&foo=redacted",
want: []any{
"params",
map[string]string{
"bar": "redacted",
"foo": "redacted",
},
"multiValueParams",
url.Values{
"bar": {"redacted", "redacted", "redacted"},
"foo": {"redacted", "redacted", "redacted"},
},
},
},
{
name: "url decodes allowed values",
params: url.Values{
"foo": []string{"a%3Ab", "c", "urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange"},
"bar": []string{"d", "e", "f"},
},
allowedKeys: sets.New("foo"),
want: []any{
"params",
map[string]string{
"bar": "redacted",
"foo": "a:b",
},
"multiValueParams",
url.Values{
"bar": {"redacted", "redacted", "redacted"},
"foo": {"a:b", "c", "urn:ietf:params:oauth:grant-type:token-exchange"},
},
},
},
{
name: "ignores url decode errors",
params: url.Values{
"bad_encoding": []string{"%.."},
},
allowedKeys: sets.New("bad_encoding"),
want: []any{
"params",
map[string]string{
"bad_encoding": "%..",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, SanitizeParams(tt.params, tt.allowedKeys))
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
// This comparison should require the exact order
require.Equal(t, test.want, SanitizeParams(test.params, test.allowedKeys))
})
}
}