Files
droid2api/log-sanitizer.js
2025-12-27 15:07:28 +08:00

88 lines
2.5 KiB
JavaScript

const REDACTION = '[REDACTED]';
const REDACT_KEY_RE = /(authorization|x-api-key|api[-_]?key|access_token|refresh_token|client_secret|private_key|set-cookie|cookie|password|secret)/i;
const EMAIL_KEY_RE = /email/i;
const IP_KEY_RE = /(^ip$|ip_address|remote_address|x-forwarded-for)/i;
function maskEmail(value) {
if (typeof value !== 'string') return value;
return value.replace(/([A-Za-z0-9._%+-])([A-Za-z0-9._%+-]*)(@[A-Za-z0-9.-]+\.[A-Za-z]{2,})/g, '$1***$3');
}
function maskIp(value) {
if (typeof value !== 'string') return value;
let masked = value.replace(/\b(\d{1,3}\.\d{1,3}\.\d{1,3})\.\d{1,3}\b/g, '$1.xxx');
masked = masked.replace(/\b([A-Fa-f0-9]{0,4}:){2,7}[A-Fa-f0-9]{0,4}\b/g, '****');
return masked;
}
function maskTokensInString(value) {
if (typeof value !== 'string') return value;
let masked = value.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+\b/g, 'Bearer ' + REDACTION);
masked = masked.replace(/\b(api_key|apikey|access_token|refresh_token|client_secret|password)=([^\s&]+)/gi, '$1=' + REDACTION);
return masked;
}
function sanitizeString(value) {
if (typeof value !== 'string') return value;
let masked = value;
masked = maskTokensInString(masked);
masked = maskEmail(masked);
masked = maskIp(masked);
return masked;
}
function sanitizeValue(value, key, seen) {
if (value === null || value === undefined) return value;
if (key && REDACT_KEY_RE.test(key)) {
return REDACTION;
}
if (typeof value === 'string') {
if (key && EMAIL_KEY_RE.test(key)) {
return maskEmail(value);
}
if (key && IP_KEY_RE.test(key)) {
return maskIp(value);
}
return sanitizeString(value);
}
if (typeof value === 'number' || typeof value === 'boolean') {
return value;
}
if (Array.isArray(value)) {
return value.map(item => sanitizeValue(item, key, seen));
}
if (typeof value === 'object') {
return sanitizeObject(value, seen);
}
return value;
}
function sanitizeObject(value, seen) {
if (!value || typeof value !== 'object') return value;
if (!seen) seen = new WeakSet();
if (seen.has(value)) return '[Circular]';
seen.add(value);
const output = Array.isArray(value) ? [] : {};
for (const [key, val] of Object.entries(value)) {
output[key] = sanitizeValue(val, key, seen);
}
return output;
}
export function sanitizeForLog(value) {
return sanitizeValue(value, null, new WeakSet());
}
export function sanitizeLogMessage(message) {
if (typeof message !== 'string') return message;
return sanitizeString(message);
}