Files
multica/packages/views/common/task-transcript/redact.ts
YOMXXX c599f47ba4 fix(redact): cover GitHub fine-grained PATs and Google API keys (#4678)
The primary server redactor now covers github_pat_ and standard AIza keys, but the client transcript safety net does not. Add matching client patterns and regression coverage so previously stored or otherwise unredacted transcript values are masked at display time.

Also make the fixed-length Google key rule redact a key ending in '-' while preserving its trailing delimiter. Keep the 39-character format exact to avoid broadening false positives.

Co-authored-by: multica-agent <github@multica.ai>
2026-07-15 16:06:59 +08:00

42 lines
2.2 KiB
TypeScript

/**
* Client-side fallback for redacting sensitive information in agent output.
* The server performs primary redaction; this is a safety net for display.
*/
const patterns: { re: RegExp; replacement: string }[] = [
// AWS access key IDs
{ re: /\bAKIA[0-9A-Z]{16}\b/g, replacement: "[REDACTED AWS KEY]" },
// AWS secret access keys
{ re: /(?:aws_secret_access_key|secret_?access_?key)\s*[=:]\s*[A-Za-z0-9/+=]{40}/gi, replacement: "[REDACTED AWS SECRET]" },
// PEM private keys
{ re: /-----BEGIN[A-Z\s]*PRIVATE KEY-----[\s\S]*?-----END[A-Z\s]*PRIVATE KEY-----/g, replacement: "[REDACTED PRIVATE KEY]" },
// GitHub OAuth / classic tokens (ghp_/gho_/ghu_/ghs_/ghr_)
{ re: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,255}\b/g, replacement: "[REDACTED GITHUB TOKEN]" },
// GitHub fine-grained PATs (github_pat_…, recommended since 2022)
{ re: /\bgithub_pat_[A-Za-z0-9_]{20,255}\b/g, replacement: "[REDACTED GITHUB TOKEN]" },
// Google API keys (AIza…, e.g. Gemini / Maps / Firebase)
{ re: /\bAIza[0-9A-Za-z_-]{35}([^0-9A-Za-z_-]|$)/g, replacement: "[REDACTED GOOGLE API KEY]$1" },
// GitLab personal access tokens
{ re: /\bglpat-[A-Za-z0-9_-]{20,}\b/g, replacement: "[REDACTED GITLAB TOKEN]" },
// OpenAI / Anthropic API keys
{ re: /\bsk-[A-Za-z0-9_-]{20,}\b/g, replacement: "[REDACTED API KEY]" },
// Slack tokens
{ re: /\bxox[bporas]-[A-Za-z0-9-]{10,}\b/g, replacement: "[REDACTED SLACK TOKEN]" },
// JWT tokens
{ re: /\bey[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g, replacement: "[REDACTED JWT]" },
// Bearer tokens
{ re: /\bBearer\s+[A-Za-z0-9\-._~+/]+=*/gi, replacement: "Bearer [REDACTED]" },
// Connection strings with embedded passwords
{ re: /(?:postgres|mysql|mongodb|redis|amqp)(?:ql)?:\/\/[^:\s]+:[^@\s]+@/gi, replacement: "[REDACTED CONNECTION STRING]@" },
// Generic key=value secret env vars
{ re: /(?:API_KEY|API_SECRET|SECRET_KEY|SECRET|ACCESS_TOKEN|AUTH_TOKEN|PRIVATE_KEY|DATABASE_URL|DB_PASSWORD|DB_URL|REDIS_URL|PASSWORD|TOKEN)\s*[=:]\s*\S+/gi, replacement: "[REDACTED CREDENTIAL]" },
];
export function redactSecrets(text: string): string {
let result = text;
for (const { re, replacement } of patterns) {
result = result.replace(re, replacement);
}
return result;
}