@carvis_ai/sanitize-log
v0.1.0
Published
Production-grade log sanitization with two-layer secret redaction. Detect and mask tokens, API keys, JWTs, and sensitive fields before they hit your logs.
Maintainers
Readme
@carvis_ai/sanitize-log
Production-grade log sanitization with two-layer secret redaction.
Prevents tokens, API keys, JWTs, passwords, and other sensitive data from leaking into your logs. Works with any logging backend (pino, winston, console).
Why This Exists
Most sanitization libraries either redact everything (killing debuggability) or miss embedded secrets in string values. This library does both:
- Key-based redaction — fields named
password,token,apiKey, etc. are fully redacted - Value-based detection — strings are scanned for Bearer tokens, JWTs, API key patterns (
sk-,pk-), and basic auth in URLs
URL query parameters are selectively redacted: ?token=secret&page=2 becomes ?token=[REDACTED]&page=2. You keep your debugging params.
Install
npm install @carvis_ai/sanitize-logQuick Start
import { sanitizeForLogging } from "@carvis_ai/sanitize-log";
const data = {
user: "john",
password: "secret123",
headers: { Authorization: "Bearer eyJhbG..." },
url: "https://api.com?token=abc&page=1",
};
console.log(sanitizeForLogging(data));
// {
// user: "john",
// password: "[REDACTED]",
// headers: { Authorization: "[REDACTED]" },
// url: "https://api.com?token=[REDACTED]&page=1"
// }API
sanitizeForLogging(obj, maxDepth?)
Deep-sanitize an object. Handles circular references, arrays, nested objects.
sanitizeForLogging({ password: "secret" });
// => { password: "[REDACTED]" }
sanitizeForLogging("Bearer eyJhbGci...");
// => "Bearer [REDACTED]"sanitizeStringValue(value)
Scan a string for embedded secrets and mask them.
sanitizeStringValue("sk-1234567890abcdef");
// => "sk-[REDACTED]"
sanitizeStringValue("https://user:[email protected]");
// => "https://user:[REDACTED]@host.com"createSanitizer(options?)
Create a customized sanitizer instance.
import { createSanitizer } from "@carvis_ai/sanitize-log";
const { sanitizeForLogging } = createSanitizer({
extraFields: ["x-custom-secret", "internalToken"],
extraPatterns: [
{
pattern: /MYAPP-[A-Z0-9]{20,}/g,
replace: () => "MYAPP-[REDACTED]",
},
],
maxDepth: 5,
});| Option | Type | Description |
|--------|------|-------------|
| extraFields | string[] | Additional field names to redact (merged with defaults) |
| extraPatterns | ValuePattern[] | Additional regex patterns for string value detection |
| maxDepth | number | Max recursion depth (default: 10) |
Logger Utilities
Minimal pino-compatible logger interface with automatic sanitization.
import { consoleLogger, createPrefixedLogger } from "@carvis_ai/sanitize-log";
// Auto-sanitizing console logger
consoleLogger.error({ err: new Error("fail"), token: "secret" }, "Request failed");
// console: "Request failed" { err: { name: "Error", message: "fail", stack: "..." }, token: "[REDACTED]" }
// Prefixed logger (cached, no GC pressure)
const log = createPrefixedLogger("[auth]");
log.info({ userId: "123" }, "Login successful");
// console: "[auth] Login successful" { userId: "123" }
// Wrap any pino/winston logger
const log = createPrefixedLogger("[api]", pinoInstance);tryCatch(promise)
Type-safe async error handling with discriminated unions.
import { tryCatch } from "@carvis_ai/sanitize-log";
const { data, error } = await tryCatch(fetchUser(id));
if (error) {
log.error({ err: error }, "Failed to fetch user");
return;
}
// data is typed correctly, error is null
console.log(data.name);What Gets Redacted
Key-based (full redaction)
Any field whose name contains: password, token, secret, apikey, authorization, creditcard, cvv, ssn, pin, sessionid, cookie, bearer, private_key, client_secret, id_token, code_verifier, code_challenge.
Detection works across naming conventions:
accessToken(camelCase)access_token(snake_case)x-auth-token(hyphenated)
Value-based (pattern detection)
| Pattern | Example | Result |
|---------|---------|--------|
| Bearer tokens | Bearer eyJhbG... | Bearer [REDACTED] |
| API keys | sk-1234567890abcdef | sk-[REDACTED] |
| JWTs | eyJhbG.eyJzd.dozjg | [JWT-REDACTED] |
| Basic auth URLs | ://user:pass@host | ://user:[REDACTED]@host |
| Sensitive URL params | ?token=abc&page=1 | ?token=[REDACTED]&page=1 |
Used at Carvis
This library is extracted from Carvis, where it runs in production across our backend API, Chrome extension, and AI agent orchestration layer.
The problem: Our platform integrates with dozens of third-party APIs — shop management systems, parts distributors, vehicle data providers. Every integration exchanges OAuth tokens, API keys, and customer data. Before this library, secrets leaked into logs through:
- Error messages containing request URLs with tokens in query strings
- Stringified API responses with embedded JWTs
- OAuth callback parameters logged during debugging
- Nested objects from third-party SDKs with credentials in unexpected fields
How we use it: Every logger instance is wrapped with sanitizeForLogging. All log output — including error-level logs forwarded to Sentry — is auto-sanitized. Engineers don't think about it; the default is safe.
We run ~50 prefixed logger modules, each processing requests carrying OAuth tokens and customer data. The FIFO cache and WeakMap caching were added after profiling showed naive sanitization added measurable overhead at our log volume.
We use createSanitizer to extend the defaults with domain-specific fields and patterns for partner APIs that use non-standard key formats — without polluting the library defaults.
If you're building integrations that touch customer data — especially in automotive, healthcare, fintech, or any multi-vendor SaaS — this is the same protection we rely on daily.
Design Decisions
- FIFO cache for field name parsing — bounded at 1000 entries, no LRU complexity. Field names are a fixed set in practice; cache misses are cheap.
- WeakMap cache for prefixed loggers — GC'd when the base logger is collected. No memory leaks from temporary logger instances.
- Selective URL param redaction — non-sensitive params (
page,limit,shopId) are preserved for debugging. Most libraries redact everything. - Error serialization —
Error.messageandError.stackare non-enumerable. The logger serializes them before sanitization so they appear in logs.
License
MIT
