397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
39 lines
2.3 KiB
JavaScript
39 lines
2.3 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* secret-scan (Машина 5 Пакет 7, Блок 4) — общий скан секрет-паттернов в тексте.
|
||
*
|
||
* Один источник секрет-паттернов на ДВА канала (анти-дрейф, как classify-destructive для
|
||
* разрушительности): read-path-deny (7.1, скан выдачи Read) + mcp-classification (7.3, скан
|
||
* исходящего egress). Секрет-ПОДМНОЖЕСТВО (не PII телефон/email — то в observer-pii-filter):
|
||
* приватные ключи PEM, известные токены провайдеров, строки подключения со встроенными кредами.
|
||
*
|
||
* Чистая функция; паттерны без флага /g (для безопасного .test без lastIndex-состояния).
|
||
* Токен-regex согласованы с observer-pii-filter (тот редактирует, этот — детектит/блокирует).
|
||
*/
|
||
|
||
const SECRET_PATTERNS = [
|
||
['pem-private-key', /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----/],
|
||
['jwt', /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/],
|
||
['aws-key', /\bAKIA[A-Z0-9]{16}\b/],
|
||
['yc-static', /\bAQVN[A-Za-z0-9_-]{15,}\b/],
|
||
['yc-session', /\bt1\.[A-Za-z0-9_-]{40,}\b/],
|
||
['yc-oauth', /\by0_[A-Za-z0-9_-]{40,}\b/],
|
||
['sentry-token', /sntrys?_[A-Za-z0-9]{12,}/],
|
||
['openai-token', /\bsk-[A-Za-z0-9]{20,}/],
|
||
['github-token', /\bgh[pousr]_[A-Za-z0-9]{36,}\b/],
|
||
['slack-token', /\bxox[baprs]-[A-Za-z0-9-]{10,}/],
|
||
['bearer', /Bearer\s+[A-Za-z0-9._-]{20,}/],
|
||
// Строка подключения с КРЕДАМИ: scheme://user:pass@host. Без user:pass@ (только host:port) —
|
||
// не секрет (см. тест). Покрывает postgres/mysql/mongodb/redis/amqp.
|
||
['conn-string-creds', /\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|rediss?|amqp):\/\/[^\s:@/]+:[^\s:@/]+@/i],
|
||
];
|
||
|
||
/** Скан текста на секрет-паттерны. @returns {{found:boolean, hits:string[]}} (пустой/невалидный → not found). */
|
||
export function scanSecrets(text) {
|
||
const s = String(text == null ? '' : text);
|
||
const hits = SECRET_PATTERNS.filter(([, re]) => re.test(s)).map(([name]) => name);
|
||
return { found: hits.length > 0, hits };
|
||
}
|
||
|
||
export const _internals = { SECRET_PATTERNS };
|