Skip to content

Commit 36e3fcb

Browse files
committed
protosanitizer: rewrite with protoreflect
1 parent 4b75450 commit 36e3fcb

File tree

4 files changed

+35
-5169
lines changed

4 files changed

+35
-5169
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ require (
88
github.com/stretchr/testify v1.9.0
99
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0
1010
go.opentelemetry.io/otel/trace v1.28.0
11-
golang.org/x/net v0.26.0
1211
google.golang.org/grpc v1.65.0
12+
google.golang.org/protobuf v1.34.2
1313
k8s.io/api v0.31.0
1414
k8s.io/client-go v0.31.0
1515
k8s.io/component-base v0.31.0
@@ -51,13 +51,13 @@ require (
5151
github.com/x448/float16 v0.8.4 // indirect
5252
go.opentelemetry.io/otel v1.28.0 // indirect
5353
go.opentelemetry.io/otel/metric v1.28.0 // indirect
54+
golang.org/x/net v0.26.0 // indirect
5455
golang.org/x/oauth2 v0.21.0 // indirect
5556
golang.org/x/sys v0.22.0 // indirect
5657
golang.org/x/term v0.21.0 // indirect
5758
golang.org/x/text v0.16.0 // indirect
5859
golang.org/x/time v0.3.0 // indirect
5960
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
60-
google.golang.org/protobuf v1.34.2 // indirect
6161
gopkg.in/inf.v0 v0.9.1 // indirect
6262
gopkg.in/yaml.v2 v2.4.0 // indirect
6363
gopkg.in/yaml.v3 v3.0.1 // indirect

protosanitizer/protosanitizer.go

Lines changed: 33 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,10 @@ package protosanitizer
2121
import (
2222
"encoding/json"
2323
"fmt"
24-
"reflect"
25-
"strings"
2624

27-
"github.com/golang/protobuf/descriptor"
28-
"github.com/golang/protobuf/proto"
29-
protobufdescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
25+
"github.com/container-storage-interface/spec/lib/go/csi"
26+
"google.golang.org/protobuf/proto"
27+
"google.golang.org/protobuf/reflect/protoreflect"
3028
)
3129

3230
// StripSecrets returns a wrapper around the original CSI gRPC message
@@ -42,20 +40,11 @@ import (
4240
// result to logging functions which may or may not end up serializing
4341
// the parameter depending on the current log level.
4442
func StripSecrets(msg interface{}) fmt.Stringer {
45-
return &stripSecrets{msg, isCSI1Secret}
46-
}
47-
48-
// StripSecretsCSI03 is like StripSecrets, except that it works
49-
// for messages based on CSI 0.3 and older. It does not work
50-
// for CSI 1.0, use StripSecrets for that.
51-
func StripSecretsCSI03(msg interface{}) fmt.Stringer {
52-
return &stripSecrets{msg, isCSI03Secret}
43+
return &stripSecrets{msg}
5344
}
5445

5546
type stripSecrets struct {
56-
msg interface{}
57-
58-
isSecretField func(field *protobufdescriptor.FieldDescriptorProto) bool
47+
msg any
5948
}
6049

6150
func (s *stripSecrets) String() string {
@@ -66,12 +55,16 @@ func (s *stripSecrets) String() string {
6655
if err != nil {
6756
return fmt.Sprintf("<<json.Marshal %T: %s>>", s.msg, err)
6857
}
58+
msg, ok := s.msg.(proto.Message)
59+
if !ok {
60+
return string(b)
61+
}
6962
if err := json.Unmarshal(b, &parsed); err != nil {
7063
return fmt.Sprintf("<<json.Unmarshal %T: %s>>", s.msg, err)
7164
}
7265

7366
// Now remove secrets from the generic representation of the message.
74-
s.strip(parsed, s.msg)
67+
s.strip(parsed, msg.ProtoReflect())
7568

7669
// Re-encoded the stripped representation and return that.
7770
b, err = json.Marshal(parsed)
@@ -81,13 +74,7 @@ func (s *stripSecrets) String() string {
8174
return string(b)
8275
}
8376

84-
func (s *stripSecrets) strip(parsed interface{}, msg interface{}) {
85-
protobufMsg, ok := msg.(descriptor.Message)
86-
if !ok {
87-
// Not a protobuf message, so we are done.
88-
return
89-
}
90-
77+
func (s *stripSecrets) strip(parsed interface{}, msg protoreflect.Message) {
9178
// The corresponding map in the parsed JSON representation.
9279
parsedFields, ok := parsed.(map[string]interface{})
9380
if !ok {
@@ -100,77 +87,33 @@ func (s *stripSecrets) strip(parsed interface{}, msg interface{}) {
10087
// on each field where the name matches the field name in the protobuf
10188
// spec (like volume_capabilities). The field.GetJsonName() method returns
10289
// a different name (volumeCapabilities) which we don't use.
103-
_, md := descriptor.ForMessage(protobufMsg)
104-
fields := md.GetField()
105-
if fields != nil {
106-
for _, field := range fields {
107-
if s.isSecretField(field) {
108-
// Overwrite only if already set.
109-
if _, ok := parsedFields[field.GetName()]; ok {
110-
parsedFields[field.GetName()] = "***stripped***"
111-
}
112-
} else if field.GetType() == protobufdescriptor.FieldDescriptorProto_TYPE_MESSAGE {
113-
// When we get here,
114-
// the type name is something like ".csi.v1.CapacityRange" (leading dot!)
115-
// and looking up "csi.v1.CapacityRange"
116-
// returns the type of a pointer to a pointer
117-
// to CapacityRange. We need a pointer to such
118-
// a value for recursive stripping.
119-
typeName := field.GetTypeName()
120-
if strings.HasPrefix(typeName, ".") {
121-
typeName = typeName[1:]
122-
}
123-
t := proto.MessageType(typeName)
124-
if t == nil || t.Kind() != reflect.Ptr {
125-
// Shouldn't happen, but
126-
// better check anyway instead
127-
// of panicking.
128-
continue
129-
}
130-
v := reflect.New(t.Elem())
131-
132-
// Recursively strip the message(s) that
133-
// the field contains.
134-
i := v.Interface()
135-
entry := parsedFields[field.GetName()]
136-
if slice, ok := entry.([]interface{}); ok {
137-
// Array of values, like VolumeCapabilities in CreateVolumeRequest.
138-
for _, entry := range slice {
139-
s.strip(entry, i)
140-
}
141-
} else {
142-
// Single value.
143-
s.strip(entry, i)
90+
msg.Range(func(field protoreflect.FieldDescriptor, v protoreflect.Value) bool {
91+
name := field.TextName()
92+
if isCSI1Secret(field) {
93+
// Overwrite only if already set.
94+
if _, ok := parsedFields[name]; ok {
95+
parsedFields[name] = "***stripped***"
96+
}
97+
} else if field.Kind() == protoreflect.MessageKind && !field.IsMap() {
98+
entry := parsedFields[name]
99+
if field.Cardinality() == protoreflect.Repeated {
100+
l := v.List()
101+
// Array of values, like VolumeCapabilities in CreateVolumeRequest.
102+
for i, entry := range entry.([]interface{}) {
103+
s.strip(entry, l.Get(i).Message())
144104
}
105+
} else {
106+
// Single value.
107+
s.strip(entry, v.Message())
145108
}
146109
}
147-
}
110+
return true
111+
})
148112
}
149113

150114
// isCSI1Secret uses the csi.E_CsiSecret extension from CSI 1.0 to
151115
// determine whether a field contains secrets.
152-
func isCSI1Secret(field *protobufdescriptor.FieldDescriptorProto) bool {
153-
ex, err := proto.GetExtension(field.Options, e_CsiSecret)
154-
return err == nil && ex != nil && *ex.(*bool)
155-
}
156-
157-
// Copied from the CSI 1.0 spec (https://github.com/container-storage-interface/spec/blob/37e74064635d27c8e33537c863b37ccb1182d4f8/lib/go/csi/csi.pb.go#L4520-L4527)
158-
// to avoid a package dependency that would prevent usage of this package
159-
// in repos using an older version of the spec.
160-
//
161-
// Future revision of the CSI spec must not change this extensions, otherwise
162-
// they will break filtering in binaries based on the 1.0 version of the spec.
163-
var e_CsiSecret = &proto.ExtensionDesc{
164-
ExtendedType: (*protobufdescriptor.FieldOptions)(nil),
165-
ExtensionType: (*bool)(nil),
166-
Field: 1059,
167-
Name: "csi.v1.csi_secret",
168-
Tag: "varint,1059,opt,name=csi_secret,json=csiSecret",
169-
Filename: "github.com/container-storage-interface/spec/csi.proto",
170-
}
171-
172-
// isCSI03Secret relies on the naming convention in CSI <= 0.3
173-
// to determine whether a field contains secrets.
174-
func isCSI03Secret(field *protobufdescriptor.FieldDescriptorProto) bool {
175-
return strings.HasSuffix(field.GetName(), "_secrets")
116+
func isCSI1Secret(desc protoreflect.FieldDescriptor) bool {
117+
ex := proto.GetExtension(desc.Options(), csi.E_CsiSecret)
118+
return ex.(bool)
176119
}

0 commit comments

Comments
 (0)