@@ -10,6 +10,7 @@ import (
10
10
"reflect"
11
11
"regexp"
12
12
"strings"
13
+ "unsafe"
13
14
14
15
"github.com/mitchellh/reflectwalk"
15
16
)
@@ -48,6 +49,16 @@ type TrustedValue interface {
48
49
IsTrustedValue ()
49
50
}
50
51
52
+ type TrustedValueWrap struct {
53
+ Value any
54
+ }
55
+
56
+ func (TrustedValueWrap ) IsTrustedValue () {}
57
+
58
+ func (t TrustedValueWrap ) MarshalJSON () ([]byte , error ) {
59
+ return json .Marshal (t .Value )
60
+ }
61
+
51
62
// Scrubber defines the interface for a scrubber, which can sanitise various types of data.
52
63
// The scrubbing process involves removing or replacing sensitive data to prevent it from being exposed.
53
64
//
@@ -86,6 +97,8 @@ type Scrubber interface {
86
97
// }
87
98
//
88
99
Struct (val any ) error
100
+
101
+ DeepCopyStruct (val any ) any
89
102
}
90
103
91
104
// Default is the default scrubber consumers of this package should use
@@ -189,6 +202,129 @@ func (s *scrubberImpl) Struct(val any) error {
189
202
return nil
190
203
}
191
204
205
+ func (s * scrubberImpl ) deepCopyStruct (fieldName string , src reflect.Value , scrubTag string ) reflect.Value {
206
+ if src .Kind () == reflect .Ptr && src .IsNil () {
207
+ return reflect .New (src .Type ()).Elem ()
208
+ }
209
+
210
+ if src .Kind () == reflect .String {
211
+ dst := reflect .New (src .Type ())
212
+ var (
213
+ setExplicitValue bool
214
+ explicitValue string
215
+ )
216
+ switch scrubTag {
217
+ case "ignore" :
218
+ return dst
219
+ case "hash" :
220
+ setExplicitValue = true
221
+ explicitValue = SanitiseHash (src .String ())
222
+ case "redact" :
223
+ setExplicitValue = true
224
+ explicitValue = SanitiseRedact (src .String ())
225
+ }
226
+
227
+ if setExplicitValue {
228
+ dst .Elem ().SetString (explicitValue )
229
+ } else {
230
+ sanitisatiser := s .getSanitisatiser (fieldName )
231
+ if sanitisatiser != nil {
232
+ dst .Elem ().SetString (sanitisatiser (src .String ()))
233
+ } else {
234
+ dst .Elem ().SetString (s .Value (src .String ()))
235
+ }
236
+ }
237
+ if ! dst .CanInterface () {
238
+ return dst
239
+ }
240
+ return dst .Elem ()
241
+ }
242
+
243
+ switch src .Kind () {
244
+
245
+ case reflect .Struct :
246
+ dst := reflect .New (src .Type ())
247
+ t := src .Type ()
248
+
249
+ for i := 0 ; i < t .NumField (); i ++ {
250
+ f := t .Field (i )
251
+ srcValue := src .Field (i )
252
+ dstValue := dst .Elem ().Field (i )
253
+
254
+ if ! srcValue .CanInterface () {
255
+ dstValue = reflect .NewAt (dstValue .Type (), unsafe .Pointer (dstValue .UnsafeAddr ())).Elem ()
256
+
257
+ if ! srcValue .CanAddr () {
258
+ switch {
259
+ case srcValue .CanInt ():
260
+ dstValue .SetInt (srcValue .Int ())
261
+ case srcValue .CanUint ():
262
+ dstValue .SetUint (srcValue .Uint ())
263
+ case srcValue .CanFloat ():
264
+ dstValue .SetFloat (srcValue .Float ())
265
+ case srcValue .CanComplex ():
266
+ dstValue .SetComplex (srcValue .Complex ())
267
+ case srcValue .Kind () == reflect .Bool :
268
+ dstValue .SetBool (srcValue .Bool ())
269
+ }
270
+
271
+ continue
272
+ }
273
+
274
+ srcValue = reflect .NewAt (srcValue .Type (), unsafe .Pointer (srcValue .UnsafeAddr ())).Elem ()
275
+ }
276
+
277
+ tagValue := f .Tag .Get ("scrub" )
278
+ copied := s .deepCopyStruct (f .Name , srcValue , tagValue )
279
+ dstValue .Set (copied )
280
+ }
281
+ return dst .Elem ()
282
+
283
+ case reflect .Map :
284
+ dst := reflect .MakeMap (src .Type ())
285
+ keys := src .MapKeys ()
286
+ for i := 0 ; i < src .Len (); i ++ {
287
+ mValue := src .MapIndex (keys [i ])
288
+ dst .SetMapIndex (keys [i ], s .deepCopyStruct (keys [i ].String (), mValue , "" ))
289
+ }
290
+ return dst
291
+
292
+ case reflect .Slice :
293
+ dst := reflect .MakeSlice (src .Type (), src .Len (), src .Cap ())
294
+ for i := 0 ; i < src .Len (); i ++ {
295
+ dst .Index (i ).Set (s .deepCopyStruct (fieldName , src .Index (i ), "" ))
296
+ }
297
+ return dst
298
+
299
+ case reflect .Array :
300
+ if src .Len () == 0 {
301
+ return src // can not access to src.Index(0)
302
+ }
303
+
304
+ dst := reflect .New (src .Type ()).Elem ()
305
+ for i := 0 ; i < src .Len (); i ++ {
306
+ dst .Index (i ).Set (s .deepCopyStruct (fieldName , src .Index (i ), "" ))
307
+ }
308
+ return dst
309
+
310
+ case reflect .Ptr :
311
+ dst := reflect .New (src .Elem ().Type ())
312
+ copied := s .deepCopyStruct (fieldName , src .Elem (), scrubTag )
313
+ dst .Elem ().Set (copied )
314
+ return dst
315
+
316
+ default :
317
+ dst := reflect .New (src .Type ())
318
+ dst .Elem ().Set (src )
319
+ return dst .Elem ()
320
+ }
321
+ }
322
+
323
+ // Struct implements Scrubber
324
+ func (s * scrubberImpl ) DeepCopyStruct (val any ) any {
325
+ return s .deepCopyStruct ("" , reflect .ValueOf (val ), "" ).Interface ()
326
+ }
327
+
192
328
func (s * scrubberImpl ) scrubJsonObject (val map [string ]interface {}) error {
193
329
// fix https://github.com/gitpod-io/security/issues/64
194
330
name , _ := val ["name" ].(string )
0 commit comments