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  }