#!/usr/bin/env node /** * 共享日志脱敏模块 (v5.9) * * 统一所有组件的敏感信息过滤规则,避免规则分散导致不一致。 * 被 route-interceptor.js 和 activity-logger.js 共同引用。 */ /** * 日志脱敏: 移除疑似敏感信息 * @param {string} text - 原始文本 * @returns {string} 脱敏后文本 */ function sanitize(text) { if (!text || typeof text !== 'string') return text || ''; return text // 键值对形式: key=xxx, token: xxx, password=xxx 等 .replace(/(?:key|token|password|secret|credential|apikey|api_key|auth|passwd)[\s]*[=:]\s*\S{6,}/gi, (m) => { const sep = m.indexOf('=') !== -1 ? '=' : ':'; const prefix = m.slice(0, m.indexOf(sep) + 1); return prefix + ' [REDACTED]'; }) // 已知 token 前缀: sk-, ghp_, Bearer 等 (后跟 >=10 字符) .replace(/\b(?:sk-|ghp_|gho_|github_pat_|xoxb-|xoxp-|Bearer\s+)\S{10,}/g, '[REDACTED_TOKEN]') // AWS Access Key (AKIA 开头 20 字符) .replace(/\bAKIA[A-Z0-9]{16}\b/g, '[REDACTED_TOKEN]') // JWT token (eyJ 开头的 Base64url 段) .replace(/\beyJ[A-Za-z0-9_-]{20,}(?:\.[A-Za-z0-9_-]+)*/g, '[REDACTED_TOKEN]') // 疑似 Base64 编码密钥 (长度 >= 40) .replace(/\b[A-Za-z0-9+/]{64,}={0,2}\b/g, (m) => { // V07 修复: 阈值 40→64,减少 Git SHA/路径误杀 if (/^[A-Za-z0-9+/]+={0,2}$/.test(m)) return '[REDACTED_B64]'; return m; }) // UUID 格式 Token (当作为 TOKEN/KEY/SECRET 等的值出现时) .replace(/(?:TOKEN|KEY|SECRET|CREDENTIAL|PASSWORD|API_KEY)[\s]*[=:]\s*[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, (m) => { const sep = m.indexOf('=') !== -1 ? '=' : ':'; const prefix = m.slice(0, m.indexOf(sep) + 1); return prefix + ' [REDACTED_UUID]'; }); } if (typeof module !== 'undefined') { // #12: 磁盘满降级日志 — appendFileSync 失败时 fallback 到 stderr function safeAppendLog(filePath, jsonData) { try { const dir = require('path').dirname(filePath); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); fs.appendFileSync(filePath, JSON.stringify(jsonData) + '\n'); } catch (e) { // 磁盘满或权限错误时输出到 stderr (至少留下审计痕迹) try { process.stderr.write('[LOG-FALLBACK] ' + JSON.stringify(jsonData) + '\n'); } catch {} } } module.exports = { safeAppendLog, sanitize }; }