Approval-based snapshot testing library for Go (mirror)
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 213 lines 5.8 kB view raw
1package shutter 2 3import ( 4 "regexp" 5 "strings" 6) 7 8// regexScrubber replaces all matches of a regex pattern with a replacement string. 9type regexScrubber struct { 10 pattern *regexp.Regexp 11 replacement string 12} 13 14func (r *regexScrubber) isOption() {} 15 16func (r *regexScrubber) Scrub(content string) string { 17 return r.pattern.ReplaceAllString(content, r.replacement) 18} 19 20// ScrubRegex creates a scrubber that replaces all matches of the given 21// regex pattern with the replacement string. 22// 23// Example: 24// 25// shutter.ScrubRegex(`user-\d+`, "<USER_ID>") 26func ScrubRegex(pattern string, replacement string) Scrubber { 27 re := regexp.MustCompile(pattern) 28 return &regexScrubber{ 29 pattern: re, 30 replacement: replacement, 31 } 32} 33 34// exactMatchScrubber replaces exact string matches with a replacement. 35type exactMatchScrubber struct { 36 match string 37 replacement string 38} 39 40func (e *exactMatchScrubber) isOption() {} 41 42func (e *exactMatchScrubber) Scrub(content string) string { 43 return strings.ReplaceAll(content, e.match, e.replacement) 44} 45 46// ScrubExact creates a scrubber that replaces exact string matches. 47// 48// Example: 49// 50// shutter.ScrubExact("secret_value", "<REDACTED>") 51func ScrubExact(match string, replacement string) Scrubber { 52 return &exactMatchScrubber{ 53 match: match, 54 replacement: replacement, 55 } 56} 57 58// Common regex patterns for scrubbing 59var ( 60 uuidPattern = regexp.MustCompile(`[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`) 61 iso8601Pattern = regexp.MustCompile(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?`) 62 emailPattern = regexp.MustCompile(`[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}`) 63 // Unix timestamp pattern - matches 10-13 digit numbers (Unix timestamps in seconds or milliseconds) 64 // Note: This is aggressive and may match other numbers. Use with caution or customize. 65 unixTsPattern = regexp.MustCompile(`\b\d{10,13}\b`) 66 // IPv4 pattern with basic range validation 67 ipv4Pattern = regexp.MustCompile(`\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b`) 68 // Credit card pattern - matches 16 digit numbers with optional separators 69 creditCardPattern = regexp.MustCompile(`\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b`) 70 jwtPattern = regexp.MustCompile(`eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*`) 71 // Date patterns 72 datePattern = regexp.MustCompile(`\b\d{4}[-/]\d{2}[-/]\d{2}\b|\b\d{2}[-/]\d{2}[-/]\d{4}\b`) 73 // API key pattern - matches patterns like: sk_live_..., pk_test_..., api_key_... 74 apiKeyPattern = regexp.MustCompile(`\b(sk|pk|api[_-]?key)[_-](live|test|prod|dev)[_-][a-zA-Z0-9]+\b`) 75) 76 77// ScrubUUID replaces all UUIDs with "<UUID>". 78// 79// Example: 80// 81// shutter.Snap(t, "user", user, shutter.ScrubUUID()) 82func ScrubUUID() Scrubber { 83 return &regexScrubber{ 84 pattern: uuidPattern, 85 replacement: "<UUID>", 86 } 87} 88 89// ScrubTimestamp replaces ISO8601 timestamps with "<TIMESTAMP>". 90// 91// Example: 92// 93// shutter.Snap(t, "event", event, shutter.ScrubTimestamp()) 94func ScrubTimestamp() Scrubber { 95 return &regexScrubber{ 96 pattern: iso8601Pattern, 97 replacement: "<TIMESTAMP>", 98 } 99} 100 101// ScrubEmail replaces email addresses with "<EMAIL>". 102// 103// Example: 104// 105// shutter.Snap(t, "user", user, shutter.ScrubEmail()) 106func ScrubEmail() Scrubber { 107 return &regexScrubber{ 108 pattern: emailPattern, 109 replacement: "<EMAIL>", 110 } 111} 112 113// ScrubUnixTimestamp replaces Unix timestamps (10-13 digits) with "<UNIX_TS>". 114// Note: This is aggressive and may match other long numbers. For more conservative 115// scrubbing with context keywords, use ScrubRegex with a custom pattern. 116// 117// Example: 118// 119// shutter.Snap(t, "data", data, shutter.ScrubUnixTimestamp()) 120func ScrubUnixTimestamp() Scrubber { 121 return &regexScrubber{ 122 pattern: unixTsPattern, 123 replacement: "<UNIX_TS>", 124 } 125} 126 127// ScrubIP replaces IPv4 addresses with "<IP>". 128// 129// Example: 130// 131// shutter.Snap(t, "request", request, shutter.ScrubIP()) 132func ScrubIP() Scrubber { 133 return &regexScrubber{ 134 pattern: ipv4Pattern, 135 replacement: "<IP>", 136 } 137} 138 139// ScrubCreditCard replaces credit card numbers with "<CREDIT_CARD>". 140// 141// Example: 142// 143// shutter.Snap(t, "payment", payment, shutter.ScrubCreditCard()) 144func ScrubCreditCard() Scrubber { 145 return &regexScrubber{ 146 pattern: creditCardPattern, 147 replacement: "<CREDIT_CARD>", 148 } 149} 150 151// ScrubJWT replaces JWT tokens with "<JWT>". 152// 153// Example: 154// 155// shutter.Snap(t, "auth", authData, shutter.ScrubJWT()) 156func ScrubJWT() Scrubber { 157 return &regexScrubber{ 158 pattern: jwtPattern, 159 replacement: "<JWT>", 160 } 161} 162 163// ScrubDate replaces various date formats with "<DATE>". 164// 165// Example: 166// 167// shutter.Snap(t, "data", data, shutter.ScrubDate()) 168func ScrubDate() Scrubber { 169 return &regexScrubber{ 170 pattern: datePattern, 171 replacement: "<DATE>", 172 } 173} 174 175// ScrubAPIKey replaces common API key patterns with "<API_KEY>". 176// Matches patterns like: sk_live_..., pk_test_..., api_key_... 177// 178// Example: 179// 180// shutter.Snap(t, "config", config, shutter.ScrubAPIKey()) 181func ScrubAPIKey() Scrubber { 182 return &regexScrubber{ 183 pattern: apiKeyPattern, 184 replacement: "<API_KEY>", 185 } 186} 187 188// customScrubber allows users to provide a custom scrubbing function. 189type customScrubber struct { 190 scrubFunc func(string) string 191} 192 193func (c *customScrubber) isOption() {} 194 195func (c *customScrubber) Scrub(content string) string { 196 return c.scrubFunc(content) 197} 198 199// ScrubWith creates a scrubber using a custom function. 200// The function receives the snapshot content and should return the scrubbed content. 201// 202// Example: 203// 204// shutter.Snap(t, "data", data, 205// shutter.ScrubWith(func(content string) string { 206// return strings.ReplaceAll(content, "localhost", "<HOST>") 207// }), 208// ) 209func ScrubWith(scrubFunc func(string) string) Scrubber { 210 return &customScrubber{ 211 scrubFunc: scrubFunc, 212 } 213}