Initial import of verae-middleware from zapier monorepo
This commit is contained in:
commit
c6f7d93bbb
66 changed files with 6590 additions and 0 deletions
93
src/debug/redact.js
Normal file
93
src/debug/redact.js
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* @fileoverview Secret redaction for debug logs.
|
||||
* @module debug/redact
|
||||
*/
|
||||
|
||||
const SENSITIVE_KEYS = new Set([
|
||||
'password',
|
||||
'veraepassword',
|
||||
'token',
|
||||
'veraetoken',
|
||||
'accesstoken',
|
||||
'apikey',
|
||||
'api_key',
|
||||
'secret',
|
||||
'authorization',
|
||||
'x-api-key',
|
||||
'cookie',
|
||||
'sessionkey',
|
||||
]);
|
||||
|
||||
const BEARER_RE = /^Bearer\s+.+/i;
|
||||
const API_KEY_RE = /^zmw_[A-Za-z0-9_-]+/;
|
||||
const SESSION_RE = /^zmt_[A-Za-z0-9_.-]+/;
|
||||
const JWT_RE = /^eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/;
|
||||
|
||||
/**
|
||||
* Redact a single string value if it looks like a secret.
|
||||
*
|
||||
* @param {string} value - Raw string.
|
||||
* @returns {string} Original or `[REDACTED]`.
|
||||
*/
|
||||
export function redactString(value) {
|
||||
if (typeof value !== 'string') return value;
|
||||
if (
|
||||
BEARER_RE.test(value) ||
|
||||
API_KEY_RE.test(value) ||
|
||||
SESSION_RE.test(value) ||
|
||||
JWT_RE.test(value)
|
||||
) {
|
||||
return '[REDACTED]';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep-clone plain data structures while redacting sensitive keys and values.
|
||||
*
|
||||
* @param {unknown} input - Any JSON-like value.
|
||||
* @param {number} [depth=0] - Recursion depth guard.
|
||||
* @returns {unknown} Redacted structure safe for logging.
|
||||
*
|
||||
* @example
|
||||
* redact({ password: 'x', jobId: '1' }); // { password: '[REDACTED]', jobId: '1' }
|
||||
*/
|
||||
export function redact(input, depth = 0) {
|
||||
if (depth > 8) return '[MaxDepth]';
|
||||
|
||||
if (input === null || input === undefined) return input;
|
||||
|
||||
if (typeof input === 'string') return redactString(input);
|
||||
|
||||
if (typeof input === 'number' || typeof input === 'boolean') return input;
|
||||
|
||||
if (input instanceof Error) {
|
||||
return { name: input.name, message: redactString(input.message), stack: undefined };
|
||||
}
|
||||
|
||||
if (Array.isArray(input)) {
|
||||
return input.map((item) => redact(item, depth + 1));
|
||||
}
|
||||
|
||||
if (typeof input === 'object') {
|
||||
/** @type {Record<string, unknown>} */
|
||||
const out = {};
|
||||
for (const [key, value] of Object.entries(input)) {
|
||||
if (SENSITIVE_KEYS.has(key.toLowerCase())) {
|
||||
out[key] = '[REDACTED]';
|
||||
} else if (key.toLowerCase() === 'targeturl' && typeof value === 'string') {
|
||||
try {
|
||||
const u = new URL(value);
|
||||
out[key] = `${u.origin}${u.pathname}`;
|
||||
} catch {
|
||||
out[key] = '[REDACTED_URL]';
|
||||
}
|
||||
} else {
|
||||
out[key] = redact(value, depth + 1);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
return String(input);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue