Skip to content

Commit 8989dd7

Browse files
authored
Merge pull request #2 from bfoley13/bfole13/redact
Extending for map/list/embedded structs
2 parents 987d4b6 + a304eb7 commit 8989dd7

File tree

2 files changed

+141
-36
lines changed

2 files changed

+141
-36
lines changed

redact.go

Lines changed: 67 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ func Redact(iface interface{}) error {
2525
if ifv.Kind() != reflect.Ptr {
2626
return errors.New("Not a pointer")
2727
}
28+
2829
ift := reflect.Indirect(ifv).Type()
2930
if ift.Kind() != reflect.Struct {
3031
return nil
@@ -33,42 +34,6 @@ func Redact(iface interface{}) error {
3334
v := ift.Field(i)
3435
el := reflect.Indirect(ifv.Elem().FieldByName(v.Name))
3536
switch el.Kind() {
36-
case reflect.Slice:
37-
if el.CanInterface() {
38-
elType := getSliceElemType(v.Type)
39-
40-
// allow strings and string pointers
41-
str := ""
42-
if (elType.ConvertibleTo(reflect.TypeOf(str)) && reflect.TypeOf(str).ConvertibleTo(elType)) ||
43-
(elType.ConvertibleTo(reflect.TypeOf(&str)) && reflect.TypeOf(&str).ConvertibleTo(elType)) {
44-
tagVal := v.Tag.Get(tagName)
45-
for i := 0; i < el.Len(); i++ {
46-
el.Index(i).Set(transformValue(tagVal, el.Index(i)))
47-
}
48-
} else {
49-
val := reflect.ValueOf(el.Interface())
50-
for i := 0; i < val.Len(); i++ {
51-
elVal := val.Index(i)
52-
if elVal.Kind() != reflect.Ptr {
53-
elVal = elVal.Addr()
54-
}
55-
Redact(elVal.Interface())
56-
}
57-
}
58-
}
59-
case reflect.Map:
60-
if el.CanInterface() {
61-
val := reflect.ValueOf(el.Interface())
62-
for _, key := range val.MapKeys() {
63-
mapValue := val.MapIndex(key)
64-
mapValuePtr := reflect.New(mapValue.Type())
65-
mapValuePtr.Elem().Set(mapValue)
66-
if mapValuePtr.Elem().CanAddr() {
67-
Redact(mapValuePtr.Elem().Addr().Interface())
68-
}
69-
val.SetMapIndex(key, reflect.Indirect(mapValuePtr))
70-
}
71-
}
7237
case reflect.Struct:
7338
if el.CanAddr() && el.Addr().CanInterface() {
7439
Redact(el.Addr().Interface())
@@ -79,6 +44,72 @@ func Redact(iface interface{}) error {
7944
input := el.String()
8045
el.SetString(transformString(input, tagVal))
8146
}
47+
default:
48+
tagVal := v.Tag.Get(tagName)
49+
if el.CanAddr() && el.Addr().CanInterface() {
50+
redactHelper(el.Addr().Interface(), tagVal)
51+
}
52+
53+
}
54+
}
55+
return nil
56+
}
57+
58+
func redactHelper(iface interface{}, tagVal string) error {
59+
ifv := reflect.ValueOf(iface)
60+
if ifv.Kind() != reflect.Ptr {
61+
return errors.New("Not a pointer")
62+
}
63+
64+
ifIndirectValue := reflect.Indirect(ifv)
65+
switch ifIndirectValue.Kind() {
66+
case reflect.Slice:
67+
if ifIndirectValue.CanInterface() {
68+
elType := getSliceElemType(ifIndirectValue.Type())
69+
70+
// allow strings and string pointers
71+
str := ""
72+
if (elType.ConvertibleTo(reflect.TypeOf(str)) && reflect.TypeOf(str).ConvertibleTo(elType)) ||
73+
(elType.ConvertibleTo(reflect.TypeOf(&str)) && reflect.TypeOf(&str).ConvertibleTo(elType)) {
74+
for i := 0; i < ifIndirectValue.Len(); i++ {
75+
ifIndirectValue.Index(i).Set(transformValue(tagVal, ifIndirectValue.Index(i)))
76+
}
77+
} else {
78+
val := reflect.ValueOf(ifIndirectValue.Interface())
79+
for i := 0; i < val.Len(); i++ {
80+
elVal := val.Index(i)
81+
if elVal.Kind() != reflect.Ptr {
82+
elVal = elVal.Addr()
83+
}
84+
redactHelper(elVal.Interface(), tagVal)
85+
}
86+
}
87+
}
88+
case reflect.Map:
89+
if ifIndirectValue.CanInterface() {
90+
val := reflect.ValueOf(ifIndirectValue.Interface())
91+
for _, key := range val.MapKeys() {
92+
mapValue := val.MapIndex(key)
93+
mapValuePtr := reflect.New(mapValue.Type())
94+
mapValuePtr.Elem().Set(mapValue)
95+
if mapValuePtr.Elem().CanAddr() {
96+
redactHelper(mapValuePtr.Elem().Addr().Interface(), tagVal)
97+
}
98+
val.SetMapIndex(key, reflect.Indirect(mapValuePtr))
99+
}
100+
}
101+
case reflect.Struct:
102+
if ifIndirectValue.CanAddr() && ifIndirectValue.Addr().CanInterface() {
103+
Redact(ifIndirectValue.Addr().Interface())
104+
}
105+
case reflect.String:
106+
if ifIndirectValue.CanSet() {
107+
input := ifIndirectValue.String()
108+
ifIndirectValue.SetString(transformString(input, tagVal))
109+
}
110+
case reflect.Ptr:
111+
if ifIndirectValue.CanInterface() {
112+
redactHelper(ifIndirectValue.Interface(), tagVal)
82113
}
83114
}
84115
return nil

redact_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ type TestStructList struct {
2626
Data []*TestStruct
2727
}
2828

29+
type TestMaps struct {
30+
Secrets map[string]string
31+
SecretPtrs map[string]*string
32+
TestStructSecrets map[string]*TestStruct
33+
}
34+
35+
type TestMapList struct {
36+
Data []*TestMaps
37+
}
38+
2939
func TestStringTestStruct(t *testing.T) {
3040
t.Run("Basic Secret Redaction", func(t *testing.T) {
3141
tStruct := &TestStruct{
@@ -103,3 +113,67 @@ func TestStringTestStructList(t *testing.T) {
103113
})
104114

105115
}
116+
117+
func TestStringTestMapAndEmbedded(t *testing.T) {
118+
t.Run("Should Redact Map And Slice Structs", func(t *testing.T) {
119+
tMaps := &TestMaps{
120+
Secrets: map[string]string{
121+
"secret-key-old": secretVal,
122+
"secret-key-new": secretVal,
123+
},
124+
SecretPtrs: map[string]*string{
125+
"ptr-secret-key": &secretPtrVal,
126+
},
127+
TestStructSecrets: map[string]*TestStruct{
128+
"ptr-test-struct-key": {
129+
NonSecret: nonSecretVal,
130+
Secret: secretVal,
131+
SecretPtr: &secretPtrVal,
132+
},
133+
},
134+
}
135+
136+
err := redact.Redact(tMaps)
137+
assert.NoError(t, err, "should not fail to redact struct")
138+
139+
assert.Equal(t, redact.RedactStrConst, tMaps.Secrets["secret-key-old"], "should redact secret value")
140+
assert.Equal(t, redact.RedactStrConst, tMaps.Secrets["secret-key-new"], "should redact secret value")
141+
assert.Equal(t, redact.RedactStrConst, *tMaps.SecretPtrs["ptr-secret-key"], "should redact secret value")
142+
assert.Equal(t, redact.RedactStrConst, tMaps.TestStructSecrets["ptr-test-struct-key"].Secret, "should redact secret value")
143+
assert.Equal(t, redact.RedactStrConst, *tMaps.TestStructSecrets["ptr-test-struct-key"].SecretPtr, "should redact secret value")
144+
assert.Equal(t, nonSecretVal, tMaps.TestStructSecrets["ptr-test-struct-key"].NonSecret, "should redact secret value")
145+
})
146+
147+
t.Run("Should Redact Map And Slice Structs", func(t *testing.T) {
148+
tMaps := &TestMaps{
149+
Secrets: map[string]string{
150+
"secret-key-old": secretVal,
151+
"secret-key-new": secretVal,
152+
},
153+
SecretPtrs: map[string]*string{
154+
"ptr-secret-key": &secretPtrVal,
155+
},
156+
TestStructSecrets: map[string]*TestStruct{
157+
"ptr-test-struct-key": {
158+
NonSecret: nonSecretVal,
159+
Secret: secretVal,
160+
SecretPtr: &secretPtrVal,
161+
},
162+
},
163+
}
164+
165+
testMapList := &TestMapList{
166+
Data: []*TestMaps{tMaps},
167+
}
168+
169+
err := redact.Redact(testMapList)
170+
assert.NoError(t, err, "should not fail to redact struct")
171+
172+
assert.Equal(t, redact.RedactStrConst, testMapList.Data[0].Secrets["secret-key-old"], "should redact secret value")
173+
assert.Equal(t, redact.RedactStrConst, testMapList.Data[0].Secrets["secret-key-new"], "should redact secret value")
174+
assert.Equal(t, redact.RedactStrConst, *testMapList.Data[0].SecretPtrs["ptr-secret-key"], "should redact secret value")
175+
assert.Equal(t, redact.RedactStrConst, testMapList.Data[0].TestStructSecrets["ptr-test-struct-key"].Secret, "should redact secret value")
176+
assert.Equal(t, redact.RedactStrConst, *testMapList.Data[0].TestStructSecrets["ptr-test-struct-key"].SecretPtr, "should redact secret value")
177+
assert.Equal(t, nonSecretVal, testMapList.Data[0].TestStructSecrets["ptr-test-struct-key"].NonSecret, "should redact secret value")
178+
})
179+
}

0 commit comments

Comments
 (0)