68 lines
2.2 KiB
JavaScript
68 lines
2.2 KiB
JavaScript
|
|
// Pre-publish payload sanitization.
|
||
|
|
// Removes sensitive tokens, local paths, emails, and env references
|
||
|
|
// from capsule payloads before broadcasting to the hub.
|
||
|
|
|
||
|
|
// Patterns to redact (replaced with placeholder)
|
||
|
|
const REDACT_PATTERNS = [
|
||
|
|
// API keys & tokens (generic)
|
||
|
|
/Bearer\s+[A-Za-z0-9\-._~+\/]+=*/g,
|
||
|
|
/sk-[A-Za-z0-9]{20,}/g,
|
||
|
|
/token[=:]\s*["']?[A-Za-z0-9\-._~+\/]{16,}["']?/gi,
|
||
|
|
/api[_-]?key[=:]\s*["']?[A-Za-z0-9\-._~+\/]{16,}["']?/gi,
|
||
|
|
/secret[=:]\s*["']?[A-Za-z0-9\-._~+\/]{16,}["']?/gi,
|
||
|
|
/password[=:]\s*["']?[^\s"',;)}\]]{6,}["']?/gi,
|
||
|
|
// GitHub tokens (ghp_, gho_, ghu_, ghs_, github_pat_)
|
||
|
|
/ghp_[A-Za-z0-9]{36,}/g,
|
||
|
|
/gho_[A-Za-z0-9]{36,}/g,
|
||
|
|
/ghu_[A-Za-z0-9]{36,}/g,
|
||
|
|
/ghs_[A-Za-z0-9]{36,}/g,
|
||
|
|
/github_pat_[A-Za-z0-9_]{22,}/g,
|
||
|
|
// AWS access keys
|
||
|
|
/AKIA[0-9A-Z]{16}/g,
|
||
|
|
// OpenAI / Anthropic tokens
|
||
|
|
/sk-proj-[A-Za-z0-9\-_]{20,}/g,
|
||
|
|
/sk-ant-[A-Za-z0-9\-_]{20,}/g,
|
||
|
|
// npm tokens
|
||
|
|
/npm_[A-Za-z0-9]{36,}/g,
|
||
|
|
// Private keys
|
||
|
|
/-----BEGIN\s+(?:RSA\s+|EC\s+|DSA\s+|OPENSSH\s+)?PRIVATE\s+KEY-----[\s\S]*?-----END\s+(?:RSA\s+|EC\s+|DSA\s+|OPENSSH\s+)?PRIVATE\s+KEY-----/g,
|
||
|
|
// Basic auth in URLs (redact only credentials, keep :// and @)
|
||
|
|
/(?<=:\/\/)[^@\s]+:[^@\s]+(?=@)/g,
|
||
|
|
// Local filesystem paths
|
||
|
|
/\/home\/[^\s"',;)}\]]+/g,
|
||
|
|
/\/Users\/[^\s"',;)}\]]+/g,
|
||
|
|
/[A-Z]:\\[^\s"',;)}\]]+/g,
|
||
|
|
// Email addresses
|
||
|
|
/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
||
|
|
// .env file references
|
||
|
|
/\.env(?:\.[a-zA-Z]+)?/g,
|
||
|
|
];
|
||
|
|
|
||
|
|
const REDACTED = '[REDACTED]';
|
||
|
|
|
||
|
|
function redactString(str) {
|
||
|
|
if (typeof str !== 'string') return str;
|
||
|
|
let result = str;
|
||
|
|
for (const pattern of REDACT_PATTERNS) {
|
||
|
|
// Reset lastIndex for global regexes
|
||
|
|
pattern.lastIndex = 0;
|
||
|
|
result = result.replace(pattern, REDACTED);
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Deep-clone and sanitize a capsule payload.
|
||
|
|
* Returns a new object with sensitive values redacted.
|
||
|
|
* Does NOT modify the original.
|
||
|
|
*/
|
||
|
|
function sanitizePayload(capsule) {
|
||
|
|
if (!capsule || typeof capsule !== 'object') return capsule;
|
||
|
|
return JSON.parse(JSON.stringify(capsule), (_key, value) => {
|
||
|
|
if (typeof value === 'string') return redactString(value);
|
||
|
|
return value;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = { sanitizePayload, redactString };
|