claude-code-best/src/utils/sensitive.ts
James Feng bad81fdfcc Phase 1: security do-now — redaction helpers + RCS defaults hardened
- Add src/utils/sensitive.ts: redactUrl/redactValue/redactForLog
- Apply redaction to MCP config printing (mcp.tsx)
- Apply redaction to hard-fail logging (log.ts)
- Apply redaction to chrome native host debug log
- Apply redaction to CLI error output (exit.ts)
- RCS: default bind to 127.0.0.1 instead of 0.0.0.0
- RCS: CORS restricted to localhost + baseUrl whitelist
- pipeTransport: default bind to 127.0.0.1 via PIPE_HOST env
2026-06-03 13:43:38 +08:00

80 lines
2.2 KiB
TypeScript

const REDACTED = '[REDACTED]'
const SENSITIVE_KEY_PATTERN =
/(api[_-]?key|auth(?:orization)?|bearer|cookie|credential|password|secret|session|token)/i
const SENSITIVE_QUERY_PARAMS = new Set([
'access_token',
'api_key',
'apikey',
'auth',
'authorization',
'client_secret',
'code',
'credential',
'key',
'password',
'refresh_token',
'secret',
'session',
'token',
])
const TOKEN_PATTERNS: Array<[RegExp, string]> = [
[/\b(sk-ant-[A-Za-z0-9_-]{10,})\b/g, REDACTED],
[/\b(AKIA|ASIA)[A-Z0-9]{16}\b/g, REDACTED],
[/\b(ghp|gho|ghs|ghu|github_pat)_[A-Za-z0-9_]{10,}\b/g, REDACTED],
[/\bnpm_[A-Za-z0-9]{36}\b/g, REDACTED],
[/\bxox[baporst]-[A-Za-z0-9-]{10,}\b/g, REDACTED],
[/\bBearer\s+[A-Za-z0-9._~+/-]+=*\b/gi, `Bearer ${REDACTED}`],
[/\bBasic\s+[A-Za-z0-9+/]+=*\b/gi, `Basic ${REDACTED}`],
]
function isSensitiveKey(key: string): boolean {
return SENSITIVE_KEY_PATTERN.test(key)
}
export function redactUrl(urlString: string): string {
try {
const parsed = new URL(urlString)
if (parsed.username) parsed.username = REDACTED
if (parsed.password) parsed.password = REDACTED
for (const key of [...parsed.searchParams.keys()]) {
if (SENSITIVE_QUERY_PARAMS.has(key.toLowerCase())) {
parsed.searchParams.set(key, REDACTED)
}
}
return redactForLog(parsed.toString())
} catch {
return redactForLog(urlString)
}
}
export function redactValue(key: string, value: unknown): string
export function redactValue(value: unknown): string
export function redactValue(keyOrValue: unknown, value?: unknown): string {
if (arguments.length > 1) {
const key = String(keyOrValue)
return isSensitiveKey(key) ? REDACTED : redactForLog(String(value ?? ''))
}
return redactForLog(String(keyOrValue ?? ''))
}
export function redactForLog(message: unknown): string {
let redacted = String(message ?? '')
for (const [pattern, replacement] of TOKEN_PATTERNS) {
redacted = redacted.replace(pattern, replacement)
}
redacted = redacted.replace(
/\b([A-Za-z0-9_.-]*(?:api[_-]?key|auth|authorization|bearer|cookie|credential|password|secret|session|token)[A-Za-z0-9_.-]*)\s*[:=]\s*("[^"]+"|'[^']+'|[^\s,;]+)/gi,
(_match, key: string) => `${key}=${REDACTED}`,
)
return redacted
}