cipher-security 2.0.8 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cipher.js +11 -1
- package/lib/agent-runtime/handlers/architect.js +199 -0
- package/lib/agent-runtime/handlers/base.js +240 -0
- package/lib/agent-runtime/handlers/blue.js +220 -0
- package/lib/agent-runtime/handlers/incident.js +161 -0
- package/lib/agent-runtime/handlers/privacy.js +190 -0
- package/lib/agent-runtime/handlers/purple.js +209 -0
- package/lib/agent-runtime/handlers/recon.js +174 -0
- package/lib/agent-runtime/handlers/red.js +246 -0
- package/lib/agent-runtime/handlers/researcher.js +170 -0
- package/lib/agent-runtime/handlers.js +35 -0
- package/lib/agent-runtime/index.js +196 -0
- package/lib/agent-runtime/parser.js +316 -0
- package/lib/analyze/consistency.js +566 -0
- package/lib/analyze/constitution.js +110 -0
- package/lib/analyze/sharding.js +251 -0
- package/lib/autonomous/agent-tool.js +165 -0
- package/lib/autonomous/feedback-loop.js +13 -6
- package/lib/autonomous/framework.js +17 -0
- package/lib/autonomous/handoff.js +506 -0
- package/lib/autonomous/modes/blue.js +26 -0
- package/lib/autonomous/modes/red.js +585 -0
- package/lib/autonomous/modes/researcher.js +322 -0
- package/lib/autonomous/researcher.js +12 -45
- package/lib/autonomous/runner.js +9 -537
- package/lib/benchmark/agent.js +88 -26
- package/lib/benchmark/baselines.js +3 -0
- package/lib/benchmark/claude-code-solver.js +254 -0
- package/lib/benchmark/cognitive.js +283 -0
- package/lib/benchmark/index.js +12 -2
- package/lib/benchmark/knowledge.js +281 -0
- package/lib/benchmark/llm.js +156 -15
- package/lib/benchmark/models.js +5 -2
- package/lib/benchmark/nyu-ctf.js +192 -0
- package/lib/benchmark/overthewire.js +347 -0
- package/lib/benchmark/picoctf.js +281 -0
- package/lib/benchmark/prompts.js +280 -0
- package/lib/benchmark/registry.js +219 -0
- package/lib/benchmark/remote-solver.js +356 -0
- package/lib/benchmark/remote-target.js +263 -0
- package/lib/benchmark/reporter.js +35 -0
- package/lib/benchmark/runner.js +174 -10
- package/lib/benchmark/sandbox.js +35 -0
- package/lib/benchmark/scorer.js +22 -4
- package/lib/benchmark/solver.js +34 -1
- package/lib/benchmark/tools.js +262 -16
- package/lib/commands.js +9 -0
- package/lib/execution/council.js +434 -0
- package/lib/execution/parallel.js +292 -0
- package/lib/gates/circuit-breaker.js +135 -0
- package/lib/gates/confidence.js +302 -0
- package/lib/gates/corrections.js +219 -0
- package/lib/gates/self-check.js +245 -0
- package/lib/gateway/commands.js +727 -0
- package/lib/guardrails/engine.js +364 -0
- package/lib/mcp/server.js +349 -3
- package/lib/memory/compressor.js +94 -7
- package/lib/pipeline/hooks.js +288 -0
- package/lib/pipeline/index.js +11 -0
- package/lib/review/budget.js +210 -0
- package/lib/review/engine.js +526 -0
- package/lib/review/layers/acceptance-auditor.js +279 -0
- package/lib/review/layers/blind-hunter.js +500 -0
- package/lib/review/layers/defense-in-depth.js +209 -0
- package/lib/review/layers/edge-case-hunter.js +266 -0
- package/lib/review/panel.js +519 -0
- package/lib/review/two-stage.js +244 -0
- package/lib/session/cost-tracker.js +203 -0
- package/lib/session/logger.js +349 -0
- package/package.json +1 -1
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
// Copyright (c) 2026 defconxt. All rights reserved.
|
|
2
|
+
// Licensed under AGPL-3.0 — see LICENSE file for details.
|
|
3
|
+
// CIPHER is a trademark of defconxt.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Blind Hunter — Pattern-based vulnerability detection layer.
|
|
7
|
+
*
|
|
8
|
+
* Scans source code for known-bad patterns using regex matching.
|
|
9
|
+
* Language-aware: applies different rulesets based on detected language.
|
|
10
|
+
* No context about intent — pure signal extraction to avoid anchoring bias.
|
|
11
|
+
*
|
|
12
|
+
* @module review/layers/blind-hunter
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { ReviewFinding, Severity } from '../engine.js';
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Pattern definitions
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {object} VulnPattern
|
|
23
|
+
* @property {string} id - Unique pattern identifier
|
|
24
|
+
* @property {string} title - Finding title
|
|
25
|
+
* @property {RegExp} pattern - Detection regex
|
|
26
|
+
* @property {string} severity - Severity level
|
|
27
|
+
* @property {string[]} cweIds - Associated CWEs
|
|
28
|
+
* @property {string} description - What this pattern catches
|
|
29
|
+
* @property {string} remediation - Fix guidance
|
|
30
|
+
* @property {string[]} languages - Which languages this applies to ('*' = all)
|
|
31
|
+
* @property {string[]} [tags] - Additional tags
|
|
32
|
+
* @property {RegExp} [exclude] - Lines matching this are false positives
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/** @type {VulnPattern[]} */
|
|
36
|
+
const PATTERNS = [
|
|
37
|
+
// ── Injection ─────────────────────────────────────────────────────────
|
|
38
|
+
{
|
|
39
|
+
id: 'BH-SQLI-001',
|
|
40
|
+
title: 'SQL injection — string concatenation in query',
|
|
41
|
+
pattern: /(?:query|execute|exec|run)\s*\(\s*[`'"].*?\$\{|(?:query|execute|exec|run)\s*\(\s*['"].*?\+\s*(?:req\.|params\.|body\.|query\.|input|user)/gi,
|
|
42
|
+
severity: Severity.CRITICAL,
|
|
43
|
+
cweIds: ['CWE-89'],
|
|
44
|
+
description: 'SQL query built with string concatenation or template literals containing user input.',
|
|
45
|
+
remediation: 'Use parameterized queries or prepared statements.',
|
|
46
|
+
languages: ['*'],
|
|
47
|
+
tags: ['owasp-a03', 'T1190'],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'BH-SQLI-002',
|
|
51
|
+
title: 'SQL injection — raw query with variable',
|
|
52
|
+
pattern: /\.(?:raw|rawQuery|unsafe)\s*\(\s*[`'"]/gi,
|
|
53
|
+
severity: Severity.HIGH,
|
|
54
|
+
cweIds: ['CWE-89'],
|
|
55
|
+
description: 'Raw SQL query method used. Verify that all inputs are parameterized.',
|
|
56
|
+
remediation: 'Replace raw queries with the ORM\'s parameterized query builder.',
|
|
57
|
+
languages: ['javascript', 'typescript', 'python', 'ruby'],
|
|
58
|
+
tags: ['owasp-a03'],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 'BH-CMDI-001',
|
|
62
|
+
title: 'Command injection — shell execution with variable',
|
|
63
|
+
pattern: /(?:exec|execSync|spawn|spawnSync|system|popen|subprocess\.(?:call|run|Popen))\s*\(\s*(?:[`'"].*?\$\{|.*?\+\s*(?:req\.|params\.|body\.|input|user|arg))/gi,
|
|
64
|
+
severity: Severity.CRITICAL,
|
|
65
|
+
cweIds: ['CWE-78'],
|
|
66
|
+
description: 'Shell command built with user-controlled input.',
|
|
67
|
+
remediation: 'Use execFile/execFileSync with argument arrays, or validate/escape input strictly.',
|
|
68
|
+
languages: ['*'],
|
|
69
|
+
tags: ['owasp-a03', 'T1059'],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'BH-CMDI-002',
|
|
73
|
+
title: 'Command injection — shell: true with variable args',
|
|
74
|
+
pattern: /shell\s*:\s*true/gi,
|
|
75
|
+
severity: Severity.MEDIUM,
|
|
76
|
+
cweIds: ['CWE-78'],
|
|
77
|
+
description: 'Process spawned with shell: true. Shell metacharacters in arguments can lead to injection.',
|
|
78
|
+
remediation: 'Remove shell: true and pass arguments as an array.',
|
|
79
|
+
languages: ['javascript', 'typescript'],
|
|
80
|
+
tags: ['owasp-a03'],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'BH-PATH-001',
|
|
84
|
+
title: 'Path traversal — user input in file path',
|
|
85
|
+
pattern: /(?:readFile|writeFile|createReadStream|createWriteStream|readdir|unlink|rmSync|open)\s*\(\s*(?:[`'"].*?\$\{|.*?\+\s*(?:req\.|params\.|body\.|query\.|input|user))/gi,
|
|
86
|
+
severity: Severity.HIGH,
|
|
87
|
+
cweIds: ['CWE-22'],
|
|
88
|
+
description: 'File operation uses user-controlled path without sanitization.',
|
|
89
|
+
remediation: 'Validate path with path.resolve() and ensure it stays within an allowed base directory.',
|
|
90
|
+
languages: ['javascript', 'typescript'],
|
|
91
|
+
tags: ['owasp-a03', 'T1083'],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 'BH-PATH-002',
|
|
95
|
+
title: 'Path traversal — Python file open with user input',
|
|
96
|
+
pattern: /open\s*\(\s*(?:f['"'].*?\{|.*?\+\s*(?:request\.|input|user|arg))/gi,
|
|
97
|
+
severity: Severity.HIGH,
|
|
98
|
+
cweIds: ['CWE-22'],
|
|
99
|
+
description: 'File open() with user-controlled path.',
|
|
100
|
+
remediation: 'Use pathlib and validate the resolved path against an allowed directory.',
|
|
101
|
+
languages: ['python'],
|
|
102
|
+
tags: ['owasp-a03'],
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// ── XSS ───────────────────────────────────────────────────────────────
|
|
106
|
+
{
|
|
107
|
+
id: 'BH-XSS-001',
|
|
108
|
+
title: 'XSS — innerHTML assignment',
|
|
109
|
+
pattern: /\.innerHTML\s*=\s*(?!['"`]\s*$)/gi,
|
|
110
|
+
severity: Severity.HIGH,
|
|
111
|
+
cweIds: ['CWE-79'],
|
|
112
|
+
description: 'Setting innerHTML with dynamic content enables XSS.',
|
|
113
|
+
remediation: 'Use textContent, or sanitize with DOMPurify before innerHTML assignment.',
|
|
114
|
+
languages: ['javascript', 'typescript'],
|
|
115
|
+
tags: ['owasp-a03'],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: 'BH-XSS-002',
|
|
119
|
+
title: 'XSS — document.write usage',
|
|
120
|
+
pattern: /document\.write\s*\(/gi,
|
|
121
|
+
severity: Severity.MEDIUM,
|
|
122
|
+
cweIds: ['CWE-79'],
|
|
123
|
+
description: 'document.write() can inject unsanitized content into the DOM.',
|
|
124
|
+
remediation: 'Use DOM APIs (createElement, appendChild) instead.',
|
|
125
|
+
languages: ['javascript', 'typescript'],
|
|
126
|
+
tags: ['owasp-a03'],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 'BH-XSS-003',
|
|
130
|
+
title: 'XSS — dangerouslySetInnerHTML',
|
|
131
|
+
pattern: /dangerouslySetInnerHTML/gi,
|
|
132
|
+
severity: Severity.MEDIUM,
|
|
133
|
+
cweIds: ['CWE-79'],
|
|
134
|
+
description: 'React dangerouslySetInnerHTML bypasses built-in XSS protection.',
|
|
135
|
+
remediation: 'Sanitize content with DOMPurify before passing to dangerouslySetInnerHTML.',
|
|
136
|
+
languages: ['javascript', 'typescript'],
|
|
137
|
+
tags: ['owasp-a03'],
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
// ── Eval / Code Execution ─────────────────────────────────────────────
|
|
141
|
+
{
|
|
142
|
+
id: 'BH-EVAL-001',
|
|
143
|
+
title: 'Code injection — eval() usage',
|
|
144
|
+
pattern: /\beval\s*\(/gi,
|
|
145
|
+
severity: Severity.HIGH,
|
|
146
|
+
cweIds: ['CWE-95'],
|
|
147
|
+
description: 'eval() executes arbitrary code. If input is user-controlled, this is code injection.',
|
|
148
|
+
remediation: 'Replace eval with JSON.parse, Function constructor with validated input, or a safe expression parser.',
|
|
149
|
+
languages: ['javascript', 'typescript', 'python'],
|
|
150
|
+
tags: ['owasp-a03', 'T1059'],
|
|
151
|
+
exclude: /\/\/.*eval|\/\*.*eval|\*.*eval|\.eslint|no-eval/,
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: 'BH-EVAL-002',
|
|
155
|
+
title: 'Code injection — new Function() constructor',
|
|
156
|
+
pattern: /new\s+Function\s*\(/gi,
|
|
157
|
+
severity: Severity.HIGH,
|
|
158
|
+
cweIds: ['CWE-95'],
|
|
159
|
+
description: 'new Function() is functionally equivalent to eval().',
|
|
160
|
+
remediation: 'Use a safe expression evaluator or structured approach.',
|
|
161
|
+
languages: ['javascript', 'typescript'],
|
|
162
|
+
tags: ['owasp-a03'],
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: 'BH-EVAL-003',
|
|
166
|
+
title: 'Code injection — exec() with dynamic input',
|
|
167
|
+
pattern: /\bexec\s*\(\s*(?:f['"']|.*?\+\s*|.*?\%)/gi,
|
|
168
|
+
severity: Severity.HIGH,
|
|
169
|
+
cweIds: ['CWE-95'],
|
|
170
|
+
description: 'Python exec() with formatted string or concatenation.',
|
|
171
|
+
remediation: 'Avoid exec(). Use ast.literal_eval() for safe evaluation.',
|
|
172
|
+
languages: ['python'],
|
|
173
|
+
tags: ['owasp-a03'],
|
|
174
|
+
exclude: /child_process|subprocess|execFile|execSync/,
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// ── Secrets / Credentials ─────────────────────────────────────────────
|
|
178
|
+
{
|
|
179
|
+
id: 'BH-SEC-001',
|
|
180
|
+
title: 'Hardcoded secret — API key or token pattern',
|
|
181
|
+
pattern: /(?:api[_-]?key|api[_-]?secret|auth[_-]?token|access[_-]?token|secret[_-]?key|private[_-]?key)\s*[:=]\s*['"]\S{8,}['"]/gi,
|
|
182
|
+
severity: Severity.HIGH,
|
|
183
|
+
cweIds: ['CWE-798'],
|
|
184
|
+
description: 'Hardcoded credential found in source code.',
|
|
185
|
+
remediation: 'Move secrets to environment variables or a secrets manager.',
|
|
186
|
+
languages: ['*'],
|
|
187
|
+
tags: ['owasp-a02', 'T1552.001'],
|
|
188
|
+
exclude: /example|placeholder|test|mock|dummy|YOUR_|CHANGE_ME|process\.env|os\.environ/i,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: 'BH-SEC-002',
|
|
192
|
+
title: 'Hardcoded secret — AWS key pattern',
|
|
193
|
+
pattern: /(?:AKIA[0-9A-Z]{16})/g,
|
|
194
|
+
severity: Severity.CRITICAL,
|
|
195
|
+
cweIds: ['CWE-798'],
|
|
196
|
+
description: 'AWS access key ID detected in source code.',
|
|
197
|
+
remediation: 'Rotate the key immediately, move to environment variables or IAM roles.',
|
|
198
|
+
languages: ['*'],
|
|
199
|
+
tags: ['owasp-a02', 'T1552.001'],
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
id: 'BH-SEC-003',
|
|
203
|
+
title: 'Hardcoded secret — password in code',
|
|
204
|
+
pattern: /(?:password|passwd|pwd)\s*[:=]\s*['"][^'"]{4,}['"]/gi,
|
|
205
|
+
severity: Severity.HIGH,
|
|
206
|
+
cweIds: ['CWE-798'],
|
|
207
|
+
description: 'Hardcoded password found in source code.',
|
|
208
|
+
remediation: 'Move passwords to environment variables or a secrets manager.',
|
|
209
|
+
languages: ['*'],
|
|
210
|
+
tags: ['owasp-a02', 'T1552.001'],
|
|
211
|
+
exclude: /example|placeholder|test|mock|dummy|YOUR_|CHANGE_ME|process\.env|os\.environ|\*{3,}|\.{3,}/i,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: 'BH-SEC-004',
|
|
215
|
+
title: 'Hardcoded secret — JWT or bearer token',
|
|
216
|
+
pattern: /(?:eyJ[A-Za-z0-9_-]{20,}\.eyJ[A-Za-z0-9_-]{20,})/g,
|
|
217
|
+
severity: Severity.HIGH,
|
|
218
|
+
cweIds: ['CWE-798'],
|
|
219
|
+
description: 'JWT token found hardcoded in source code.',
|
|
220
|
+
remediation: 'Remove the token, rotate it, and load from environment or auth flow.',
|
|
221
|
+
languages: ['*'],
|
|
222
|
+
tags: ['owasp-a02', 'T1552.001'],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
id: 'BH-SEC-005',
|
|
226
|
+
title: 'Hardcoded secret — private key block',
|
|
227
|
+
pattern: /-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----/gi,
|
|
228
|
+
severity: Severity.CRITICAL,
|
|
229
|
+
cweIds: ['CWE-798'],
|
|
230
|
+
description: 'PEM private key found in source code.',
|
|
231
|
+
remediation: 'Remove the key, rotate it, and store in a secrets manager or HSM.',
|
|
232
|
+
languages: ['*'],
|
|
233
|
+
tags: ['owasp-a02', 'T1552.001'],
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
// ── Insecure Crypto ───────────────────────────────────────────────────
|
|
237
|
+
{
|
|
238
|
+
id: 'BH-CRYPTO-001',
|
|
239
|
+
title: 'Weak hash — MD5 usage',
|
|
240
|
+
pattern: /(?:createHash\s*\(\s*['"]md5['"]|md5\s*\(|hashlib\.md5|MD5\.Create|Digest::MD5)/gi,
|
|
241
|
+
severity: Severity.MEDIUM,
|
|
242
|
+
cweIds: ['CWE-328'],
|
|
243
|
+
description: 'MD5 is cryptographically broken. Not suitable for security purposes.',
|
|
244
|
+
remediation: 'Use SHA-256 or better (SHA-3, BLAKE3) for integrity. Use bcrypt/scrypt/argon2 for passwords.',
|
|
245
|
+
languages: ['*'],
|
|
246
|
+
tags: ['owasp-a02'],
|
|
247
|
+
exclude: /checksum|etag|cache|fingerprint|non-security/i,
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: 'BH-CRYPTO-002',
|
|
251
|
+
title: 'Weak hash — SHA-1 usage',
|
|
252
|
+
pattern: /(?:createHash\s*\(\s*['"]sha1['"]|hashlib\.sha1|SHA1\.Create)/gi,
|
|
253
|
+
severity: Severity.MEDIUM,
|
|
254
|
+
cweIds: ['CWE-328'],
|
|
255
|
+
description: 'SHA-1 is deprecated for security use due to practical collision attacks.',
|
|
256
|
+
remediation: 'Use SHA-256 or better.',
|
|
257
|
+
languages: ['*'],
|
|
258
|
+
tags: ['owasp-a02'],
|
|
259
|
+
exclude: /git|subresource|integrity/i,
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: 'BH-CRYPTO-003',
|
|
263
|
+
title: 'Insecure random — Math.random() for security',
|
|
264
|
+
pattern: /Math\.random\s*\(\)/gi,
|
|
265
|
+
severity: Severity.MEDIUM,
|
|
266
|
+
cweIds: ['CWE-338'],
|
|
267
|
+
description: 'Math.random() is not cryptographically secure. Predictable output.',
|
|
268
|
+
remediation: 'Use crypto.randomBytes(), crypto.randomUUID(), or crypto.getRandomValues().',
|
|
269
|
+
languages: ['javascript', 'typescript'],
|
|
270
|
+
tags: ['owasp-a02'],
|
|
271
|
+
exclude: /test|mock|demo|example|shuffle|color|animation/i,
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
// ── Prototype Pollution ───────────────────────────────────────────────
|
|
275
|
+
{
|
|
276
|
+
id: 'BH-PROTO-001',
|
|
277
|
+
title: 'Prototype pollution — recursive merge without protection',
|
|
278
|
+
pattern: /(?:Object\.assign|_\.merge|_\.extend|_\.defaults|deepMerge|deepExtend)\s*\(/gi,
|
|
279
|
+
severity: Severity.MEDIUM,
|
|
280
|
+
cweIds: ['CWE-1321'],
|
|
281
|
+
description: 'Deep merge without __proto__/constructor/prototype filtering enables prototype pollution.',
|
|
282
|
+
remediation: 'Use a merge function that skips __proto__, constructor, and prototype keys.',
|
|
283
|
+
languages: ['javascript', 'typescript'],
|
|
284
|
+
tags: ['owasp-a03'],
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
id: 'BH-PROTO-002',
|
|
288
|
+
title: 'Prototype pollution — bracket notation with variable key',
|
|
289
|
+
pattern: /\]\s*\[\s*(?:key|prop|name|attr|field)\s*\]/gi,
|
|
290
|
+
severity: Severity.MEDIUM,
|
|
291
|
+
cweIds: ['CWE-1321'],
|
|
292
|
+
description: 'Nested bracket notation with variable key enables prototype pollution.',
|
|
293
|
+
remediation: 'Validate keys against a whitelist or use Map instead of plain objects.',
|
|
294
|
+
languages: ['javascript', 'typescript'],
|
|
295
|
+
tags: ['owasp-a03'],
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
// ── SSRF ──────────────────────────────────────────────────────────────
|
|
299
|
+
{
|
|
300
|
+
id: 'BH-SSRF-001',
|
|
301
|
+
title: 'SSRF — fetch/request with user-controlled URL',
|
|
302
|
+
pattern: /(?:fetch|axios|got|request|http\.get|urllib\.request)\s*\(\s*(?:[`'"].*?\$\{|.*?\+\s*(?:req\.|params\.|body\.|query\.|input|user|url))/gi,
|
|
303
|
+
severity: Severity.HIGH,
|
|
304
|
+
cweIds: ['CWE-918'],
|
|
305
|
+
description: 'HTTP request made with user-controlled URL. Server-side request forgery risk.',
|
|
306
|
+
remediation: 'Validate URLs against an allowlist of domains. Block internal/private IP ranges.',
|
|
307
|
+
languages: ['*'],
|
|
308
|
+
tags: ['owasp-a10', 'T1090'],
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
// ── Deserialization ───────────────────────────────────────────────────
|
|
312
|
+
{
|
|
313
|
+
id: 'BH-DESER-001',
|
|
314
|
+
title: 'Unsafe deserialization — pickle/yaml load',
|
|
315
|
+
pattern: /(?:pickle\.loads?|yaml\.(?:load|unsafe_load)\s*\((?!.*Loader\s*=\s*yaml\.SafeLoader))/gi,
|
|
316
|
+
severity: Severity.CRITICAL,
|
|
317
|
+
cweIds: ['CWE-502'],
|
|
318
|
+
description: 'Unsafe deserialization can lead to remote code execution.',
|
|
319
|
+
remediation: 'Use yaml.safe_load() instead of yaml.load(). Avoid pickle for untrusted data.',
|
|
320
|
+
languages: ['python'],
|
|
321
|
+
tags: ['owasp-a08', 'T1059'],
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
id: 'BH-DESER-002',
|
|
325
|
+
title: 'Unsafe deserialization — unserialize()',
|
|
326
|
+
pattern: /\bunserialize\s*\(/gi,
|
|
327
|
+
severity: Severity.HIGH,
|
|
328
|
+
cweIds: ['CWE-502'],
|
|
329
|
+
description: 'PHP unserialize() with user input enables object injection.',
|
|
330
|
+
remediation: 'Use json_decode() instead, or validate input strictly before unserialize.',
|
|
331
|
+
languages: ['php'],
|
|
332
|
+
tags: ['owasp-a08'],
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
// ── Regex DoS ─────────────────────────────────────────────────────────
|
|
336
|
+
{
|
|
337
|
+
id: 'BH-REDOS-001',
|
|
338
|
+
title: 'Regex DoS — catastrophic backtracking pattern',
|
|
339
|
+
pattern: /new\s+RegExp\s*\(\s*(?:req\.|params\.|body\.|query\.|input|user)/gi,
|
|
340
|
+
severity: Severity.MEDIUM,
|
|
341
|
+
cweIds: ['CWE-1333'],
|
|
342
|
+
description: 'User-controlled regex pattern enables ReDoS (Regular Expression DoS).',
|
|
343
|
+
remediation: 'Never compile user input as regex. Use string matching or a regex sandbox.',
|
|
344
|
+
languages: ['javascript', 'typescript', 'python'],
|
|
345
|
+
tags: ['owasp-a06'],
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
// ── Misconfiguration ─────────────────────────────────────────────────
|
|
349
|
+
{
|
|
350
|
+
id: 'BH-MISC-001',
|
|
351
|
+
title: 'CORS misconfiguration — wildcard origin',
|
|
352
|
+
pattern: /(?:Access-Control-Allow-Origin['"]\s*:\s*['"]\*|origin\s*:\s*['"]\*|cors\s*\(\s*\))/gi,
|
|
353
|
+
severity: Severity.MEDIUM,
|
|
354
|
+
cweIds: ['CWE-942'],
|
|
355
|
+
description: 'Wildcard CORS origin allows any website to make authenticated requests.',
|
|
356
|
+
remediation: 'Restrict CORS to specific trusted origins.',
|
|
357
|
+
languages: ['javascript', 'typescript', 'python'],
|
|
358
|
+
tags: ['owasp-a05'],
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
id: 'BH-MISC-002',
|
|
362
|
+
title: 'Debug mode enabled in production',
|
|
363
|
+
pattern: /(?:DEBUG\s*[:=]\s*(?:True|true|1|['"]true['"])|debug\s*:\s*true|NODE_ENV\s*[:=!]=\s*['"]development['"])/gi,
|
|
364
|
+
severity: Severity.LOW,
|
|
365
|
+
cweIds: ['CWE-489'],
|
|
366
|
+
description: 'Debug mode may expose sensitive information in production.',
|
|
367
|
+
remediation: 'Ensure DEBUG is disabled in production configuration.',
|
|
368
|
+
languages: ['*'],
|
|
369
|
+
tags: ['owasp-a05'],
|
|
370
|
+
exclude: /test|spec|\.test\.|\.spec\.|example|if\s*\(/i,
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
id: 'BH-MISC-003',
|
|
374
|
+
title: 'Disabled TLS verification',
|
|
375
|
+
pattern: /(?:rejectUnauthorized\s*:\s*false|verify\s*[:=]\s*False|CURLOPT_SSL_VERIFYPEER\s*,\s*(?:false|0)|InsecureSkipVerify\s*:\s*true)/gi,
|
|
376
|
+
severity: Severity.HIGH,
|
|
377
|
+
cweIds: ['CWE-295'],
|
|
378
|
+
description: 'TLS certificate verification disabled. Enables MITM attacks.',
|
|
379
|
+
remediation: 'Enable certificate verification. Use a custom CA bundle if needed.',
|
|
380
|
+
languages: ['*'],
|
|
381
|
+
tags: ['owasp-a07', 'T1557'],
|
|
382
|
+
},
|
|
383
|
+
|
|
384
|
+
// ── Dangerous APIs ────────────────────────────────────────────────────
|
|
385
|
+
{
|
|
386
|
+
id: 'BH-API-001',
|
|
387
|
+
title: 'Disabled security header — helmet not used',
|
|
388
|
+
pattern: /app\.disable\s*\(\s*['"]x-powered-by['"]\s*\)/gi,
|
|
389
|
+
severity: Severity.INFO,
|
|
390
|
+
cweIds: ['CWE-16'],
|
|
391
|
+
description: 'Manual header removal instead of using helmet middleware.',
|
|
392
|
+
remediation: 'Use helmet() middleware for comprehensive security headers.',
|
|
393
|
+
languages: ['javascript', 'typescript'],
|
|
394
|
+
tags: ['owasp-a05'],
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
id: 'BH-API-002',
|
|
398
|
+
title: 'Unvalidated redirect',
|
|
399
|
+
pattern: /(?:res\.redirect|redirect)\s*\(\s*(?:req\.|params\.|body\.|query\.|input|user)/gi,
|
|
400
|
+
severity: Severity.MEDIUM,
|
|
401
|
+
cweIds: ['CWE-601'],
|
|
402
|
+
description: 'Redirect destination from user input enables open redirect attacks.',
|
|
403
|
+
remediation: 'Validate redirect URLs against an allowlist of paths or domains.',
|
|
404
|
+
languages: ['javascript', 'typescript', 'python'],
|
|
405
|
+
tags: ['owasp-a03'],
|
|
406
|
+
},
|
|
407
|
+
|
|
408
|
+
// ── Shell / Infrastructure ────────────────────────────────────────────
|
|
409
|
+
{
|
|
410
|
+
id: 'BH-SHELL-001',
|
|
411
|
+
title: 'World-writable permissions',
|
|
412
|
+
pattern: /chmod\s+(?:777|666|o\+w)/gi,
|
|
413
|
+
severity: Severity.MEDIUM,
|
|
414
|
+
cweIds: ['CWE-732'],
|
|
415
|
+
description: 'World-writable file permissions allow any user to modify the file.',
|
|
416
|
+
remediation: 'Use least-privilege permissions (e.g., 755 for dirs, 644 for files).',
|
|
417
|
+
languages: ['shell', '*'],
|
|
418
|
+
tags: ['T1222'],
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
id: 'BH-SHELL-002',
|
|
422
|
+
title: 'Curl pipe to shell — unsigned code execution',
|
|
423
|
+
pattern: /curl\s+.*\|\s*(?:sudo\s+)?(?:bash|sh|zsh)/gi,
|
|
424
|
+
severity: Severity.HIGH,
|
|
425
|
+
cweIds: ['CWE-494'],
|
|
426
|
+
description: 'Downloading and executing scripts in one command without verification.',
|
|
427
|
+
remediation: 'Download first, verify integrity (checksum/signature), then execute.',
|
|
428
|
+
languages: ['shell', '*'],
|
|
429
|
+
tags: ['T1105'],
|
|
430
|
+
},
|
|
431
|
+
];
|
|
432
|
+
|
|
433
|
+
// ---------------------------------------------------------------------------
|
|
434
|
+
// Blind Hunter review function
|
|
435
|
+
// ---------------------------------------------------------------------------
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Run Blind Hunter pattern matching against source files.
|
|
439
|
+
*
|
|
440
|
+
* @param {import('../engine.js').SourceFile[]} sources
|
|
441
|
+
* @param {object} [options]
|
|
442
|
+
* @returns {Promise<ReviewFinding[]>}
|
|
443
|
+
*/
|
|
444
|
+
export async function blindHunterReview(sources, options = {}) {
|
|
445
|
+
const findings = [];
|
|
446
|
+
|
|
447
|
+
for (const source of sources) {
|
|
448
|
+
const lines = source.content.split('\n');
|
|
449
|
+
|
|
450
|
+
for (const pat of PATTERNS) {
|
|
451
|
+
// Language filter
|
|
452
|
+
if (!pat.languages.includes('*') && !pat.languages.includes(source.language)) {
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Test each line
|
|
457
|
+
for (let i = 0; i < lines.length; i++) {
|
|
458
|
+
const line = lines[i];
|
|
459
|
+
|
|
460
|
+
// Skip comments (simple heuristic)
|
|
461
|
+
const trimmed = line.trimStart();
|
|
462
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('#') || trimmed.startsWith('*')) {
|
|
463
|
+
// Still check for secrets in comments — they shouldn't be there either
|
|
464
|
+
if (!pat.id.startsWith('BH-SEC')) continue;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Reset regex lastIndex for global patterns
|
|
468
|
+
pat.pattern.lastIndex = 0;
|
|
469
|
+
const match = pat.pattern.exec(line);
|
|
470
|
+
if (!match) continue;
|
|
471
|
+
|
|
472
|
+
// Check exclusion pattern
|
|
473
|
+
if (pat.exclude) {
|
|
474
|
+
pat.exclude.lastIndex = 0;
|
|
475
|
+
if (pat.exclude.test(line)) continue;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
findings.push(
|
|
479
|
+
new ReviewFinding({
|
|
480
|
+
title: pat.title,
|
|
481
|
+
severity: pat.severity,
|
|
482
|
+
layer: 'blind-hunter',
|
|
483
|
+
file: source.path,
|
|
484
|
+
line: i + 1,
|
|
485
|
+
column: match.index + 1,
|
|
486
|
+
description: pat.description,
|
|
487
|
+
proof: line.trim().slice(0, 200),
|
|
488
|
+
remediation: pat.remediation,
|
|
489
|
+
cweIds: [...pat.cweIds],
|
|
490
|
+
tags: pat.tags ? [...pat.tags] : [],
|
|
491
|
+
language: source.language,
|
|
492
|
+
meta: { patternId: pat.id },
|
|
493
|
+
}),
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return findings;
|
|
500
|
+
}
|