github.com/Ingenico-ePayments/connect-sdk-go@v0.0.0-20240318153750-1f8cd329b9c9/logging/obfuscation/BodyObfuscator.go (about) 1 package obfuscation 2 3 import ( 4 "encoding/json" 5 "errors" 6 "strconv" 7 "strings" 8 ) 9 10 var errInvalidDataType = errors.New("invalid data type - should never happen") 11 12 // BodyObfuscator can be used to obfuscate properties in JSON bodies. 13 type BodyObfuscator struct { 14 rules ruleMap 15 } 16 17 func (o BodyObfuscator) obfuscateValue(value interface{}, rule Rule) (string, error) { 18 if strVal, ok := value.(string); ok { 19 return rule(strVal), nil 20 } 21 if intVal, ok := value.(int32); ok { 22 return rule(strconv.FormatInt(int64(intVal), 10)), nil 23 } 24 if intVal, ok := value.(int64); ok { 25 return rule(strconv.FormatInt(intVal, 10)), nil 26 } 27 if floatVal, ok := value.(float32); ok { 28 return rule(strconv.FormatFloat(float64(floatVal), 'f', -1, 32)), nil 29 } 30 if floatVal, ok := value.(float64); ok { 31 return rule(strconv.FormatFloat(floatVal, 'f', -1, 64)), nil 32 } 33 if boolVal, ok := value.(bool); ok { 34 return rule(strconv.FormatBool(boolVal)), nil 35 } 36 return "", errInvalidDataType 37 } 38 39 func (o BodyObfuscator) navigateJSON(content interface{}) error { 40 if content == nil { 41 return nil 42 } 43 44 if contentMap, ok := content.(map[string]interface{}); ok { 45 for name, obj := range contentMap { 46 _, isMap := obj.(map[string]interface{}) 47 if rule, ok := o.rules[name]; ok && !isMap { 48 obfuscatedValue, oErr := o.obfuscateValue(obj, rule) 49 if oErr != nil { 50 return oErr 51 } 52 53 contentMap[name] = obfuscatedValue 54 55 } else { 56 nErr := o.navigateJSON(obj) 57 if nErr != nil { 58 return nErr 59 } 60 } 61 } 62 } 63 64 if contentSlice, ok := content.([]interface{}); ok { 65 for _, obj := range contentSlice { 66 nErr := o.navigateJSON(obj) 67 if nErr != nil { 68 return nErr 69 } 70 } 71 } 72 73 return nil 74 } 75 76 // ObfuscateBody obfuscates the given body as necessary 77 func (o BodyObfuscator) ObfuscateBody(body string) (string, error) { 78 if strings.TrimSpace(body) == "" { 79 return body, nil 80 } 81 82 var parsedJSON interface{} 83 pErr := json.Unmarshal([]byte(body), &parsedJSON) 84 if _, ok := pErr.(*json.SyntaxError); ok { 85 return body, nil 86 } 87 if pErr != nil { 88 return body, pErr 89 } 90 91 nErr := o.navigateJSON(parsedJSON) 92 if nErr != nil { 93 return body, nErr 94 } 95 96 obfuscatedBody, mErr := json.MarshalIndent(parsedJSON, "", " ") 97 if mErr != nil { 98 return body, mErr 99 } 100 101 return string(obfuscatedBody), nil 102 } 103 104 // NewBodyObfuscator returns a body obfuscator. 105 // This will contain some pre-defined obfuscation rules, as well as any provided custom rules. 106 func NewBodyObfuscator(customRules map[string]Rule) BodyObfuscator { 107 rules := ruleMap{ 108 "cardNumber": KeepingEndCount(4), 109 "expiryDate": KeepingEndCount(2), 110 "cvv": All(), 111 "iban": KeepingEndCount(4), 112 "accountNumber": KeepingEndCount(4), 113 "reformattedAccountNumber": KeepingEndCount(4), 114 "bin": KeepingStartCount(6), 115 "value": All(), 116 "keyId": FixedLength(8), 117 "secretKey": FixedLength(8), 118 "publicKey": FixedLength(8), 119 "userAuthenticationToken": FixedLength(8), 120 "encryptedPayload": FixedLength(8), 121 "decryptedPayload": FixedLength(8), 122 "encryptedCustomerInput": FixedLength(8), 123 } 124 125 for name, rule := range customRules { 126 rules[name] = rule 127 } 128 129 return BodyObfuscator{rules} 130 } 131 132 var defaultBodyObfuscator = NewBodyObfuscator(ruleMap{}) 133 134 // DefaultBodyObfuscator returns a default body obfuscator. 135 // This will be equivalent to calling NewBodyObfuscator with an empty rule map. 136 func DefaultBodyObfuscator() BodyObfuscator { 137 return defaultBodyObfuscator 138 }