mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
fix(slog): masking of grouped attributes (#11606)
# Which Problems Are Solved Group attributes weren't masked if their corresponding key was configured. For example setting `data` as a masked key entry in the runtime config would still print unmasked event data. This was because ReplaceAttr does not receive group attributes, only the flattened attributes with group information. # How the Problems Are Solved Check the current groups stack in the replacer. When a configured key is found in the group, mask the current attribute. This means that the attribute structure is preserved and all sub-keys of a masked group are still printed. # Additional Changes - Use a binary search for key matching. As we are now comparing slice against slice, a binary search should make the process a little faster if many keys are configured. # Additional Context - Follow-up for PR #11435 --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: muhlemmer <[email protected]>
This commit is contained in:
co-authored by
Copilot
Copilot
muhlemmer
parent
93c6099482
commit
c02d812b6c
@@ -82,9 +82,22 @@ type MaskConfig struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
// replacer returns a function that replaces the value of any attribute whose key matches
|
||||
// one of the configured keys with the configured value.
|
||||
// If a group key matches, all attribute values in that group are replaced.
|
||||
// Attribute structure is preserved, e.g. "password" is replaced with "masked" but still appears as "password" in the logs.
|
||||
func (c MaskConfig) replacer() replacer {
|
||||
return func(_ []string, a slog.Attr) slog.Attr {
|
||||
if slices.Contains(c.Keys, a.Key) {
|
||||
keys := slices.Clone(c.Keys)
|
||||
slices.Sort(keys)
|
||||
return func(groups []string, a slog.Attr) slog.Attr {
|
||||
// mask all entries in a matching group, e.g. "data.*"
|
||||
for _, g := range groups {
|
||||
if _, found := slices.BinarySearch(keys, g); found {
|
||||
a.Value = slog.StringValue(c.Value)
|
||||
return a
|
||||
}
|
||||
}
|
||||
if _, found := slices.BinarySearch(keys, a.Key); found {
|
||||
a.Value = slog.StringValue(c.Value)
|
||||
}
|
||||
return a
|
||||
|
||||
@@ -72,7 +72,7 @@ func TestLogConfig_replacer(t *testing.T) {
|
||||
name: "masking configured key",
|
||||
c: LogConfig{
|
||||
Mask: MaskConfig{
|
||||
Keys: []string{"sensitive"},
|
||||
Keys: []string{"sensitive", "foo", "bar"},
|
||||
Value: "masked",
|
||||
},
|
||||
},
|
||||
@@ -85,7 +85,7 @@ func TestLogConfig_replacer(t *testing.T) {
|
||||
name: "masking configured key in any group",
|
||||
c: LogConfig{
|
||||
Mask: MaskConfig{
|
||||
Keys: []string{"sensitive"},
|
||||
Keys: []string{"sensitive", "foo", "bar"},
|
||||
Value: "masked",
|
||||
},
|
||||
},
|
||||
@@ -95,11 +95,39 @@ func TestLogConfig_replacer(t *testing.T) {
|
||||
},
|
||||
want: slog.String("sensitive", "masked"),
|
||||
},
|
||||
{
|
||||
name: "masking configured group",
|
||||
c: LogConfig{
|
||||
Mask: MaskConfig{
|
||||
Keys: []string{"sensitive", "foo", "bar"},
|
||||
Value: "masked",
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
groups: []string{"sensitive"},
|
||||
a: slog.String("unmatched", "value"),
|
||||
},
|
||||
want: slog.String("unmatched", "masked"),
|
||||
},
|
||||
{
|
||||
name: "masking configured sub-group",
|
||||
c: LogConfig{
|
||||
Mask: MaskConfig{
|
||||
Keys: []string{"sensitive", "foo", "bar"},
|
||||
Value: "masked",
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
groups: []string{"a", "sensitive", "b"},
|
||||
a: slog.String("unmatched", "value"),
|
||||
},
|
||||
want: slog.String("unmatched", "masked"),
|
||||
},
|
||||
{
|
||||
name: "not masking unmatched key",
|
||||
c: LogConfig{
|
||||
Mask: MaskConfig{
|
||||
Keys: []string{"sensitive"},
|
||||
Keys: []string{"sensitive", "foo", "bar"},
|
||||
Value: "masked",
|
||||
},
|
||||
},
|
||||
|
||||
+4
-2
@@ -59,8 +59,10 @@ Instrumentation:
|
||||
# Keys are unqualified attribute names and apply to all attributes with the specified name,
|
||||
# regardless of their position in the attribute hierarchy.
|
||||
# Eg. "some_key" matches "some_key" and "parent.some_key" etc.
|
||||
# If the matched log attribute is a nested object or array,
|
||||
# the entire structure is replaced with the specified Value.
|
||||
# When the key matches a group of attributes (e.g. slog.Group), all attribute values in the
|
||||
# group are masked and the structure of the group is preserved.
|
||||
# When the key matches a non-group attribute whose value is a nested object/array
|
||||
# (e.g. added via slog.Any), the entire value is replaced with Value and its structure is not preserved.
|
||||
Keys: # ZITADEL_INSTRUMENTATION_LOG_MASK_KEYS (comma separated list)
|
||||
# - "first_name"
|
||||
# - "last_name"
|
||||
|
||||
@@ -59,8 +59,10 @@ Instrumentation:
|
||||
# Keys are unqualified attribute names and apply to all attributes with the specified name,
|
||||
# regardless of their position in the attribute hierarchy.
|
||||
# Eg. "some_key" matches "some_key" and "parent.some_key" etc.
|
||||
# If the matched log attribute is a nested object or array,
|
||||
# the entire structure is replaced with the specified Value.
|
||||
# When the key matches a group of attributes (e.g. slog.Group), all attribute values in the
|
||||
# group are masked and the structure of the group is preserved.
|
||||
# When the key matches a non-group attribute whose value is a nested object/array
|
||||
# (e.g. added via slog.Any), the entire value is replaced with Value and its structure is not preserved.
|
||||
Keys: # ZITADEL_INSTRUMENTATION_LOG_MASK_KEYS (comma separated list)
|
||||
# - "first_name"
|
||||
# - "last_name"
|
||||
|
||||
@@ -59,8 +59,10 @@ Instrumentation:
|
||||
# Keys are unqualified attribute names and apply to all attributes with the specified name,
|
||||
# regardless of their position in the attribute hierarchy.
|
||||
# Eg. "some_key" matches "some_key" and "parent.some_key" etc.
|
||||
# If the matched log attribute is a nested object or array,
|
||||
# the entire structure is replaced with the specified Value.
|
||||
# When the key matches a group of attributes (e.g. slog.Group), all attribute values in the
|
||||
# group are masked and the structure of the group is preserved.
|
||||
# When the key matches a non-group attribute whose value is a nested object/array
|
||||
# (e.g. added via slog.Any), the entire value is replaced with Value and its structure is not preserved.
|
||||
Keys: # ZITADEL_INSTRUMENTATION_LOG_MASK_KEYS (comma separated list)
|
||||
# - "first_name"
|
||||
# - "last_name"
|
||||
|
||||
Reference in New Issue
Block a user