-
Notifications
You must be signed in to change notification settings - Fork 15
/
redact.go
76 lines (69 loc) · 2.36 KB
/
redact.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package utils
import (
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/descriptorpb"
v1alpha "github.com/kaskada-ai/kaskada/gen/proto/go/kaskada/kaskada/v1alpha"
)
const redacted_string string = "...redacted..."
// redacts every sensitive field in a protobuf message.
// if the sensitive field is a string, it replaces the string with "redacted"
// otherwise it clears the field
func RedactInPlace(pb proto.Message) {
m := pb.ProtoReflect()
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
opts := fd.Options().(*descriptorpb.FieldOptions)
if proto.GetExtension(opts, v1alpha.E_Sensitive).(bool) {
if fd.Kind() == protoreflect.StringKind {
if fd.Cardinality() == protoreflect.Repeated {
// list of strings
for i := 0; i < v.List().Len(); i++ {
v.List().Set(i, protoreflect.ValueOfString(redacted_string))
}
} else {
m.Set(fd, protoreflect.ValueOfString(redacted_string))
}
} else {
m.Clear(fd)
}
}
if fd.Kind() == protoreflect.MessageKind {
if fd.IsList() {
for i := 0; i < v.List().Len(); i++ {
RedactInPlace(v.List().Get(i).Message().Interface())
}
} else if fd.IsMap() {
if v.Map().IsValid() {
if fd.MapValue().Kind() == protoreflect.MessageKind {
v.Map().Range(func(mapKey protoreflect.MapKey, mapValue protoreflect.Value) bool {
RedactInPlace(mapValue.Message().Interface())
return true
})
}
} else {
// for some reason, map[string]string isn't showing up as a "real" map
// and therefore '.Range()' can't be used on it, and there is no other
// way to get the set of keys in the map.
// for now, just clear the whole map if it is sensitive, instead of
// just redacting the values.
map_opts := fd.Options().(*descriptorpb.FieldOptions)
map_sensitive := proto.GetExtension(map_opts, v1alpha.E_Sensitive).(bool)
if map_sensitive {
m.Clear(fd)
}
}
} else {
RedactInPlace(v.Message().Interface())
}
}
return true
})
}
// returns a copy of a protobuf message with sensitive fields redacted
// if the sensitive field is a string, it replaces the string with "redacted"
// otherwise it clears the field
func RedactCopy(pb proto.Message) proto.Message {
pbCopy := proto.Clone(pb)
RedactInPlace(pbCopy)
return pbCopy
}