1package lager 2 3import ( 4 "encoding/json" 5 "regexp" 6) 7 8const awsAccessKeyIDPattern = `AKIA[A-Z0-9]{16}` 9const awsSecretAccessKeyPattern = `KEY["']?\s*(?::|=>|=)\s*["']?[A-Z0-9/\+=]{40}["']?` 10const cryptMD5Pattern = `\$1\$[A-Z0-9./]{1,16}\$[A-Z0-9./]{22}` 11const cryptSHA256Pattern = `\$5\$[A-Z0-9./]{1,16}\$[A-Z0-9./]{43}` 12const cryptSHA512Pattern = `\$6\$[A-Z0-9./]{1,16}\$[A-Z0-9./]{86}` 13const privateKeyHeaderPattern = `-----BEGIN(.*)PRIVATE KEY-----` 14 15type JSONRedacter struct { 16 keyMatchers []*regexp.Regexp 17 valueMatchers []*regexp.Regexp 18} 19 20func NewJSONRedacter(keyPatterns []string, valuePatterns []string) (*JSONRedacter, error) { 21 if keyPatterns == nil { 22 keyPatterns = []string{"[Pp]wd", "[Pp]ass"} 23 } 24 if valuePatterns == nil { 25 valuePatterns = []string{awsAccessKeyIDPattern, awsSecretAccessKeyPattern, cryptMD5Pattern, cryptSHA256Pattern, cryptSHA512Pattern, privateKeyHeaderPattern} 26 } 27 ret := &JSONRedacter{} 28 for _, v := range keyPatterns { 29 r, err := regexp.Compile(v) 30 if err != nil { 31 return nil, err 32 } 33 ret.keyMatchers = append(ret.keyMatchers, r) 34 } 35 for _, v := range valuePatterns { 36 r, err := regexp.Compile(v) 37 if err != nil { 38 return nil, err 39 } 40 ret.valueMatchers = append(ret.valueMatchers, r) 41 } 42 return ret, nil 43} 44 45func (r JSONRedacter) Redact(data []byte) []byte { 46 var jsonBlob interface{} 47 err := json.Unmarshal(data, &jsonBlob) 48 if err != nil { 49 return handleError(err) 50 } 51 r.redactValue(&jsonBlob) 52 53 data, err = json.Marshal(jsonBlob) 54 if err != nil { 55 return handleError(err) 56 } 57 58 return data 59} 60 61func (r JSONRedacter) redactValue(data *interface{}) interface{} { 62 if data == nil { 63 return data 64 } 65 66 if a, ok := (*data).([]interface{}); ok { 67 r.redactArray(&a) 68 } else if m, ok := (*data).(map[string]interface{}); ok { 69 r.redactObject(&m) 70 } else if s, ok := (*data).(string); ok { 71 for _, m := range r.valueMatchers { 72 if m.MatchString(s) { 73 (*data) = "*REDACTED*" 74 break 75 } 76 } 77 } 78 return (*data) 79} 80 81func (r JSONRedacter) redactArray(data *[]interface{}) { 82 for i, _ := range *data { 83 r.redactValue(&((*data)[i])) 84 } 85} 86 87func (r JSONRedacter) redactObject(data *map[string]interface{}) { 88 for k, v := range *data { 89 for _, m := range r.keyMatchers { 90 if m.MatchString(k) { 91 (*data)[k] = "*REDACTED*" 92 break 93 } 94 } 95 if (*data)[k] != "*REDACTED*" { 96 (*data)[k] = r.redactValue(&v) 97 } 98 } 99} 100 101func handleError(err error) []byte { 102 var content []byte 103 if _, ok := err.(*json.UnsupportedTypeError); ok { 104 data := map[string]interface{}{"lager serialisation error": err.Error()} 105 content, err = json.Marshal(data) 106 } 107 if err != nil { 108 panic(err) 109 } 110 return content 111} 112