cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

75 lines
2.6 KiB
TypeScript

const REDACTED = "[Redacted]";
const REDACT_KEYS = new Set([
"email",
"prompt",
"message",
"messages",
"body",
"query",
"rawheaders",
"headers",
"passwd",
"api_key",
]);
const SECRET_KEY = /password|secret|token|authorization|cookie|credential|apikey/;
function redactValue(value: unknown, seen = new WeakSet<object>()): unknown {
if (value === null || value === undefined) return value;
if (typeof value === "string") return redactSensitiveText(value);
if (typeof value !== "object") return value;
if (seen.has(value)) return "[Circular]";
seen.add(value);
if (Array.isArray(value)) {
return value.map((item) => redactValue(item, seen));
}
if (value instanceof Error) {
const output: Record<string, unknown> = {
name: value.name || "Error",
message: redactSensitiveText(value.message),
};
if (typeof value.stack === "string" && value.stack.length > 0) {
output.stack = redactSensitiveText(value.stack);
}
if (value.cause !== undefined) {
output.cause =
typeof value.cause === "string"
? redactSensitiveText(value.cause)
: redactValue(value.cause, seen);
}
return output;
}
if (value instanceof Date) return value;
const output: Record<string, unknown> = {};
for (const [key, nested] of Object.entries(value)) {
output[key] = shouldRedactKey(key) ? REDACTED : redactValue(nested, seen);
}
return output;
}
export function redactBindings(bindings: Record<string, unknown>): Record<string, unknown> {
return redactValue(bindings) as Record<string, unknown>;
}
const EMAIL = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
const BEARER = /\bBearer\s+[^\s"',;&]+/gi;
const SECRET_ASSIGNMENT =
/\b([A-Za-z0-9_]*(?:password|secret|token|authorization|apikey|api_key)[A-Za-z0-9_]*)\s*[:=]\s*\S+/gi;
const JSON_SECRET_FIELD =
/"(password|passwd|secret|token|authorization|apikey|api_key|accesstoken|refreshtoken|email|cookie)"\s*:\s*"(?:\\.|[^"\\])*"/gi;
const BARE_SECRET =
/\b(?:sk-(?:or-v1-)?[A-Za-z0-9_-]{8,}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+|(?:ak_|ck_)[A-Za-z0-9]+)\b/g;
export function redactSensitiveText(text: string): string {
return text
.replace(EMAIL, REDACTED)
.replace(JSON_SECRET_FIELD, `"$1":"${REDACTED}"`)
.replace(BEARER, `Bearer ${REDACTED}`)
.replace(SECRET_ASSIGNMENT, (_match, key: string) => `${key}=${REDACTED}`)
.replace(BARE_SECRET, REDACTED);
}
function shouldRedactKey(key: string): boolean {
const normalized = key.toLowerCase().replace(/[^a-z0-9_]/g, "");
return REDACT_KEYS.has(normalized) || SECRET_KEY.test(normalized);
}