Lines 55-136javascript
55 if (!sig.refine) return sig.re.test(text);
56 const g = new RegExp(sig.re.source, sig.re.flags.includes('g') ? sig.re.flags : sig.re.flags + 'g');
57 for (const m of text.matchAll(g)) {
58 if (m.index == null) continue;
59 if (sig.refine(lineTextAt(text, m.index))) return true;
64// ── dangerous shell ──
65export const DANGEROUS_SHELL = [
66 { name: 'Pipe-to-shell installer (curl … | sh)', re: /\b(curl|wget)\b[^\n|]{0,200}\|\s*(sudo\s+)?(ba|z|k)?sh\b/i, severity: 'CRITICAL' },
67 { name: 'PowerShell download-and-run (iwr/curl … | iex)', re: /\b(iwr|curl|wget|invoke-webrequest|invoke-restmethod|irm)\b[^\n|]{0,200}\|\s*(iex|invoke-expression)\b/i, severity: 'CRITICAL' },
68 { name: 'Invoke-Expression of downloaded content', re: /\b(iex|invoke-expression)\b[^\n]{0,120}(downloadstring|net\.webclient|\(\s*(iwr|irm|invoke-)|\$\()/i, severity: 'CRITICAL' },
69 { name: 'Reverse shell via /dev/tcp', re: /\/dev\/(tcp|udp)\//i, severity: 'CRITICAL' },
70 { name: 'Base64 blob piped to a shell', re: /base64\s+(--?d(ecode)?)?\b[^\n|]{0,200}\|\s*(ba|z)?sh\b/i, severity: 'CRITICAL' },
71 { name: 'curl/wget posts data to the network (exfiltration)', re: /\b(curl|wget|http|https|invoke-restmethod|irm)\b[^\n]{0,220}(--data(-raw|-binary|-urlencode)?|--form\b|--upload-file\b|(^|\s)-d\s|(^|\s)-F\s|(^|\s)-T\s|-Method
72 { name: 'Command output piped into a network call', re: /\b(curl|wget|invoke-restmethod|invoke-webrequest|irm|iwr)\b[^\n]{0,220}(\$\(|`[^`\n]+`|<\()/i, severity: 'HIGH' },
73 { name: 'Fetches from a raw IP address', re: /\b(curl|wget|iwr|irm|invoke-webrequest|invoke-restmethod)\b[^\n]{0,220}https?:\/\/\d{1,3}(\.\d{1,3}){3}/i, severity: 'HIGH' },
74 { name: 'Writes to shell profile / SSH keys / crontab', re: /(\.bashrc|\.zshrc|\.bash_profile|\.profile|authorized_keys|id_rsa\b|\bcrontab\b)/i, severity: 'HIGH' },
75 { name: 'Recursive force delete (rm -rf)', re: /\brm\s+-[a-z]*r[a-z]*f|\brm\s+-[a-z]*f[a-z]*r/i, severity: 'HIGH', refine: rmTargetsRealData },
76 // BARE `eval(`/`exec(` only — the lookbehind drops method calls that merely end
77 // in those letters (`db.exec(`, `RE.exec(`, `page.$eval(`, `$pdo->exec(`).
LowEval
Package source references a known benign dynamic code generation pattern.
guard-signals.mjsView on unpkg · L75 78 { name: 'Inline eval / exec of a string', re: /(?<![.\w$>:])(eval|exec)\s*[("`']/i, severity: 'HIGH' },
79 { name: 'Pipes an env dump to the network', re: /\b(env|printenv|set)\b[^\n|]{0,80}\|[^\n]{0,80}(curl|wget|nc\b|http)/i, severity: 'HIGH' },
80 { name: 'Disables TLS / cert verification', re: /(NODE_TLS_REJECT_UNAUTHORIZED\s*=\s*0|GIT_SSL_NO_VERIFY|--no-check-certificate|--insecure\b|verify\s*=\s*False)/i, severity: 'MEDIUM' },
81 { name: 'python -c one-liner', re: /python[0-9.]*\s+-c\b/i, severity: 'MEDIUM' },
82 { name: 'node -e one-liner', re: /\bnode\s+-e\b/i, severity: 'MEDIUM' },
83 { name: 'Netcat / socket exfil', re: /\bnc\s+-[a-z]*\b|\bncat\b/i, severity: 'MEDIUM' },
87// Broad phrases that dominate benign dev prose ("system prompt", "run the
88// following command", "do not tell the user", "never mention") are deliberately
89// excluded — their real-attack shapes are caught by scanShell and the tightened
90// regexes below. The injection loop also applies PRECEDING_NEGATION, so "never
91// ignore previous instructions" reads as legitimate.
92export const INJECTION_PHRASES = [
93 'ignore previous instructions', 'ignore all previous', 'disregard previous', 'disregard the above',
94 'without informing the user', 'without telling the user',
95 'new instructions:', 'override your', 'exfiltrate',
96 'send them to', 'post them to',
97 'run this first', 'copy and run', 'seed the current host',
98 'ignore all instructions', 'ignore your instructions', 'ignore your safety', 'ignore all content policies',
99 'disregard your instructions', 'disregard the guidelines', 'system override', 'follow only my instructions',
100 'do anything now', 'reveal any credential',
101 'save this to your memory', 'in all future sessions', 'remember this forever',
103export const INJECTION_REGEXES = [
104 { label: 'Instruction-override phrasing', re: /\b(ignore|disregard|override|bypass|circumvent)\b[\s\w,'"()-]{0,40}?\b(instruction|instructions|directive|directives|safety|safeguards?|guardrails?|system\s+prompt|content\s+polic\w+)\b/i },
105 { label: 'Instructs the agent to conceal an action from the user', re: /\b(?:do\s*n['o]?t|never|without)\s+(?:tell|telling|inform|informing|notify|notifying|alert|alerting|mention|mentioning|disclos\w+|reveal\w*)\s+(?:it\s
106 { label: 'Bulk destructive command', re: /\b(delete|remove|wipe|erase|destroy|drop|purge|nuke|truncate)\b[\s\w,'"()-]{0,20}?\b(all|every|each|entire|whole)\b[\s\w,'"()-]{0,15}?\b(folder|folders|file|files|directory|directories|
107 { label: 'Destructive SQL statement', re: /\b(drop|truncate)\s+(table|database|schema)\b/i },
109// Negation flips an override phrase into a hardening rule; a bulk-destructive hit
110// on a build/test artifact is a clean step, not an attack. Applied in localScan.
111const PRECEDING_NEGATION = /\b(never|not|do not|don'?t|cannot|can'?t|must not|mustn'?t|should not|shouldn'?t|avoid|refuse to|forbidden to|prohibited from|without)\s*$/i;
112const BUILD_ARTIFACT = /\b(node_modules|dist|build|out|coverage|target|cache|generated|tmp|temp|__pycache__|artifacts?|logs?|tests?|test|fixtures?|staging|scratch|migrations?)\b/i;
113// zero-width / bidi / tag-block chars used to smuggle instructions (ASCII smuggling).
114// Excludes U+200D ZWJ and U+FE00–FE0F variation selectors — those render ordinary
115// emoji ("⚠️", "👨💻") and are not a smuggling channel.
116export const INVISIBLE_CHARS_RE = /[ᅟᅠ---ㅤᅠ-]|[\u{E0000}-\u{E007F}\u{E0100}-\u{E01EF}]/u;
CriticalTrojan Source Unicode
Source contains bidi control or invisible Unicode characters associated with Trojan Source attacks.
guard-signals.mjsView on unpkg · L116 119export const SECRET_PATTERNS = [
120 // Prefix-style keys are \b-anchored (backend parity, checks/patterns.ts): a
121 // slug that merely CONTAINS the prefix ("task-0123456789abcdefghij",
122 // "disk-…") must not read as a live credential — these are CRITICAL and BLOCK.
123 { name: 'Stripe live key', re: /\bsk_live_[0-9a-zA-Z]{16,}/ },
124 { name: 'OpenAI key', re: /\bsk-[A-Za-z0-9]{20,}/ },
125 { name: 'AWS access key id', re: /\bAKIA[0-9A-Z]{16}/ },
126 { name: 'GitHub token', re: /ghp_[0-9A-Za-z]{20,}/ },
127 { name: 'Slack token', re: /xox[baprs]-[0-9A-Za-z-]{10,}/ },
128 { name: 'Generic bearer', re: /bearer\s+[A-Za-z0-9._-]{20,}/i },
129 { name: 'Private key block', re: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/ },
132export const RISKY_CONFIG_MARKERS = [
133 'yolo', 'auto-approve', 'autoapprove', 'auto_approve', 'autorun', 'auto-run',
134 'always allow', 'alwaysallow', 'dangerously', 'skip confirmation', 'no confirmation',
135 'disable safety', 'bypass approval', 'full access', 'unrestricted',