/** * L-05: reject agent content that looks like a leaked credential before it * is stored or published. `scanForSecrets` returns matched PATTERN NAMES * only — never the matched values (those must not be echoed anywhere). */ const PATTERNS: readonly (readonly [string, RegExp])[] = [ ['aws_access_key', /AKIA[0-9A-Z]{16}/], ['pem_private_key', /-----BEGIN [A-Z ]*PRIVATE KEY-----/], ['openai_api_key', /sk-[A-Za-z0-9]{20,}/], ['github_token', /ghp_[A-Za-z0-9]{36}/], ['generic_credential', /(?:password|passwd|api[_-]?key|secret)\s*[:=]\s*\S+/i], ] as const export function scanForSecrets(text: string): string[] { const names: string[] = [] for (const [name, re] of PATTERNS) { if (re.test(text)) names.push(name) } return names } /** Scan the strings an agent can set; returns pattern names across all targets. */ export function scanAgentPayload(targets: (string | undefined | null)[]): string[] { const names = new Set() for (const target of targets) { if (typeof target !== 'string') continue for (const name of scanForSecrets(target)) names.add(name) } return [...names] }