Skip to content

Commit 249460f

Browse files
committed
feat: add message sanitization to BunGateLogger for sensitive data
1 parent 02bdeb2 commit 249460f

File tree

2 files changed

+84
-8
lines changed

2 files changed

+84
-8
lines changed

src/logger/pino-logger.ts

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,47 @@ export class BunGateLogger implements Logger {
189189
return sanitized
190190
}
191191

192+
/**
193+
* Sanitizes message strings that might contain sensitive information
194+
* Looks for common patterns of exposed secrets in log messages
195+
*/
196+
private sanitizeMessage(message: string | undefined): string | undefined {
197+
if (!message || typeof message !== 'string') {
198+
return message
199+
}
200+
201+
// Pattern to match common API key/token formats in strings
202+
// This catches patterns like: "apiKey: abc123", "token=xyz", "Bearer token123", etc.
203+
const sensitivePatterns = [
204+
// API keys with various formats
205+
/\b(api[_-]?key|apikey)[\s:=]+[^\s,}\]]+/gi,
206+
// Bearer tokens
207+
/\bBearer\s+[^\s,}\]]+/gi,
208+
// Token assignments
209+
/\b(token|jwt|access[_-]?token|refresh[_-]?token)[\s:=]+[^\s,}\]]+/gi,
210+
// Password assignments
211+
/\b(password|passwd|pwd)[\s:=]+[^\s,}\]]+/gi,
212+
// Secret assignments
213+
/\b(secret|private[_-]?key)[\s:=]+[^\s,}\]]+/gi,
214+
// Generic key-value patterns with sensitive keys
215+
/["']?(apiKey|api_key|token|password|secret)["']?\s*[:=]\s*["']?[^"',}\]\s]+/gi,
216+
]
217+
218+
let sanitized = message
219+
for (const pattern of sensitivePatterns) {
220+
sanitized = sanitized.replace(pattern, (match) => {
221+
// Keep the key name but redact the value
222+
const colonIndex = match.search(/[:=]/)
223+
if (colonIndex !== -1) {
224+
return match.substring(0, colonIndex + 1) + ' [REDACTED]'
225+
}
226+
return '[REDACTED]'
227+
})
228+
}
229+
230+
return sanitized
231+
}
232+
192233
getSerializers(): LoggerOptions['serializers'] | undefined {
193234
return this.config.serializers
194235
}
@@ -201,10 +242,12 @@ export class BunGateLogger implements Logger {
201242
): void {
202243
if (typeof msgOrObj === 'string') {
203244
const sanitizedData = this.sanitizeData(dataOrMsg || {})
204-
this.pino.info(sanitizedData, msgOrObj)
245+
const sanitizedMsg = this.sanitizeMessage(msgOrObj)
246+
this.pino.info(sanitizedData, sanitizedMsg)
205247
} else {
206248
const sanitizedObj = this.sanitizeData(msgOrObj)
207-
this.pino.info(sanitizedObj, dataOrMsg as string)
249+
const sanitizedMsg = this.sanitizeMessage(dataOrMsg as string)
250+
this.pino.info(sanitizedObj, sanitizedMsg)
208251
}
209252
}
210253

@@ -216,10 +259,12 @@ export class BunGateLogger implements Logger {
216259
): void {
217260
if (typeof msgOrObj === 'string') {
218261
const sanitizedData = this.sanitizeData(dataOrMsg || {})
219-
this.pino.debug(sanitizedData, msgOrObj)
262+
const sanitizedMsg = this.sanitizeMessage(msgOrObj)
263+
this.pino.debug(sanitizedData, sanitizedMsg)
220264
} else {
221265
const sanitizedObj = this.sanitizeData(msgOrObj)
222-
this.pino.debug(sanitizedObj, dataOrMsg as string)
266+
const sanitizedMsg = this.sanitizeMessage(dataOrMsg as string)
267+
this.pino.debug(sanitizedObj, sanitizedMsg)
223268
}
224269
}
225270

@@ -231,10 +276,12 @@ export class BunGateLogger implements Logger {
231276
): void {
232277
if (typeof msgOrObj === 'string') {
233278
const sanitizedData = this.sanitizeData(dataOrMsg || {})
234-
this.pino.warn(sanitizedData, msgOrObj)
279+
const sanitizedMsg = this.sanitizeMessage(msgOrObj)
280+
this.pino.warn(sanitizedData, sanitizedMsg)
235281
} else {
236282
const sanitizedObj = this.sanitizeData(msgOrObj)
237-
this.pino.warn(sanitizedObj, dataOrMsg as string)
283+
const sanitizedMsg = this.sanitizeMessage(dataOrMsg as string)
284+
this.pino.warn(sanitizedObj, sanitizedMsg)
238285
}
239286
}
240287

@@ -259,10 +306,12 @@ export class BunGateLogger implements Logger {
259306
: {}),
260307
}
261308
const sanitizedData = this.sanitizeData(errorData)
262-
this.pino.error(sanitizedData, msgOrObj)
309+
const sanitizedMsg = this.sanitizeMessage(msgOrObj)
310+
this.pino.error(sanitizedData, sanitizedMsg)
263311
} else {
264312
const sanitizedObj = this.sanitizeData(msgOrObj)
265-
this.pino.error(sanitizedObj, errorOrMsg as string)
313+
const sanitizedMsg = this.sanitizeMessage(errorOrMsg as string)
314+
this.pino.error(sanitizedObj, sanitizedMsg)
266315
}
267316
}
268317

test/logger/pino-logger.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,31 @@ describe('BunGateLogger', () => {
143143
})
144144
}).not.toThrow()
145145
})
146+
147+
test('should sanitize sensitive data in message strings', () => {
148+
const logger = new BunGateLogger({
149+
level: 'info',
150+
})
151+
152+
// Test that sensitive patterns in messages are sanitized
153+
expect(() => {
154+
logger.info('User logged in with apiKey: abc123xyz')
155+
logger.info('Authentication failed for token=secret-token-456')
156+
logger.warn('Password reset requested: password: newPass123')
157+
logger.debug('API request with Bearer eyJhbGciOiJIUzI1NiIs...')
158+
logger.error('Failed to authenticate with secret: my-secret-key')
159+
}).not.toThrow()
160+
})
161+
162+
test('should sanitize sensitive data in object-based log calls with messages', () => {
163+
const logger = new BunGateLogger({
164+
level: 'debug',
165+
})
166+
167+
// Test object form with message that might contain sensitive data
168+
expect(() => {
169+
logger.info({ userId: 123 }, 'User authenticated with apiKey: xyz789')
170+
logger.warn({ action: 'login' }, 'Token validation failed: token=abc123')
171+
}).not.toThrow()
172+
})
146173
})

0 commit comments

Comments
 (0)