|
| 1 | +const REDACTED = "[REDACTED]"; |
| 2 | + |
| 3 | +const SECRET_PREFIX_PATTERN = /\bsk-[A-Za-z0-9][A-Za-z0-9_-]*\b/g; |
| 4 | +const BEARER_TOKEN_PATTERN = /\b(Bearer\s+)([^\s,;]+)/gi; |
| 5 | +const SENSITIVE_QUERY_PARAM_PATTERN = |
| 6 | + /([?&](?:access[_-]?token|api[_-]?key|auth(?:orization)?|client[_-]?secret|password|refresh[_-]?token|secret|session[_-]?token|token)=)([^&#\s]+)/gi; |
| 7 | +const SENSITIVE_FIELD_PATTERN = |
| 8 | + /((?:"|')?(?:access[_-]?token|api[_-]?key|auth(?:orization)?|client[_-]?secret|password|refresh[_-]?token|secret|session[_-]?token|token)(?:"|')?\s*[:=]\s*)(["'`]?)([^"'`\s,}]+)(\2)/gi; |
| 9 | +const PROCESS_ENV_PATTERN = |
| 10 | + /\b((?:process\.)?env\.[A-Za-z_][A-Za-z0-9_]*\s*(?:=|:)\s*)(["'`]?)([^"'`\s,}]+)(\2)/g; |
| 11 | +const ENV_ASSIGNMENT_PATTERN = |
| 12 | + /\b([A-Z][A-Z0-9_]{1,63}\s*=\s*)(["'`]?)([^"'`\s]+)(\2)/g; |
| 13 | + |
| 14 | +function isPlainObject(value: unknown): value is Record<string, unknown> { |
| 15 | + if (typeof value !== "object" || value === null) { |
| 16 | + return false; |
| 17 | + } |
| 18 | + const prototype = Object.getPrototypeOf(value); |
| 19 | + return prototype === Object.prototype || prototype === null; |
| 20 | +} |
| 21 | + |
| 22 | +export function redactSensitiveText(text: string): string { |
| 23 | + return text |
| 24 | + .replace(SECRET_PREFIX_PATTERN, REDACTED) |
| 25 | + .replace(BEARER_TOKEN_PATTERN, `$1${REDACTED}`) |
| 26 | + .replace(SENSITIVE_QUERY_PARAM_PATTERN, `$1${REDACTED}`) |
| 27 | + .replace(SENSITIVE_FIELD_PATTERN, `$1$2${REDACTED}$4`) |
| 28 | + .replace(PROCESS_ENV_PATTERN, `$1$2${REDACTED}$4`) |
| 29 | + .replace(ENV_ASSIGNMENT_PATTERN, `$1$2${REDACTED}$4`); |
| 30 | +} |
| 31 | + |
| 32 | +export function redactSensitiveValue<T>(value: T): T { |
| 33 | + if (typeof value === "string") { |
| 34 | + return redactSensitiveText(value) as T; |
| 35 | + } |
| 36 | + |
| 37 | + if (Array.isArray(value)) { |
| 38 | + return value.map((item) => redactSensitiveValue(item)) as T; |
| 39 | + } |
| 40 | + |
| 41 | + if (isPlainObject(value)) { |
| 42 | + const entries = Object.entries(value).map(([key, entryValue]) => [ |
| 43 | + key, |
| 44 | + redactSensitiveValue(entryValue), |
| 45 | + ]); |
| 46 | + return Object.fromEntries(entries) as T; |
| 47 | + } |
| 48 | + |
| 49 | + return value; |
| 50 | +} |
0 commit comments