Skip to Content
Deneva MCPComponentsLog Scrubber

Log scrubber

Source: src/security/log-scrubber.ts

Defence-in-depth layer for the application logger.

What Pino already does

Pino’s built-in redact handles known paths:

redact: { paths: ['req.headers["x-api-key"]', 'req.headers["x-admin-token"]'] }

That covers the request-headers case completely.

What this scrubber adds

The harder case is arbitrary error objects that an adapter SDK might construct with token-shaped strings or access_token properties baked deep inside. Pino can’t redact a path it doesn’t know about. So we wire the scrubber as the err serializer in src/index.ts:

serializers: { err: (e) => scrubTokens({ type, message, stack }) }

scrubTokens walks the value (depth-limited at 6) and:

  • replaces Bearer <anything> with Bearer [REDACTED]
  • replaces ya29.<chars> (Google OAuth) with [REDACTED]
  • replaces EAA<chars> (Meta long-lived tokens) with [REDACTED]
  • replaces values of keys whose name matches /access_token|refresh_token|client_secret|authorization|api[_-]?key|x-admin-token/i

When the regex matters most

Adapter SDKs sometimes embed access tokens directly into error messages — a token-prefixed string then ends up in err.message. Without this layer the token leaves the process via journalctl. Phase 5’s grep evidence check (looking for ya29. in the journal) becomes a regression detector, not the primary defence.

Tests

tests/integration/log-scrubber.test.ts covers:

  • Bearer <token> redaction
  • ya29.*, EAA* standalone redaction
  • key-name redaction (access_token, refresh_token, etc.)
  • nested objects + arrays