-
Notifications
You must be signed in to change notification settings - Fork 200
internal: Add support for 'sensitive' JSON tag for bundle config fields #5896
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0cce77e
0eefe27
7abdf8a
f930a69
687b636
f8ff27e
b5da9aa
2e0b5c7
f52fb6a
5ae8b6d
9879c7c
f598e30
0847e11
49ea5f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -285,6 +285,15 @@ func (n normalizeOptions) normalizeString(typ reflect.Type, src dyn.Value, path | |
|
|
||
| switch src.Kind() { | ||
| case dyn.KindString: | ||
| // A sensitive string must be returned as-is: dyn.NewValue(MustString(), ...) | ||
| // would construct a plain string and strip the secretString wrapper. Type | ||
| // normalization (e.g. coercing a bool "true" to string) cannot apply to a | ||
| // sensitive value because its Kind is already KindString, so returning early | ||
| // here is correct. Updating the secret value is done via FromTyped, not | ||
| // Normalize: FromTyped re-wraps the new value as NewSensitiveValue. | ||
| if src.IsSensitive() { | ||
| return src, nil | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This means it is not possible to update the secret value in the typed tree. If expected, please include this expectation in the comment. |
||
| } | ||
| return dyn.NewValue(src.MustString(), src.Locations()), nil | ||
| case dyn.KindBool: | ||
| return dyn.NewValue(strconv.FormatBool(src.MustBool()), src.Locations()), nil | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,10 @@ type structInfo struct { | |
| // Maps JSON-name of the field to Golang struct name | ||
| GolangNames map[string]string | ||
|
|
||
| // Sensitive tracks fields tagged `bundle:"sensitive"` by their JSON name. | ||
| // Values for these fields should be masked in display output. | ||
| Sensitive map[string]bool | ||
|
andrewnester marked this conversation as resolved.
|
||
|
|
||
| // ForceSendFieldsIndex maps the JSON-name of the field to the index path (for | ||
| // use with [reflect.Value.FieldByIndex]) of the ForceSendFields slice that | ||
| // governs it: the one declared by the struct that also declares the field. | ||
|
|
@@ -68,6 +72,7 @@ func buildStructInfo(typ reflect.Type) structInfo { | |
| ForceEmpty: make(map[string]bool), | ||
| GolangNames: make(map[string]string), | ||
| ForceSendFieldsIndex: make(map[string][]int), | ||
| Sensitive: make(map[string]bool), | ||
| } | ||
|
|
||
| // Queue holds the indexes of the structs to visit. | ||
|
|
@@ -134,6 +139,11 @@ func buildStructInfo(typ reflect.Type) structInfo { | |
| } | ||
| out.GolangNames[name] = sf.Name | ||
|
|
||
| btag := structtag.BundleTag(sf.Tag.Get("bundle")) | ||
| if btag.Sensitive() { | ||
| out.Sensitive[name] = true | ||
| } | ||
|
|
||
| // The field is declared directly in this struct, so it is governed by | ||
| // this struct's ForceSendFields (if it has one). | ||
| if forceSendFieldsIndex != nil { | ||
|
|
@@ -176,6 +186,25 @@ func (s *structInfo) FieldValues(v reflect.Value) []FieldValue { | |
| return out | ||
| } | ||
|
|
||
| // SensitiveFieldNames returns the JSON field names of typ that carry the | ||
| // `bundle:"sensitive"` tag. A pointer type is dereferenced before inspection. | ||
| // Returns nil for non-struct types. Callers use this to identify fields that | ||
| // must be masked in display output (validate -o json, plan -o json) without | ||
| // touching the typed values used by the actual deployment pipeline. | ||
| func SensitiveFieldNames(typ reflect.Type) map[string]bool { | ||
| for typ.Kind() == reflect.Pointer { | ||
| typ = typ.Elem() | ||
| } | ||
| if typ.Kind() != reflect.Struct { | ||
| return nil | ||
| } | ||
| si := getStructInfo(typ) | ||
| if len(si.Sensitive) == 0 { | ||
| return nil | ||
| } | ||
| return si.Sensitive | ||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a few unit tests here in dyn/convert that demonstrates that this works in cases with embedded structs, anonymous structs, etc. Would also be good to include a user of these fields in the same package. |
||
| // isForceSend reports whether the field named k is listed in the ForceSendFields | ||
| // that governs it (see structInfo.ForceSendFieldsIndex). | ||
| func (s *structInfo) isForceSend(v reflect.Value, k string) bool { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.