github.com/microsoft/moc@v0.17.1/pkg/redact/redact.go (about) 1 // Copyright (c) Microsoft Corporation. All rights reserved. 2 // Licensed under the Apache v2.0 license. 3 4 package redact 5 6 import ( 7 "encoding/json" 8 "reflect" 9 "strings" 10 11 "github.com/golang/protobuf/descriptor" 12 "github.com/golang/protobuf/proto" 13 descriptorpb "github.com/golang/protobuf/protoc-gen-go/descriptor" 14 "github.com/microsoft/moc/rpc/common" 15 ) 16 17 const ( 18 RedactedString = "** Redacted **" 19 ) 20 21 // RedactedMessage - returns a copy of a proto message struct with data from fields marked as sensitive redacted 22 func RedactedMessage(msg interface{}) interface{} { 23 rMsg := proto.Clone((msg).(proto.Message)) 24 Redact(rMsg, reflect.ValueOf(rMsg)) 25 return rMsg 26 } 27 28 // Redact - removes data from fields marked as sensitive given a proto message struct 29 func Redact(msg interface{}, val reflect.Value) { 30 // TODO: This needs to be optimized! 31 // Should cache messages that contain no sensitive data to ignore, and should cache the map of tag number to field name 32 33 if !val.IsValid() { 34 return 35 } 36 37 switch val.Kind() { 38 case reflect.Slice: 39 for i := 0; i < val.Len(); i += 1 { 40 Redact(val.Index(i).Interface(), val.Index(i)) 41 } 42 case reflect.Map: 43 // TODO: Implement map logic. Currently only certificates in identity use it, and are redacted 44 case reflect.Ptr: 45 Redact(msg, val.Elem()) 46 case reflect.Struct: 47 redactMessage(msg, val) 48 } 49 } 50 51 func redactMessage(msg interface{}, val reflect.Value) { 52 properties := proto.GetProperties(reflect.TypeOf(msg).Elem()) 53 _, md := descriptor.ForMessage((msg).(descriptor.Message)) 54 55 for _, field := range md.GetField() { 56 var fieldVal reflect.Value 57 if field.Options != nil || field.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE { 58 for _, p := range properties.Prop { 59 if int32(p.Tag) == field.GetNumber() { 60 fieldVal = val.FieldByName(p.Name) 61 break 62 } 63 } 64 if !fieldVal.IsValid() { 65 for _, oot := range properties.OneofTypes { 66 if int32(oot.Prop.Tag) == field.GetNumber() { 67 fieldVal = val.Field(oot.Field).Elem().FieldByName(oot.Prop.Name) 68 break 69 } 70 } 71 } 72 if !fieldVal.IsValid() { 73 return 74 } 75 } 76 if field.Options != nil { 77 ex, err := proto.GetExtension(field.Options, common.E_Sensitivejson) 78 if err != proto.ErrMissingExtension && err == nil && *ex.(*bool) { 79 if fieldVal.Kind() == reflect.String { 80 redactJsonSensitiveField(fieldVal) 81 } else { 82 t := fieldVal.Type() 83 fieldVal.Set(reflect.Zero(t)) 84 } 85 continue 86 } 87 88 ex, err = proto.GetExtension(field.Options, common.E_Sensitive) 89 if err == proto.ErrMissingExtension { 90 continue 91 } 92 93 if err == nil && *ex.(*bool) { 94 if fieldVal.Kind() == reflect.String { 95 fieldVal.SetString(RedactedString) 96 } else { 97 t := fieldVal.Type() 98 fieldVal.Set(reflect.Zero(t)) 99 } 100 continue 101 } 102 } 103 if field.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE { 104 Redact(fieldVal.Interface(), reflect.ValueOf(fieldVal.Interface())) 105 } 106 } 107 } 108 109 func redactJsonSensitiveField(val reflect.Value) { 110 var jsonData map[string]interface{} 111 validJsonString := strings.ReplaceAll(val.String(), `\`, `"`) 112 if err := json.Unmarshal([]byte(validJsonString), &jsonData); err != nil { 113 return 114 } 115 for key := range jsonData { 116 // This can be extended to an array of sensitive keys if needed 117 if key == "private-key" { 118 jsonData[key] = RedactedString 119 } 120 } 121 redactedJson, err := json.Marshal(jsonData) 122 if err == nil { 123 val.SetString(string(redactedJson)) 124 } 125 }