ghostpatch 1.0.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/LICENSE +21 -0
- package/README.md +213 -0
- package/__tests__/detectors.test.ts +224 -0
- package/__tests__/rules.test.ts +117 -0
- package/__tests__/scanner.test.ts +222 -0
- package/dist/ai/anthropic.d.ts +11 -0
- package/dist/ai/anthropic.d.ts.map +1 -0
- package/dist/ai/anthropic.js +76 -0
- package/dist/ai/anthropic.js.map +1 -0
- package/dist/ai/huggingface.d.ts +12 -0
- package/dist/ai/huggingface.d.ts.map +1 -0
- package/dist/ai/huggingface.js +95 -0
- package/dist/ai/huggingface.js.map +1 -0
- package/dist/ai/openai.d.ts +11 -0
- package/dist/ai/openai.d.ts.map +1 -0
- package/dist/ai/openai.js +71 -0
- package/dist/ai/openai.js.map +1 -0
- package/dist/ai/prompts.d.ts +5 -0
- package/dist/ai/prompts.d.ts.map +1 -0
- package/dist/ai/prompts.js +101 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/ai/provider.d.ts +9 -0
- package/dist/ai/provider.d.ts.map +1 -0
- package/dist/ai/provider.js +66 -0
- package/dist/ai/provider.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +318 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/reporter.d.ts +7 -0
- package/dist/core/reporter.d.ts.map +1 -0
- package/dist/core/reporter.js +366 -0
- package/dist/core/reporter.js.map +1 -0
- package/dist/core/rules.d.ts +8 -0
- package/dist/core/rules.d.ts.map +1 -0
- package/dist/core/rules.js +1077 -0
- package/dist/core/rules.js.map +1 -0
- package/dist/core/scanner.d.ts +6 -0
- package/dist/core/scanner.d.ts.map +1 -0
- package/dist/core/scanner.js +217 -0
- package/dist/core/scanner.js.map +1 -0
- package/dist/core/severity.d.ts +100 -0
- package/dist/core/severity.d.ts.map +1 -0
- package/dist/core/severity.js +52 -0
- package/dist/core/severity.js.map +1 -0
- package/dist/detectors/auth.d.ts +3 -0
- package/dist/detectors/auth.d.ts.map +1 -0
- package/dist/detectors/auth.js +138 -0
- package/dist/detectors/auth.js.map +1 -0
- package/dist/detectors/crypto.d.ts +3 -0
- package/dist/detectors/crypto.d.ts.map +1 -0
- package/dist/detectors/crypto.js +128 -0
- package/dist/detectors/crypto.js.map +1 -0
- package/dist/detectors/dependency.d.ts +4 -0
- package/dist/detectors/dependency.d.ts.map +1 -0
- package/dist/detectors/dependency.js +267 -0
- package/dist/detectors/dependency.js.map +1 -0
- package/dist/detectors/deserialize.d.ts +3 -0
- package/dist/detectors/deserialize.d.ts.map +1 -0
- package/dist/detectors/deserialize.js +107 -0
- package/dist/detectors/deserialize.js.map +1 -0
- package/dist/detectors/injection.d.ts +3 -0
- package/dist/detectors/injection.d.ts.map +1 -0
- package/dist/detectors/injection.js +158 -0
- package/dist/detectors/injection.js.map +1 -0
- package/dist/detectors/misconfig.d.ts +3 -0
- package/dist/detectors/misconfig.d.ts.map +1 -0
- package/dist/detectors/misconfig.js +153 -0
- package/dist/detectors/misconfig.js.map +1 -0
- package/dist/detectors/pathtraversal.d.ts +3 -0
- package/dist/detectors/pathtraversal.d.ts.map +1 -0
- package/dist/detectors/pathtraversal.js +90 -0
- package/dist/detectors/pathtraversal.js.map +1 -0
- package/dist/detectors/prototype.d.ts +3 -0
- package/dist/detectors/prototype.d.ts.map +1 -0
- package/dist/detectors/prototype.js +79 -0
- package/dist/detectors/prototype.js.map +1 -0
- package/dist/detectors/secrets.d.ts +4 -0
- package/dist/detectors/secrets.d.ts.map +1 -0
- package/dist/detectors/secrets.js +137 -0
- package/dist/detectors/secrets.js.map +1 -0
- package/dist/detectors/ssrf.d.ts +3 -0
- package/dist/detectors/ssrf.d.ts.map +1 -0
- package/dist/detectors/ssrf.js +78 -0
- package/dist/detectors/ssrf.js.map +1 -0
- package/dist/detectors/zeroday.d.ts +9 -0
- package/dist/detectors/zeroday.d.ts.map +1 -0
- package/dist/detectors/zeroday.js +77 -0
- package/dist/detectors/zeroday.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +358 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/utils/config.d.ts +4 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +97 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/fingerprint.d.ts +5 -0
- package/dist/utils/fingerprint.d.ts.map +1 -0
- package/dist/utils/fingerprint.js +55 -0
- package/dist/utils/fingerprint.js.map +1 -0
- package/dist/utils/languages.d.ts +8 -0
- package/dist/utils/languages.d.ts.map +1 -0
- package/dist/utils/languages.js +128 -0
- package/dist/utils/languages.js.map +1 -0
- package/package.json +53 -0
- package/src/ai/anthropic.ts +82 -0
- package/src/ai/huggingface.ts +111 -0
- package/src/ai/openai.ts +75 -0
- package/src/ai/prompts.ts +100 -0
- package/src/ai/provider.ts +68 -0
- package/src/cli/index.ts +314 -0
- package/src/core/reporter.ts +356 -0
- package/src/core/rules.ts +1089 -0
- package/src/core/scanner.ts +201 -0
- package/src/core/severity.ts +140 -0
- package/src/detectors/auth.ts +152 -0
- package/src/detectors/crypto.ts +128 -0
- package/src/detectors/dependency.ts +240 -0
- package/src/detectors/deserialize.ts +106 -0
- package/src/detectors/injection.ts +172 -0
- package/src/detectors/misconfig.ts +152 -0
- package/src/detectors/pathtraversal.ts +89 -0
- package/src/detectors/prototype.ts +77 -0
- package/src/detectors/secrets.ts +138 -0
- package/src/detectors/ssrf.ts +77 -0
- package/src/detectors/zeroday.ts +93 -0
- package/src/index.ts +24 -0
- package/src/mcp/server.ts +379 -0
- package/src/utils/config.ts +64 -0
- package/src/utils/fingerprint.ts +21 -0
- package/src/utils/languages.ts +95 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,1089 @@
|
|
|
1
|
+
import { Rule, Severity } from './severity';
|
|
2
|
+
|
|
3
|
+
const ALL_LANGS = ['javascript', 'typescript', 'python', 'java', 'go', 'rust', 'c', 'cpp', 'csharp', 'php', 'ruby', 'swift', 'kotlin', 'shell', 'sql'];
|
|
4
|
+
const WEB_LANGS = ['javascript', 'typescript', 'python', 'java', 'php', 'ruby', 'csharp', 'kotlin'];
|
|
5
|
+
const JS_TS = ['javascript', 'typescript'];
|
|
6
|
+
const BACKEND = ['javascript', 'typescript', 'python', 'java', 'go', 'php', 'ruby', 'csharp', 'kotlin'];
|
|
7
|
+
|
|
8
|
+
// ============================================================
|
|
9
|
+
// A01: Broken Access Control (CWE-284, CWE-639, CWE-862)
|
|
10
|
+
// ============================================================
|
|
11
|
+
const brokenAccessControl: Rule[] = [
|
|
12
|
+
{
|
|
13
|
+
id: 'BAC001', name: 'Missing Authentication Check', severity: Severity.HIGH, confidence: 'medium',
|
|
14
|
+
cwe: 'CWE-862', owasp: 'A01',
|
|
15
|
+
pattern: /app\.(get|post|put|delete|patch)\s*\([^)]*(?:admin|user|account|profile|settings|dashboard)[^)]*,\s*(?!.*(?:auth|protect|guard|middleware|session|verify|check|require))/i,
|
|
16
|
+
languages: JS_TS, description: 'Route handler for sensitive endpoint may lack authentication middleware.',
|
|
17
|
+
remediation: 'Add authentication middleware before the route handler.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'BAC002', name: 'Direct Object Reference', severity: Severity.HIGH, confidence: 'medium',
|
|
21
|
+
cwe: 'CWE-639', owasp: 'A01',
|
|
22
|
+
pattern: /(?:params|query|body)\s*[\[.]\s*['"]?(?:id|userId|user_id|accountId|account_id|orderId)['"]?\s*[\])]?/i,
|
|
23
|
+
antiPattern: /(?:authorize|ownership|belongs|verify.*owner|check.*permission)/i,
|
|
24
|
+
languages: BACKEND, description: 'User-supplied ID used directly without ownership verification (IDOR risk).',
|
|
25
|
+
remediation: 'Verify the authenticated user owns or has access to the requested resource.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'BAC003', name: 'Unrestricted File Upload', severity: Severity.HIGH, confidence: 'medium',
|
|
29
|
+
cwe: 'CWE-434', owasp: 'A01',
|
|
30
|
+
pattern: /(?:multer|upload|formidable|busboy|multipart)\s*\(/i,
|
|
31
|
+
antiPattern: /(?:fileFilter|allowedTypes|whitelist|mimetype.*check|extension.*check|validateFile)/i,
|
|
32
|
+
languages: JS_TS, description: 'File upload handler without apparent file type validation.',
|
|
33
|
+
remediation: 'Validate file types, sizes, and content. Use allowlists for permitted extensions.',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'BAC004', name: 'CORS Wildcard', severity: Severity.MEDIUM, confidence: 'high',
|
|
37
|
+
cwe: 'CWE-942', owasp: 'A01',
|
|
38
|
+
pattern: /(?:Access-Control-Allow-Origin|cors\s*\()\s*[:(]\s*['"]?\*['"]?/i,
|
|
39
|
+
languages: WEB_LANGS, description: 'CORS configured with wildcard origin, allowing any domain.',
|
|
40
|
+
remediation: 'Restrict CORS to specific trusted origins.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'BAC005', name: 'Missing CSRF Protection', severity: Severity.MEDIUM, confidence: 'medium',
|
|
44
|
+
cwe: 'CWE-352', owasp: 'A01',
|
|
45
|
+
pattern: /app\.(post|put|delete|patch)\s*\(/i,
|
|
46
|
+
antiPattern: /(?:csrf|xsrf|csurf|csrfProtection|_token|antiforgery)/i,
|
|
47
|
+
languages: JS_TS, description: 'State-changing endpoint may lack CSRF protection.',
|
|
48
|
+
remediation: 'Implement CSRF tokens or use SameSite cookie attribute.',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'BAC006', name: 'Privilege Escalation Risk', severity: Severity.CRITICAL, confidence: 'medium',
|
|
52
|
+
cwe: 'CWE-269', owasp: 'A01',
|
|
53
|
+
pattern: /(?:role|isAdmin|is_admin|permission|privilege)\s*=\s*(?:req\.|request\.|params|body|query|input)/i,
|
|
54
|
+
languages: BACKEND, description: 'User role or privilege set from user-controlled input.',
|
|
55
|
+
remediation: 'Never derive authorization roles from user input. Use server-side session data.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'BAC007', name: 'JWT None Algorithm', severity: Severity.CRITICAL, confidence: 'high',
|
|
59
|
+
cwe: 'CWE-345', owasp: 'A01',
|
|
60
|
+
pattern: /algorithms?\s*:\s*\[?\s*['"]none['"]/i,
|
|
61
|
+
languages: JS_TS, description: 'JWT configured to accept "none" algorithm, bypassing signature verification.',
|
|
62
|
+
remediation: 'Never allow the "none" algorithm. Explicitly set allowed algorithms.',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: 'BAC008', name: 'Missing Authorization Decorator', severity: Severity.MEDIUM, confidence: 'medium',
|
|
66
|
+
cwe: 'CWE-862', owasp: 'A01',
|
|
67
|
+
pattern: /@(?:Get|Post|Put|Delete|Patch)\s*\(/i,
|
|
68
|
+
antiPattern: /(?:@Auth|@Authorize|@Guard|@Roles|@Permissions|@UseGuards|@Protected|@Public)/i,
|
|
69
|
+
languages: ['typescript'], description: 'Controller endpoint may lack authorization decorator.',
|
|
70
|
+
remediation: 'Add appropriate authorization guard or decorator.',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'BAC009', name: 'Insecure Redirect', severity: Severity.MEDIUM, confidence: 'medium',
|
|
74
|
+
cwe: 'CWE-601', owasp: 'A01',
|
|
75
|
+
pattern: /(?:redirect|location)\s*[\(=]\s*(?:req\.|request\.|params|query|body|input)/i,
|
|
76
|
+
languages: WEB_LANGS, description: 'Redirect URL taken from user input (open redirect vulnerability).',
|
|
77
|
+
remediation: 'Validate redirect URLs against an allowlist of trusted destinations.',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'BAC010', name: 'Directory Listing Enabled', severity: Severity.LOW, confidence: 'high',
|
|
81
|
+
cwe: 'CWE-548', owasp: 'A01',
|
|
82
|
+
pattern: /(?:express\.static|serveStatic|autoindex\s+on|Options\s+\+?Indexes)/i,
|
|
83
|
+
antiPattern: /(?:dotfiles.*deny|index.*false)/i,
|
|
84
|
+
languages: [...JS_TS, 'shell'], description: 'Static file serving may expose directory listings.',
|
|
85
|
+
remediation: 'Disable directory listing and serve only explicitly intended files.',
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
// ============================================================
|
|
90
|
+
// A02: Cryptographic Failures (CWE-327, CWE-328, CWE-330)
|
|
91
|
+
// ============================================================
|
|
92
|
+
const cryptoFailures: Rule[] = [
|
|
93
|
+
{
|
|
94
|
+
id: 'CRYPTO001', name: 'Weak Hash Algorithm (MD5)', severity: Severity.HIGH, confidence: 'high',
|
|
95
|
+
cwe: 'CWE-328', owasp: 'A02',
|
|
96
|
+
pattern: /(?:md5|MD5)\s*[\(.<]/,
|
|
97
|
+
languages: ALL_LANGS, description: 'MD5 is cryptographically broken and should not be used for security.',
|
|
98
|
+
remediation: 'Use SHA-256 or stronger for integrity checks, bcrypt/scrypt/argon2 for passwords.',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'CRYPTO002', name: 'Weak Hash Algorithm (SHA1)', severity: Severity.MEDIUM, confidence: 'high',
|
|
102
|
+
cwe: 'CWE-328', owasp: 'A02',
|
|
103
|
+
pattern: /(?:sha1|SHA1|sha-1|SHA-1)\s*[\(.<'"]/,
|
|
104
|
+
languages: ALL_LANGS, description: 'SHA-1 is deprecated for security use due to collision attacks.',
|
|
105
|
+
remediation: 'Use SHA-256 or stronger.',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'CRYPTO003', name: 'Hardcoded Encryption Key', severity: Severity.CRITICAL, confidence: 'high',
|
|
109
|
+
cwe: 'CWE-321', owasp: 'A02',
|
|
110
|
+
pattern: /(?:(?:encryption|encrypt|cipher|aes|des|secret)[-_]?key|ENCRYPTION_KEY|SECRET_KEY)\s*[:=]\s*['"][^'"]{8,}['"]/i,
|
|
111
|
+
languages: ALL_LANGS, description: 'Hardcoded encryption key found in source code.',
|
|
112
|
+
remediation: 'Store encryption keys in environment variables or a key management service.',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: 'CRYPTO004', name: 'Weak Cipher (DES/RC4)', severity: Severity.HIGH, confidence: 'high',
|
|
116
|
+
cwe: 'CWE-327', owasp: 'A02',
|
|
117
|
+
pattern: /(?:createCipher(?:iv)?\s*\(\s*['"](?:des|rc4|rc2|blowfish)|DES(?:ede)?|RC4|Blowfish)\b/i,
|
|
118
|
+
languages: ALL_LANGS, description: 'Weak or broken cipher algorithm detected.',
|
|
119
|
+
remediation: 'Use AES-256-GCM or ChaCha20-Poly1305.',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: 'CRYPTO005', name: 'ECB Mode Encryption', severity: Severity.HIGH, confidence: 'high',
|
|
123
|
+
cwe: 'CWE-327', owasp: 'A02',
|
|
124
|
+
pattern: /(?:aes.*ecb|ECB|ecb.*mode|Mode\.ECB)/i,
|
|
125
|
+
languages: ALL_LANGS, description: 'ECB mode does not provide semantic security.',
|
|
126
|
+
remediation: 'Use GCM, CBC with HMAC, or another authenticated encryption mode.',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 'CRYPTO006', name: 'Insecure Random Number', severity: Severity.HIGH, confidence: 'high',
|
|
130
|
+
cwe: 'CWE-330', owasp: 'A02',
|
|
131
|
+
pattern: /Math\.random\s*\(\)/,
|
|
132
|
+
antiPattern: /(?:test|mock|sample|example|demo|shuffle|color|animation|ui|display|css)/i,
|
|
133
|
+
languages: JS_TS, description: 'Math.random() is not cryptographically secure.',
|
|
134
|
+
remediation: 'Use crypto.randomBytes() or crypto.getRandomValues() for security-sensitive operations.',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'CRYPTO007', name: 'Insecure Random (Python)', severity: Severity.HIGH, confidence: 'medium',
|
|
138
|
+
cwe: 'CWE-330', owasp: 'A02',
|
|
139
|
+
pattern: /(?:import\s+random|from\s+random\s+import|random\.(randint|choice|random|uniform|sample))/,
|
|
140
|
+
antiPattern: /(?:test|mock|sample_data|example|seed)/i,
|
|
141
|
+
languages: ['python'], description: 'Python random module is not cryptographically secure.',
|
|
142
|
+
remediation: 'Use secrets module or os.urandom() for security-sensitive operations.',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: 'CRYPTO008', name: 'Missing HTTPS', severity: Severity.MEDIUM, confidence: 'medium',
|
|
146
|
+
cwe: 'CWE-319', owasp: 'A02',
|
|
147
|
+
pattern: /['"]http:\/\/(?!localhost|127\.0\.0\.1|0\.0\.0\.0|::1)[^'"]+['"]/,
|
|
148
|
+
languages: ALL_LANGS, description: 'Unencrypted HTTP URL detected for non-local endpoint.',
|
|
149
|
+
remediation: 'Use HTTPS for all external communications.',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: 'CRYPTO009', name: 'TLS Verification Disabled', severity: Severity.CRITICAL, confidence: 'high',
|
|
153
|
+
cwe: 'CWE-295', owasp: 'A02',
|
|
154
|
+
pattern: /(?:rejectUnauthorized\s*:\s*false|verify\s*=\s*False|InsecureSkipVerify\s*:\s*true|CURLOPT_SSL_VERIFYPEER\s*,\s*(?:false|0)|SSL_VERIFY_NONE|check_hostname\s*=\s*False)/,
|
|
155
|
+
languages: ALL_LANGS, description: 'TLS certificate verification disabled, vulnerable to MITM attacks.',
|
|
156
|
+
remediation: 'Always verify TLS certificates in production.',
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
id: 'CRYPTO010', name: 'Hardcoded IV/Nonce', severity: Severity.HIGH, confidence: 'medium',
|
|
160
|
+
cwe: 'CWE-329', owasp: 'A02',
|
|
161
|
+
pattern: /(?:iv|nonce|IV|NONCE)\s*[:=]\s*(?:['"][^'"]{8,}['"]|Buffer\.from\s*\(\s*['"])/,
|
|
162
|
+
languages: ALL_LANGS, description: 'Hardcoded initialization vector or nonce found.',
|
|
163
|
+
remediation: 'Generate a unique random IV/nonce for each encryption operation.',
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: 'CRYPTO011', name: 'Weak Password Hashing', severity: Severity.HIGH, confidence: 'high',
|
|
167
|
+
cwe: 'CWE-916', owasp: 'A02',
|
|
168
|
+
pattern: /(?:createHash\s*\(\s*['"](?:md5|sha1|sha256)['"]|hashlib\.(?:md5|sha1|sha256))\s*[(.]/,
|
|
169
|
+
antiPattern: /(?:hmac|pbkdf2|checksum|file.*hash|integrity)/i,
|
|
170
|
+
languages: [...JS_TS, 'python'], description: 'Plain hash used for password storage. Vulnerable to rainbow table attacks.',
|
|
171
|
+
remediation: 'Use bcrypt, scrypt, or argon2 for password hashing.',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: 'CRYPTO012', name: 'Insufficient Key Length', severity: Severity.MEDIUM, confidence: 'medium',
|
|
175
|
+
cwe: 'CWE-326', owasp: 'A02',
|
|
176
|
+
pattern: /(?:generateKeyPair|createRSAKey|RSA.*bits|keySize)\s*[:(]\s*(?:512|768|1024)\b/,
|
|
177
|
+
languages: ALL_LANGS, description: 'RSA key length below recommended minimum of 2048 bits.',
|
|
178
|
+
remediation: 'Use at least 2048-bit RSA keys, or prefer 4096-bit.',
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
// ============================================================
|
|
183
|
+
// A03: Injection (CWE-79, CWE-89, CWE-78, CWE-90, CWE-917)
|
|
184
|
+
// ============================================================
|
|
185
|
+
const injection: Rule[] = [
|
|
186
|
+
{
|
|
187
|
+
id: 'INJ001', name: 'SQL Injection (String Concat)', severity: Severity.CRITICAL, confidence: 'high',
|
|
188
|
+
cwe: 'CWE-89', owasp: 'A03',
|
|
189
|
+
pattern: /(?:query|execute|exec|raw)\s*\(\s*['"`](?:SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|UNION)\s[^'"`]*['"`]\s*\+/i,
|
|
190
|
+
languages: BACKEND, description: 'SQL query built with string concatenation — SQL injection risk.',
|
|
191
|
+
remediation: 'Use parameterized queries or prepared statements.',
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: 'INJ002', name: 'SQL Injection (Template Literal)', severity: Severity.CRITICAL, confidence: 'high',
|
|
195
|
+
cwe: 'CWE-89', owasp: 'A03',
|
|
196
|
+
pattern: /(?:query|execute|exec|raw)\s*\(\s*`(?:SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|CREATE)\s[^`]*\$\{/i,
|
|
197
|
+
languages: JS_TS, description: 'SQL query built with template literals — SQL injection risk.',
|
|
198
|
+
remediation: 'Use parameterized queries or prepared statements.',
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: 'INJ003', name: 'SQL Injection (f-string)', severity: Severity.CRITICAL, confidence: 'high',
|
|
202
|
+
cwe: 'CWE-89', owasp: 'A03',
|
|
203
|
+
pattern: /(?:execute|cursor\.execute|fetchall|fetchone)\s*\(\s*f['"](?:SELECT|INSERT|UPDATE|DELETE)\s/i,
|
|
204
|
+
languages: ['python'], description: 'SQL query built with f-string — SQL injection risk.',
|
|
205
|
+
remediation: 'Use parameterized queries with placeholders.',
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
id: 'INJ004', name: 'SQL Injection (format)', severity: Severity.CRITICAL, confidence: 'high',
|
|
209
|
+
cwe: 'CWE-89', owasp: 'A03',
|
|
210
|
+
pattern: /(?:execute|cursor)\s*\(\s*['"](?:SELECT|INSERT|UPDATE|DELETE)\s[^'"]*['"]\.format\s*\(/i,
|
|
211
|
+
languages: ['python'], description: 'SQL query built with .format() — SQL injection risk.',
|
|
212
|
+
remediation: 'Use parameterized queries with %s placeholders.',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: 'INJ005', name: 'Command Injection', severity: Severity.CRITICAL, confidence: 'high',
|
|
216
|
+
cwe: 'CWE-78', owasp: 'A03',
|
|
217
|
+
pattern: /(?:exec|execSync|spawn|spawnSync|execFile|child_process)\s*\(\s*(?:`[^`]*\$\{|['"][^'"]*['"\s]*\+\s*(?:req|input|param|arg|user|query|body))/i,
|
|
218
|
+
languages: JS_TS, description: 'Command built with user input — OS command injection risk.',
|
|
219
|
+
remediation: 'Use execFile with argument arrays. Never interpolate user input into commands.',
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: 'INJ006', name: 'Command Injection (Python)', severity: Severity.CRITICAL, confidence: 'high',
|
|
223
|
+
cwe: 'CWE-78', owasp: 'A03',
|
|
224
|
+
pattern: /(?:os\.system|os\.popen|subprocess\.call|subprocess\.run|subprocess\.Popen)\s*\(\s*(?:f['"]|['"].*['"].*\+|.*\.format\s*\(|.*%\s)/,
|
|
225
|
+
languages: ['python'], description: 'Command built with user input — OS command injection risk.',
|
|
226
|
+
remediation: 'Use subprocess with shell=False and pass arguments as a list.',
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
id: 'INJ007', name: 'XSS (innerHTML)', severity: Severity.HIGH, confidence: 'high',
|
|
230
|
+
cwe: 'CWE-79', owasp: 'A03',
|
|
231
|
+
pattern: /\.innerHTML\s*=\s*(?!['"]<(?:br|hr|p|div|span)\s*\/?>['"])/,
|
|
232
|
+
languages: JS_TS, description: 'Setting innerHTML with dynamic content creates XSS risk.',
|
|
233
|
+
remediation: 'Use textContent, or sanitize HTML with DOMPurify.',
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
id: 'INJ008', name: 'XSS (document.write)', severity: Severity.HIGH, confidence: 'high',
|
|
237
|
+
cwe: 'CWE-79', owasp: 'A03',
|
|
238
|
+
pattern: /document\.write\s*\(/,
|
|
239
|
+
languages: JS_TS, description: 'document.write can introduce XSS vulnerabilities.',
|
|
240
|
+
remediation: 'Use DOM manipulation methods instead of document.write.',
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
id: 'INJ009', name: 'XSS (dangerouslySetInnerHTML)', severity: Severity.HIGH, confidence: 'medium',
|
|
244
|
+
cwe: 'CWE-79', owasp: 'A03',
|
|
245
|
+
pattern: /dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:/,
|
|
246
|
+
antiPattern: /(?:sanitize|purify|DOMPurify|xss|escape)/i,
|
|
247
|
+
languages: JS_TS, description: 'React dangerouslySetInnerHTML used without sanitization.',
|
|
248
|
+
remediation: 'Sanitize content with DOMPurify before using dangerouslySetInnerHTML.',
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: 'INJ010', name: 'Eval Usage', severity: Severity.CRITICAL, confidence: 'high',
|
|
252
|
+
cwe: 'CWE-95', owasp: 'A03',
|
|
253
|
+
pattern: /\beval\s*\(\s*(?!['"][^'"]*['"])/,
|
|
254
|
+
languages: [...JS_TS, 'python', 'php', 'ruby'], description: 'eval() with dynamic input enables code injection.',
|
|
255
|
+
remediation: 'Avoid eval(). Use JSON.parse() for data, or safe alternatives.',
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
id: 'INJ011', name: 'LDAP Injection', severity: Severity.HIGH, confidence: 'medium',
|
|
259
|
+
cwe: 'CWE-90', owasp: 'A03',
|
|
260
|
+
pattern: /(?:ldap|LDAP).*(?:search|bind|modify)\s*\(.*(?:\+|`|\$\{|\.format|%s)/,
|
|
261
|
+
languages: BACKEND, description: 'LDAP query built with string concatenation — injection risk.',
|
|
262
|
+
remediation: 'Use parameterized LDAP queries and escape special characters.',
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: 'INJ012', name: 'NoSQL Injection', severity: Severity.HIGH, confidence: 'medium',
|
|
266
|
+
cwe: 'CWE-943', owasp: 'A03',
|
|
267
|
+
pattern: /(?:find|findOne|findMany|deleteOne|deleteMany|updateOne|updateMany|aggregate)\s*\(\s*(?:req\.body|req\.query|req\.params|request\.)/,
|
|
268
|
+
languages: JS_TS, description: 'MongoDB query using unsanitized user input — NoSQL injection risk.',
|
|
269
|
+
remediation: 'Validate and sanitize input. Use explicit field queries instead of passing raw input.',
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
id: 'INJ013', name: 'Template Injection (SSTI)', severity: Severity.CRITICAL, confidence: 'medium',
|
|
273
|
+
cwe: 'CWE-1336', owasp: 'A03',
|
|
274
|
+
pattern: /(?:render_template_string|Template\s*\(|Jinja2|nunjucks\.renderString|ejs\.render)\s*\(\s*(?:req|request|input|user|param|query|body)/i,
|
|
275
|
+
languages: [...JS_TS, 'python'], description: 'User input used directly in template rendering — SSTI risk.',
|
|
276
|
+
remediation: 'Never pass user input as template source. Use template files with data binding.',
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
id: 'INJ014', name: 'XPath Injection', severity: Severity.HIGH, confidence: 'medium',
|
|
280
|
+
cwe: 'CWE-643', owasp: 'A03',
|
|
281
|
+
pattern: /(?:xpath|XPath|selectNodes?|evaluate)\s*\(.*(?:\+|`|\$\{|\.format)/,
|
|
282
|
+
languages: BACKEND, description: 'XPath query built with string concatenation — injection risk.',
|
|
283
|
+
remediation: 'Use parameterized XPath queries.',
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
id: 'INJ015', name: 'RegExp Injection (ReDoS)', severity: Severity.MEDIUM, confidence: 'medium',
|
|
287
|
+
cwe: 'CWE-1333', owasp: 'A03',
|
|
288
|
+
pattern: /new\s+RegExp\s*\(\s*(?:req\.|request\.|input|user|param|query|body|arg)/i,
|
|
289
|
+
languages: JS_TS, description: 'RegExp created from user input — ReDoS risk.',
|
|
290
|
+
remediation: 'Validate and escape user input before using in RegExp, or use RE2.',
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
id: 'INJ016', name: 'Header Injection', severity: Severity.MEDIUM, confidence: 'medium',
|
|
294
|
+
cwe: 'CWE-113', owasp: 'A03',
|
|
295
|
+
pattern: /(?:setHeader|writeHead|header)\s*\(\s*['"][^'"]+['"]\s*,\s*(?:req\.|request\.|input|user|param|query|body)/i,
|
|
296
|
+
languages: WEB_LANGS, description: 'HTTP header value from user input — header injection risk.',
|
|
297
|
+
remediation: 'Validate and sanitize values before setting HTTP headers.',
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
id: 'INJ017', name: 'Code Injection (Function constructor)', severity: Severity.CRITICAL, confidence: 'high',
|
|
301
|
+
cwe: 'CWE-94', owasp: 'A03',
|
|
302
|
+
pattern: /new\s+Function\s*\(\s*(?!['"][^'"]*['"]\s*\))/,
|
|
303
|
+
languages: JS_TS, description: 'Function constructor with dynamic input is equivalent to eval().',
|
|
304
|
+
remediation: 'Avoid the Function constructor. Use safe alternatives.',
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
id: 'INJ018', name: 'Shell Injection via shell:true', severity: Severity.HIGH, confidence: 'high',
|
|
308
|
+
cwe: 'CWE-78', owasp: 'A03',
|
|
309
|
+
pattern: /(?:spawn|exec)\s*\([^)]*\{[^}]*shell\s*:\s*true/,
|
|
310
|
+
languages: JS_TS, description: 'Using shell:true with spawn/exec enables shell injection.',
|
|
311
|
+
remediation: 'Use spawn without shell:true, pass arguments as array.',
|
|
312
|
+
},
|
|
313
|
+
];
|
|
314
|
+
|
|
315
|
+
// ============================================================
|
|
316
|
+
// A04: Insecure Design (CWE-209, CWE-256, CWE-501)
|
|
317
|
+
// ============================================================
|
|
318
|
+
const insecureDesign: Rule[] = [
|
|
319
|
+
{
|
|
320
|
+
id: 'DES001', name: 'Missing Rate Limiting', severity: Severity.MEDIUM, confidence: 'low',
|
|
321
|
+
cwe: 'CWE-770', owasp: 'A04',
|
|
322
|
+
pattern: /app\.(post|put)\s*\(\s*['"]\/(?:login|signin|auth|register|signup|password|reset|api\/token)['"]/i,
|
|
323
|
+
antiPattern: /(?:rateLimit|rate-limit|throttle|brute|limiter)/i,
|
|
324
|
+
languages: JS_TS, description: 'Authentication endpoint without apparent rate limiting.',
|
|
325
|
+
remediation: 'Add rate limiting middleware (e.g., express-rate-limit) to auth endpoints.',
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
id: 'DES002', name: 'Error Details Exposed', severity: Severity.MEDIUM, confidence: 'medium',
|
|
329
|
+
cwe: 'CWE-209', owasp: 'A04',
|
|
330
|
+
pattern: /(?:res\.(?:send|json|status)\s*\(|response\.)\s*.*(?:err\.stack|error\.stack|stackTrace|stack_trace|traceback)/i,
|
|
331
|
+
languages: WEB_LANGS, description: 'Stack trace or error details sent to client.',
|
|
332
|
+
remediation: 'Log detailed errors server-side, return generic messages to clients.',
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
id: 'DES003', name: 'Missing Input Validation', severity: Severity.MEDIUM, confidence: 'low',
|
|
336
|
+
cwe: 'CWE-20', owasp: 'A04',
|
|
337
|
+
pattern: /(?:req\.body|req\.query|req\.params)\s*\.\s*\w+/,
|
|
338
|
+
antiPattern: /(?:validate|joi|yup|zod|express-validator|class-validator|sanitize|check\(|body\(|param\(|query\()/i,
|
|
339
|
+
languages: JS_TS, description: 'Request input used without apparent validation.',
|
|
340
|
+
remediation: 'Use input validation libraries like Joi, Zod, or express-validator.',
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
id: 'DES004', name: 'Mass Assignment', severity: Severity.HIGH, confidence: 'medium',
|
|
344
|
+
cwe: 'CWE-915', owasp: 'A04',
|
|
345
|
+
pattern: /(?:\.create|\.update|\.findAndUpdate|\.insertOne|Model\.)\s*\(\s*(?:req\.body|request\.body|\.\.\.req\.body|Object\.assign.*req\.body)/,
|
|
346
|
+
languages: JS_TS, description: 'Entire request body passed to database operation — mass assignment risk.',
|
|
347
|
+
remediation: 'Explicitly pick allowed fields from the request body.',
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
id: 'DES005', name: 'Verbose Error Mode in Production', severity: Severity.MEDIUM, confidence: 'medium',
|
|
351
|
+
cwe: 'CWE-209', owasp: 'A04',
|
|
352
|
+
pattern: /(?:DEBUG\s*=\s*True|debug\s*:\s*true|NODE_ENV.*development|showStackError\s*:\s*true)/,
|
|
353
|
+
antiPattern: /(?:process\.env|if\s*\(|test|spec|\.env\.example)/i,
|
|
354
|
+
languages: ALL_LANGS, description: 'Debug/verbose error mode may be enabled in production.',
|
|
355
|
+
remediation: 'Ensure debug mode is disabled in production environments.',
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
id: 'DES006', name: 'Unrestricted Resource Consumption', severity: Severity.MEDIUM, confidence: 'medium',
|
|
359
|
+
cwe: 'CWE-400', owasp: 'A04',
|
|
360
|
+
pattern: /(?:bodyParser\.json\(\s*\)|express\.json\(\s*\))/,
|
|
361
|
+
antiPattern: /limit/,
|
|
362
|
+
languages: JS_TS, description: 'JSON body parser without size limit — denial of service risk.',
|
|
363
|
+
remediation: 'Set a body size limit: express.json({ limit: "100kb" })',
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
id: 'DES007', name: 'Missing Timeout', severity: Severity.LOW, confidence: 'low',
|
|
367
|
+
cwe: 'CWE-400', owasp: 'A04',
|
|
368
|
+
pattern: /(?:fetch|axios|http\.request|urllib|requests\.(?:get|post)|HttpClient)\s*\(/,
|
|
369
|
+
antiPattern: /(?:timeout|signal|AbortController)/i,
|
|
370
|
+
languages: BACKEND, description: 'HTTP request without timeout configuration.',
|
|
371
|
+
remediation: 'Always set timeouts on external HTTP requests.',
|
|
372
|
+
},
|
|
373
|
+
];
|
|
374
|
+
|
|
375
|
+
// ============================================================
|
|
376
|
+
// A05: Security Misconfiguration (CWE-16, CWE-1004, CWE-614)
|
|
377
|
+
// ============================================================
|
|
378
|
+
const misconfig: Rule[] = [
|
|
379
|
+
{
|
|
380
|
+
id: 'CFG001', name: 'Debug Mode Enabled', severity: Severity.MEDIUM, confidence: 'medium',
|
|
381
|
+
cwe: 'CWE-489', owasp: 'A05',
|
|
382
|
+
pattern: /(?:app\.debug\s*=\s*True|DEBUG\s*=\s*True|debug:\s*true|EnableDebugging|SetDebug\(true\))/,
|
|
383
|
+
antiPattern: /(?:process\.env|os\.environ|config\.get|if\s|\.env)/,
|
|
384
|
+
languages: ALL_LANGS, description: 'Application debug mode appears to be hardcoded as enabled.',
|
|
385
|
+
remediation: 'Use environment variable for debug mode, ensure disabled in production.',
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
id: 'CFG002', name: 'Default Credentials', severity: Severity.CRITICAL, confidence: 'high',
|
|
389
|
+
cwe: 'CWE-798', owasp: 'A05',
|
|
390
|
+
pattern: /(?:password|passwd|pwd)\s*[:=]\s*['"](?:admin|password|123456|root|default|test|guest|letmein|welcome|monkey|dragon|master|qwerty|abc123)['"]/i,
|
|
391
|
+
languages: ALL_LANGS, description: 'Default or common password found in source code.',
|
|
392
|
+
remediation: 'Remove hardcoded credentials. Use environment variables or secret management.',
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
id: 'CFG003', name: 'Insecure Cookie', severity: Severity.MEDIUM, confidence: 'high',
|
|
396
|
+
cwe: 'CWE-614', owasp: 'A05',
|
|
397
|
+
pattern: /(?:secure\s*:\s*false|httpOnly\s*:\s*false|sameSite\s*:\s*['"]?none['"]?)/i,
|
|
398
|
+
languages: JS_TS, description: 'Cookie configured with insecure flags.',
|
|
399
|
+
remediation: 'Set secure: true, httpOnly: true, sameSite: "strict" for session cookies.',
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
id: 'CFG004', name: 'Missing Security Headers', severity: Severity.LOW, confidence: 'low',
|
|
403
|
+
cwe: 'CWE-693', owasp: 'A05',
|
|
404
|
+
pattern: /(?:app\.listen|createServer|express\(\))/,
|
|
405
|
+
antiPattern: /(?:helmet|security.*header|X-Content-Type|X-Frame-Options|Content-Security-Policy|Strict-Transport|csp)/i,
|
|
406
|
+
languages: JS_TS, description: 'Web server may lack security headers.',
|
|
407
|
+
remediation: 'Use Helmet.js or manually set security headers (CSP, HSTS, X-Frame-Options).',
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
id: 'CFG005', name: 'Exposed GraphQL Introspection', severity: Severity.MEDIUM, confidence: 'high',
|
|
411
|
+
cwe: 'CWE-200', owasp: 'A05',
|
|
412
|
+
pattern: /introspection\s*:\s*true/,
|
|
413
|
+
languages: JS_TS, description: 'GraphQL introspection enabled — schema exposed to attackers.',
|
|
414
|
+
remediation: 'Disable introspection in production.',
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
id: 'CFG006', name: 'Exposed .env File', severity: Severity.HIGH, confidence: 'high',
|
|
418
|
+
cwe: 'CWE-538', owasp: 'A05',
|
|
419
|
+
pattern: /express\.static\s*\(\s*['"]\.?\/?['"]\s*\)|serveStatic\s*\(\s*['"]\.?\/?['"]/,
|
|
420
|
+
languages: JS_TS, description: 'Serving files from root directory may expose .env and other sensitive files.',
|
|
421
|
+
remediation: 'Serve static files from a dedicated public directory.',
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
id: 'CFG007', name: 'Wildcard CORS Origin', severity: Severity.MEDIUM, confidence: 'high',
|
|
425
|
+
cwe: 'CWE-942', owasp: 'A05',
|
|
426
|
+
pattern: /origin\s*:\s*(?:true|['"]?\*['"]?|\(\s*_\s*,\s*callback\s*\)\s*=>\s*callback\s*\(\s*null\s*,\s*true\s*\))/,
|
|
427
|
+
languages: JS_TS, description: 'CORS allows all origins, enabling cross-origin attacks.',
|
|
428
|
+
remediation: 'Restrict CORS to specific trusted origins.',
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
id: 'CFG008', name: 'Verbose Server Banner', severity: Severity.LOW, confidence: 'medium',
|
|
432
|
+
cwe: 'CWE-200', owasp: 'A05',
|
|
433
|
+
pattern: /(?:x-powered-by|server:\s|Server:\s|expose_php)/i,
|
|
434
|
+
antiPattern: /(?:disable|remove|false|off)/i,
|
|
435
|
+
languages: ALL_LANGS, description: 'Server version information may be exposed in headers.',
|
|
436
|
+
remediation: 'Remove or disable server identification headers.',
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
id: 'CFG009', name: 'Permissive File Permissions', severity: Severity.MEDIUM, confidence: 'medium',
|
|
440
|
+
cwe: 'CWE-732', owasp: 'A05',
|
|
441
|
+
pattern: /(?:chmod\s+(?:777|666|755.*sensitive)|os\.chmod.*0o?777|writeFile.*mode.*0o?777)/,
|
|
442
|
+
languages: ALL_LANGS, description: 'Overly permissive file permissions (world-writable).',
|
|
443
|
+
remediation: 'Use restrictive permissions. Files: 644, directories: 755, secrets: 600.',
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
id: 'CFG010', name: 'Binding to All Interfaces', severity: Severity.LOW, confidence: 'medium',
|
|
447
|
+
cwe: 'CWE-668', owasp: 'A05',
|
|
448
|
+
pattern: /(?:listen\s*\(\s*(?:\d+\s*,\s*)?['"]0\.0\.0\.0['"]|host\s*[:=]\s*['"]0\.0\.0\.0['"]|INADDR_ANY)/,
|
|
449
|
+
languages: ALL_LANGS, description: 'Server binding to all network interfaces.',
|
|
450
|
+
remediation: 'Bind to localhost (127.0.0.1) in development. Use firewall rules in production.',
|
|
451
|
+
},
|
|
452
|
+
];
|
|
453
|
+
|
|
454
|
+
// ============================================================
|
|
455
|
+
// A06: Vulnerable Components (CWE-1035, CWE-1104)
|
|
456
|
+
// ============================================================
|
|
457
|
+
const vulnerableComponents: Rule[] = [
|
|
458
|
+
{
|
|
459
|
+
id: 'DEP001', name: 'Known Vulnerable Package (lodash)', severity: Severity.MEDIUM, confidence: 'medium',
|
|
460
|
+
cwe: 'CWE-1035', owasp: 'A06',
|
|
461
|
+
pattern: /['"]lodash['"]\s*:\s*['"][^'"]*(?:^[0-3]\.|4\.(?:0|1[0-6])\.)/,
|
|
462
|
+
languages: ALL_LANGS, description: 'Potentially outdated lodash version with known prototype pollution vulnerabilities.',
|
|
463
|
+
remediation: 'Update to latest lodash version.',
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
id: 'DEP002', name: 'Known Vulnerable Package (jQuery)', severity: Severity.MEDIUM, confidence: 'medium',
|
|
467
|
+
cwe: 'CWE-1035', owasp: 'A06',
|
|
468
|
+
pattern: /(?:jquery.*(?:1\.\d|2\.[012])|cdn.*jquery.*(?:1\.\d|2\.[012]))/i,
|
|
469
|
+
languages: [...JS_TS, 'html'], description: 'Outdated jQuery version with known XSS vulnerabilities.',
|
|
470
|
+
remediation: 'Update to jQuery 3.5+ or remove jQuery dependency.',
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
id: 'DEP003', name: 'Pinned to Vulnerable Version', severity: Severity.LOW, confidence: 'low',
|
|
474
|
+
cwe: 'CWE-1104', owasp: 'A06',
|
|
475
|
+
pattern: /['"](?:express|axios|socket\.io|mongoose|sequelize)['"]\s*:\s*['"]\d+\.\d+\.\d+['"]/,
|
|
476
|
+
languages: ALL_LANGS, description: 'Dependency pinned to exact version — may miss security patches.',
|
|
477
|
+
remediation: 'Use caret (^) or tilde (~) version ranges to receive patch updates.',
|
|
478
|
+
},
|
|
479
|
+
];
|
|
480
|
+
|
|
481
|
+
// ============================================================
|
|
482
|
+
// A07: Authentication Failures (CWE-287, CWE-256, CWE-521)
|
|
483
|
+
// ============================================================
|
|
484
|
+
const authFailures: Rule[] = [
|
|
485
|
+
{
|
|
486
|
+
id: 'AUTH001', name: 'Weak Password Policy', severity: Severity.MEDIUM, confidence: 'medium',
|
|
487
|
+
cwe: 'CWE-521', owasp: 'A07',
|
|
488
|
+
pattern: /(?:password|passwd).*(?:length|len|min).*(?:[1-5]|minLength\s*[:=]\s*[1-5])\b/i,
|
|
489
|
+
languages: ALL_LANGS, description: 'Password minimum length too short (should be 8+ characters).',
|
|
490
|
+
remediation: 'Enforce minimum 8-character passwords with complexity requirements.',
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
id: 'AUTH002', name: 'Missing Password Hashing', severity: Severity.CRITICAL, confidence: 'medium',
|
|
494
|
+
cwe: 'CWE-256', owasp: 'A07',
|
|
495
|
+
pattern: /(?:password|passwd)\s*[:=]\s*(?:req\.|request\.|input|body)\.[^;]*(?:save|create|insert|update)\s*\(/i,
|
|
496
|
+
antiPattern: /(?:hash|bcrypt|scrypt|argon|pbkdf|encrypt|crypt)/i,
|
|
497
|
+
languages: BACKEND, description: 'Password appears to be stored without hashing.',
|
|
498
|
+
remediation: 'Hash passwords with bcrypt, scrypt, or argon2 before storage.',
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
id: 'AUTH003', name: 'Hardcoded JWT Secret', severity: Severity.CRITICAL, confidence: 'high',
|
|
502
|
+
cwe: 'CWE-798', owasp: 'A07',
|
|
503
|
+
pattern: /(?:jwt|jsonwebtoken)\.(?:sign|verify)\s*\([^)]*,\s*['"][^'"]{4,}['"]/i,
|
|
504
|
+
languages: JS_TS, description: 'JWT secret hardcoded in source code.',
|
|
505
|
+
remediation: 'Store JWT secret in environment variable.',
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
id: 'AUTH004', name: 'JWT Without Expiration', severity: Severity.MEDIUM, confidence: 'medium',
|
|
509
|
+
cwe: 'CWE-613', owasp: 'A07',
|
|
510
|
+
pattern: /jwt\.sign\s*\(\s*\{[^}]*\}\s*,/,
|
|
511
|
+
antiPattern: /(?:expiresIn|exp:|maxAge)/i,
|
|
512
|
+
languages: JS_TS, description: 'JWT token created without expiration time.',
|
|
513
|
+
remediation: 'Always set token expiration with expiresIn option.',
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
id: 'AUTH005', name: 'Session Fixation', severity: Severity.HIGH, confidence: 'medium',
|
|
517
|
+
cwe: 'CWE-384', owasp: 'A07',
|
|
518
|
+
pattern: /(?:login|authenticate|signIn)\s*(?:=|:).*\{/i,
|
|
519
|
+
antiPattern: /(?:regenerate|destroy.*session|req\.session\.regenerate|session\.invalidate)/i,
|
|
520
|
+
languages: WEB_LANGS, description: 'Login handler may not regenerate session ID — session fixation risk.',
|
|
521
|
+
remediation: 'Regenerate session ID after successful authentication.',
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
id: 'AUTH006', name: 'Plaintext Password in URL', severity: Severity.HIGH, confidence: 'high',
|
|
525
|
+
cwe: 'CWE-598', owasp: 'A07',
|
|
526
|
+
pattern: /(?:url|href|redirect|link).*[?&](?:password|passwd|pwd|secret|token)=/i,
|
|
527
|
+
languages: ALL_LANGS, description: 'Sensitive data passed in URL query parameters.',
|
|
528
|
+
remediation: 'Send sensitive data in request body or headers, never in URLs.',
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
id: 'AUTH007', name: 'Missing Account Lockout', severity: Severity.MEDIUM, confidence: 'low',
|
|
532
|
+
cwe: 'CWE-307', owasp: 'A07',
|
|
533
|
+
pattern: /(?:login|authenticate|signIn|signin)\s*(?:=|:|\().*(?:password|credential)/i,
|
|
534
|
+
antiPattern: /(?:lockout|maxAttempts|max_attempts|failedAttempts|failed_attempts|brute|throttle|rateLimit)/i,
|
|
535
|
+
languages: BACKEND, description: 'Login function without apparent brute force protection.',
|
|
536
|
+
remediation: 'Implement account lockout or progressive delays after failed attempts.',
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
id: 'AUTH008', name: 'Insecure Remember Me', severity: Severity.MEDIUM, confidence: 'medium',
|
|
540
|
+
cwe: 'CWE-613', owasp: 'A07',
|
|
541
|
+
pattern: /(?:remember.*me|rememberMe|keep.*logged|persistent.*session).*(?:cookie|token)/i,
|
|
542
|
+
antiPattern: /(?:secure|httpOnly|signed|encrypted)/i,
|
|
543
|
+
languages: WEB_LANGS, description: 'Remember-me functionality may use insecure token storage.',
|
|
544
|
+
remediation: 'Use secure, httpOnly, signed tokens for persistent sessions.',
|
|
545
|
+
},
|
|
546
|
+
];
|
|
547
|
+
|
|
548
|
+
// ============================================================
|
|
549
|
+
// A08: Data Integrity Failures (CWE-502, CWE-829)
|
|
550
|
+
// ============================================================
|
|
551
|
+
const dataIntegrity: Rule[] = [
|
|
552
|
+
{
|
|
553
|
+
id: 'SER001', name: 'Insecure Deserialization (JS)', severity: Severity.CRITICAL, confidence: 'high',
|
|
554
|
+
cwe: 'CWE-502', owasp: 'A08',
|
|
555
|
+
pattern: /(?:serialize|node-serialize|js-yaml\.load(?!.*safe)|unserialize|phpunserialize)\s*\(/i,
|
|
556
|
+
languages: [...JS_TS, 'php'], description: 'Insecure deserialization of potentially untrusted data.',
|
|
557
|
+
remediation: 'Use safe deserialization methods (js-yaml.safeLoad, JSON.parse for data).',
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
id: 'SER002', name: 'Insecure Deserialization (Python)', severity: Severity.CRITICAL, confidence: 'high',
|
|
561
|
+
cwe: 'CWE-502', owasp: 'A08',
|
|
562
|
+
pattern: /(?:pickle\.loads?|yaml\.(?:load|unsafe_load)|marshal\.loads?|shelve\.open)\s*\(/,
|
|
563
|
+
antiPattern: /(?:safe_load|SafeLoader|Loader=yaml\.SafeLoader)/,
|
|
564
|
+
languages: ['python'], description: 'Insecure deserialization — arbitrary code execution risk.',
|
|
565
|
+
remediation: 'Use yaml.safe_load(), avoid pickle for untrusted data, use JSON.',
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
id: 'SER003', name: 'Insecure Deserialization (Java)', severity: Severity.CRITICAL, confidence: 'high',
|
|
569
|
+
cwe: 'CWE-502', owasp: 'A08',
|
|
570
|
+
pattern: /(?:ObjectInputStream|readObject|XMLDecoder|XStream|Kryo|Hessian)\s*[\.(]/,
|
|
571
|
+
languages: ['java', 'kotlin'], description: 'Java deserialization of potentially untrusted data.',
|
|
572
|
+
remediation: 'Use allowlist-based deserialization filter or avoid native serialization.',
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
id: 'SER004', name: 'Untrusted CDN/Script', severity: Severity.MEDIUM, confidence: 'medium',
|
|
576
|
+
cwe: 'CWE-829', owasp: 'A08',
|
|
577
|
+
pattern: /<script\s+src\s*=\s*['"]https?:\/\/(?!(?:cdn\.jsdelivr|cdnjs\.cloudflare|unpkg|ajax\.googleapis))/i,
|
|
578
|
+
antiPattern: /integrity\s*=/i,
|
|
579
|
+
languages: ['html'], description: 'External script loaded without Subresource Integrity (SRI).',
|
|
580
|
+
remediation: 'Add integrity and crossorigin attributes to external script tags.',
|
|
581
|
+
},
|
|
582
|
+
];
|
|
583
|
+
|
|
584
|
+
// ============================================================
|
|
585
|
+
// A09: Security Logging & Monitoring Failures (CWE-778)
|
|
586
|
+
// ============================================================
|
|
587
|
+
const loggingFailures: Rule[] = [
|
|
588
|
+
{
|
|
589
|
+
id: 'LOG001', name: 'Sensitive Data in Logs', severity: Severity.HIGH, confidence: 'medium',
|
|
590
|
+
cwe: 'CWE-532', owasp: 'A09',
|
|
591
|
+
pattern: /(?:console\.log|logger?\.\w+|print|println|System\.out|logging\.\w+)\s*\([^)]*(?:password|passwd|secret|token|apiKey|api_key|credit.?card|ssn|social.?security)[^)]*\)/i,
|
|
592
|
+
languages: ALL_LANGS, description: 'Sensitive data may be written to logs.',
|
|
593
|
+
remediation: 'Mask or redact sensitive data before logging.',
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
id: 'LOG002', name: 'Log Injection', severity: Severity.MEDIUM, confidence: 'medium',
|
|
597
|
+
cwe: 'CWE-117', owasp: 'A09',
|
|
598
|
+
pattern: /(?:console\.log|logger?\.\w+|logging\.\w+)\s*\(\s*(?:`[^`]*\$\{(?:req|request|input|user)|['"][^'"]*['"]\s*\+\s*(?:req|request|input|user))/i,
|
|
599
|
+
languages: BACKEND, description: 'User input written to logs without sanitization — log injection risk.',
|
|
600
|
+
remediation: 'Sanitize user input before logging. Remove newlines and control characters.',
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
id: 'LOG003', name: 'Console.log in Production', severity: Severity.LOW, confidence: 'low',
|
|
604
|
+
cwe: 'CWE-489', owasp: 'A09',
|
|
605
|
+
pattern: /console\.(?:log|debug|trace)\s*\(/,
|
|
606
|
+
antiPattern: /(?:test|spec|debug|dev|\.test\.|\.spec\.|__test)/i,
|
|
607
|
+
languages: JS_TS, description: 'Console.log statements may leak information in production.',
|
|
608
|
+
remediation: 'Use a proper logging library with level control.',
|
|
609
|
+
},
|
|
610
|
+
];
|
|
611
|
+
|
|
612
|
+
// ============================================================
|
|
613
|
+
// A10: Server-Side Request Forgery (CWE-918)
|
|
614
|
+
// ============================================================
|
|
615
|
+
const ssrf: Rule[] = [
|
|
616
|
+
{
|
|
617
|
+
id: 'SSRF001', name: 'SSRF via User URL', severity: Severity.HIGH, confidence: 'medium',
|
|
618
|
+
cwe: 'CWE-918', owasp: 'A10',
|
|
619
|
+
pattern: /(?:fetch|axios|http\.get|https\.get|request|urllib|requests\.get|HttpClient\.get)\s*\(\s*(?:req\.|request\.|input|user|param|query|body|arg|url|uri)/i,
|
|
620
|
+
languages: BACKEND, description: 'HTTP request URL from user input — SSRF risk.',
|
|
621
|
+
remediation: 'Validate and allowlist URLs. Block internal/private IP ranges.',
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
id: 'SSRF002', name: 'DNS Rebinding Risk', severity: Severity.MEDIUM, confidence: 'low',
|
|
625
|
+
cwe: 'CWE-918', owasp: 'A10',
|
|
626
|
+
pattern: /(?:fetch|axios|request)\s*\(\s*(?:url|uri|endpoint|target|destination)\s*[,)]/i,
|
|
627
|
+
antiPattern: /(?:allowlist|whitelist|validateUrl|isAllowed|checkUrl|blockPrivate)/i,
|
|
628
|
+
languages: BACKEND, description: 'HTTP request to variable URL without apparent validation.',
|
|
629
|
+
remediation: 'Validate URL and resolved IP against allowlist. Check for DNS rebinding.',
|
|
630
|
+
},
|
|
631
|
+
];
|
|
632
|
+
|
|
633
|
+
// ============================================================
|
|
634
|
+
// Additional: Secrets & Hardcoded Credentials
|
|
635
|
+
// ============================================================
|
|
636
|
+
const secrets: Rule[] = [
|
|
637
|
+
{
|
|
638
|
+
id: 'SEC001', name: 'AWS Access Key', severity: Severity.CRITICAL, confidence: 'high',
|
|
639
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
640
|
+
pattern: /(?:AKIA|ASIA)[A-Z0-9]{16}/,
|
|
641
|
+
antiPattern: /(?:example|sample|test|fake|dummy|placeholder|xxx|your_)/i,
|
|
642
|
+
languages: ALL_LANGS, description: 'AWS access key ID found in source code.',
|
|
643
|
+
remediation: 'Remove the key and rotate it immediately. Use IAM roles or environment variables.',
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
id: 'SEC002', name: 'AWS Secret Key', severity: Severity.CRITICAL, confidence: 'high',
|
|
647
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
648
|
+
pattern: /(?:aws_secret_access_key|AWS_SECRET_ACCESS_KEY)\s*[:=]\s*['"][A-Za-z0-9/+=]{40}['"]/,
|
|
649
|
+
languages: ALL_LANGS, description: 'AWS secret access key found in source code.',
|
|
650
|
+
remediation: 'Remove and rotate immediately. Use IAM roles or AWS Secrets Manager.',
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
id: 'SEC003', name: 'Generic API Key', severity: Severity.HIGH, confidence: 'medium',
|
|
654
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
655
|
+
pattern: /(?:api[-_]?key|apikey|API[-_]?KEY)\s*[:=]\s*['"][a-zA-Z0-9_\-]{20,}['"]/i,
|
|
656
|
+
antiPattern: /(?:example|sample|test|fake|dummy|placeholder|xxx|your_|process\.env|os\.environ|config\.|env\[)/i,
|
|
657
|
+
languages: ALL_LANGS, description: 'Hardcoded API key found in source code.',
|
|
658
|
+
remediation: 'Store API keys in environment variables or a secrets manager.',
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
id: 'SEC004', name: 'Private Key', severity: Severity.CRITICAL, confidence: 'high',
|
|
662
|
+
cwe: 'CWE-321', owasp: 'A02',
|
|
663
|
+
pattern: /-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----/,
|
|
664
|
+
languages: ALL_LANGS, description: 'Private key embedded in source code.',
|
|
665
|
+
remediation: 'Remove private key from source. Use file references or key management service.',
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
id: 'SEC005', name: 'GitHub Token', severity: Severity.CRITICAL, confidence: 'high',
|
|
669
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
670
|
+
pattern: /(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,}/,
|
|
671
|
+
languages: ALL_LANGS, description: 'GitHub personal access token found in source code.',
|
|
672
|
+
remediation: 'Remove and rotate the token immediately. Use environment variables.',
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
id: 'SEC006', name: 'Google API Key', severity: Severity.HIGH, confidence: 'high',
|
|
676
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
677
|
+
pattern: /AIza[A-Za-z0-9_\\-]{35}/,
|
|
678
|
+
languages: ALL_LANGS, description: 'Google API key found in source code.',
|
|
679
|
+
remediation: 'Remove and rotate the key. Use environment variables and restrict key scope.',
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
id: 'SEC007', name: 'Slack Token', severity: Severity.CRITICAL, confidence: 'high',
|
|
683
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
684
|
+
pattern: /xox[bpors]-[A-Za-z0-9-]{10,}/,
|
|
685
|
+
languages: ALL_LANGS, description: 'Slack API token found in source code.',
|
|
686
|
+
remediation: 'Remove and rotate immediately. Use environment variables.',
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
id: 'SEC008', name: 'Stripe Key', severity: Severity.CRITICAL, confidence: 'high',
|
|
690
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
691
|
+
pattern: /(?:sk|pk)_(?:live|test)_[A-Za-z0-9]{20,}/,
|
|
692
|
+
languages: ALL_LANGS, description: 'Stripe API key found in source code.',
|
|
693
|
+
remediation: 'Remove and rotate immediately. Use environment variables.',
|
|
694
|
+
},
|
|
695
|
+
{
|
|
696
|
+
id: 'SEC009', name: 'Generic Secret', severity: Severity.HIGH, confidence: 'medium',
|
|
697
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
698
|
+
pattern: /(?:secret|SECRET|token|TOKEN|password|PASSWORD|passwd|PASSWD|credentials?|CREDENTIALS?)\s*[:=]\s*['"][a-zA-Z0-9!@#$%^&*()_+\-={}\[\]:;"'<>,.?/\\|~`]{12,}['"]/,
|
|
699
|
+
antiPattern: /(?:example|sample|test|fake|dummy|placeholder|xxx|your_|process\.env|os\.environ|config\.|env\[|<|TODO|CHANGE|REPLACE)/i,
|
|
700
|
+
languages: ALL_LANGS, description: 'Potential hardcoded secret or credential found.',
|
|
701
|
+
remediation: 'Store secrets in environment variables or a secrets manager.',
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
id: 'SEC010', name: 'Database Connection String', severity: Severity.HIGH, confidence: 'high',
|
|
705
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
706
|
+
pattern: /['"](?:mongodb(?:\+srv)?|postgres(?:ql)?|mysql|mssql|redis|amqp):\/\/[^:]+:[^@]+@[^'"]+['"]/i,
|
|
707
|
+
antiPattern: /(?:localhost|127\.0\.0\.1|example\.com|process\.env|os\.environ)/i,
|
|
708
|
+
languages: ALL_LANGS, description: 'Database connection string with credentials in source code.',
|
|
709
|
+
remediation: 'Use environment variables for database connection strings.',
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
id: 'SEC011', name: 'SendGrid API Key', severity: Severity.HIGH, confidence: 'high',
|
|
713
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
714
|
+
pattern: /SG\.[A-Za-z0-9_\-]{22}\.[A-Za-z0-9_\-]{43}/,
|
|
715
|
+
languages: ALL_LANGS, description: 'SendGrid API key found in source code.',
|
|
716
|
+
remediation: 'Remove and rotate immediately. Use environment variables.',
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
id: 'SEC012', name: 'Twilio Credentials', severity: Severity.HIGH, confidence: 'high',
|
|
720
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
721
|
+
pattern: /(?:AC[a-z0-9]{32}|SK[a-z0-9]{32})/,
|
|
722
|
+
languages: ALL_LANGS, description: 'Twilio Account SID or API key found in source code.',
|
|
723
|
+
remediation: 'Remove and rotate immediately. Use environment variables.',
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
id: 'SEC013', name: 'Heroku API Key', severity: Severity.HIGH, confidence: 'high',
|
|
727
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
728
|
+
pattern: /(?:heroku.*api[-_]?key|HEROKU_API_KEY)\s*[:=]\s*['"][a-f0-9-]{36,}['"]/i,
|
|
729
|
+
languages: ALL_LANGS, description: 'Heroku API key found in source code.',
|
|
730
|
+
remediation: 'Remove and rotate immediately. Use environment variables.',
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
id: 'SEC014', name: 'Firebase Config', severity: Severity.MEDIUM, confidence: 'medium',
|
|
734
|
+
cwe: 'CWE-798', owasp: 'A02',
|
|
735
|
+
pattern: /(?:firebase|FIREBASE).*(?:apiKey|authDomain|databaseURL|storageBucket)\s*[:=]\s*['"]/i,
|
|
736
|
+
languages: ALL_LANGS, description: 'Firebase configuration found in source code.',
|
|
737
|
+
remediation: 'Use environment variables for Firebase config. Secure with Firebase Security Rules.',
|
|
738
|
+
},
|
|
739
|
+
];
|
|
740
|
+
|
|
741
|
+
// ============================================================
|
|
742
|
+
// Prototype Pollution (JavaScript specific)
|
|
743
|
+
// ============================================================
|
|
744
|
+
const prototypePollution: Rule[] = [
|
|
745
|
+
{
|
|
746
|
+
id: 'PROTO001', name: 'Prototype Pollution via Merge', severity: Severity.HIGH, confidence: 'medium',
|
|
747
|
+
cwe: 'CWE-1321', owasp: 'A03',
|
|
748
|
+
pattern: /(?:Object\.assign|_\.merge|_\.extend|_\.defaultsDeep|deepmerge|deep-extend|merge-deep)\s*\(\s*(?:\{\}|target|dst|obj)/,
|
|
749
|
+
languages: JS_TS, description: 'Deep merge operation may be vulnerable to prototype pollution.',
|
|
750
|
+
remediation: 'Validate input keys. Reject __proto__, constructor, and prototype.',
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
id: 'PROTO002', name: 'Prototype Pollution via Bracket Notation', severity: Severity.HIGH, confidence: 'medium',
|
|
754
|
+
cwe: 'CWE-1321', owasp: 'A03',
|
|
755
|
+
pattern: /\w+\s*\[\s*(?:key|prop|name|field|attr|k|p)\s*\]\s*=\s*(?!undefined|null|false|true|0|''|"")/,
|
|
756
|
+
antiPattern: /(?:hasOwnProperty|Object\.keys|Object\.entries|whitelist|allowlist|sanitize|prototype|__proto__|constructor)/i,
|
|
757
|
+
languages: JS_TS, description: 'Dynamic property assignment without prototype pollution check.',
|
|
758
|
+
remediation: 'Check that key is not __proto__, constructor, or prototype before assignment.',
|
|
759
|
+
},
|
|
760
|
+
];
|
|
761
|
+
|
|
762
|
+
// ============================================================
|
|
763
|
+
// Path Traversal (CWE-22)
|
|
764
|
+
// ============================================================
|
|
765
|
+
const pathTraversal: Rule[] = [
|
|
766
|
+
{
|
|
767
|
+
id: 'PATH001', name: 'Path Traversal', severity: Severity.HIGH, confidence: 'medium',
|
|
768
|
+
cwe: 'CWE-22', owasp: 'A01',
|
|
769
|
+
pattern: /(?:readFile|readFileSync|createReadStream|writeFile|writeFileSync|unlink|unlinkSync|open|openSync)\s*\(\s*(?:req\.|request\.|input|user|param|query|body|path\.join\s*\([^)]*req)/i,
|
|
770
|
+
languages: JS_TS, description: 'File operation with user-controlled path — directory traversal risk.',
|
|
771
|
+
remediation: 'Validate and sanitize file paths. Use path.resolve() and check against base directory.',
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
id: 'PATH002', name: 'Path Traversal (Python)', severity: Severity.HIGH, confidence: 'medium',
|
|
775
|
+
cwe: 'CWE-22', owasp: 'A01',
|
|
776
|
+
pattern: /(?:open|os\.path\.join|pathlib\.Path)\s*\(\s*(?:request\.|input|user_|flask\.request|args\.get)/i,
|
|
777
|
+
languages: ['python'], description: 'File operation with user-controlled path — directory traversal risk.',
|
|
778
|
+
remediation: 'Validate paths against a safe base directory. Use os.path.realpath() to resolve symlinks.',
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
id: 'PATH003', name: 'Zip Slip', severity: Severity.HIGH, confidence: 'medium',
|
|
782
|
+
cwe: 'CWE-22', owasp: 'A01',
|
|
783
|
+
pattern: /(?:extractAll|extract\s*\(|unzip|ZipFile|tar\.extractall|tar\.extract)\s*\(/i,
|
|
784
|
+
antiPattern: /(?:validatePath|sanitize|startsWith|normalize|realpath|abspath)/i,
|
|
785
|
+
languages: BACKEND, description: 'Archive extraction without path validation — Zip Slip vulnerability.',
|
|
786
|
+
remediation: 'Validate extracted file paths stay within the intended directory.',
|
|
787
|
+
},
|
|
788
|
+
];
|
|
789
|
+
|
|
790
|
+
// ============================================================
|
|
791
|
+
// Additional patterns for comprehensive coverage
|
|
792
|
+
// ============================================================
|
|
793
|
+
const additional: Rule[] = [
|
|
794
|
+
{
|
|
795
|
+
id: 'MISC001', name: 'setTimeout/setInterval with String', severity: Severity.MEDIUM, confidence: 'high',
|
|
796
|
+
cwe: 'CWE-95', owasp: 'A03',
|
|
797
|
+
pattern: /(?:setTimeout|setInterval)\s*\(\s*['"`]/,
|
|
798
|
+
languages: JS_TS, description: 'setTimeout/setInterval with string argument acts like eval().',
|
|
799
|
+
remediation: 'Pass a function reference instead of a string.',
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
id: 'MISC002', name: 'Unsafe Reflection', severity: Severity.HIGH, confidence: 'medium',
|
|
803
|
+
cwe: 'CWE-470', owasp: 'A03',
|
|
804
|
+
pattern: /(?:require\s*\(|import\s*\()\s*(?:req\.|request\.|input|user|param|query|body)/i,
|
|
805
|
+
languages: JS_TS, description: 'Dynamic require/import with user input — code injection risk.',
|
|
806
|
+
remediation: 'Use a mapping of allowed module names instead of dynamic import.',
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
id: 'MISC003', name: 'XML External Entity (XXE)', severity: Severity.HIGH, confidence: 'medium',
|
|
810
|
+
cwe: 'CWE-611', owasp: 'A05',
|
|
811
|
+
pattern: /(?:parseXML|xml2js|DOMParser|SAXParser|XMLReader|etree\.parse|lxml\.etree)\s*[(.]/i,
|
|
812
|
+
antiPattern: /(?:disallow.*dtd|external.*entity.*false|FEATURE_EXTERNAL|resolve_entities\s*=\s*False)/i,
|
|
813
|
+
languages: BACKEND, description: 'XML parsing without apparent XXE protection.',
|
|
814
|
+
remediation: 'Disable DTD processing and external entity resolution.',
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
id: 'MISC004', name: 'Hardcoded IP Address', severity: Severity.LOW, confidence: 'medium',
|
|
818
|
+
cwe: 'CWE-798', owasp: 'A05',
|
|
819
|
+
pattern: /['"](?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3})['"]/,
|
|
820
|
+
languages: ALL_LANGS, description: 'Hardcoded private IP address found in source code.',
|
|
821
|
+
remediation: 'Use configuration files or environment variables for IP addresses.',
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
id: 'MISC005', name: 'Unsafe YAML Loading', severity: Severity.HIGH, confidence: 'high',
|
|
825
|
+
cwe: 'CWE-502', owasp: 'A08',
|
|
826
|
+
pattern: /yaml\.load\s*\(/,
|
|
827
|
+
antiPattern: /(?:safe_load|SafeLoader|Loader\s*=\s*yaml\.SafeLoader)/,
|
|
828
|
+
languages: ['python'], description: 'yaml.load() without SafeLoader enables code execution.',
|
|
829
|
+
remediation: 'Use yaml.safe_load() or yaml.load(data, Loader=yaml.SafeLoader).',
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
id: 'MISC006', name: 'Process Environment Exposure', severity: Severity.MEDIUM, confidence: 'medium',
|
|
833
|
+
cwe: 'CWE-200', owasp: 'A05',
|
|
834
|
+
pattern: /(?:res\.(?:send|json)|response\.)\s*\(\s*process\.env\s*\)/,
|
|
835
|
+
languages: JS_TS, description: 'Entire process.env sent to client, exposing all environment variables.',
|
|
836
|
+
remediation: 'Only send specific, non-sensitive configuration values to the client.',
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
id: 'MISC007', name: 'Disabled Security Feature', severity: Severity.MEDIUM, confidence: 'high',
|
|
840
|
+
cwe: 'CWE-693', owasp: 'A05',
|
|
841
|
+
pattern: /(?:helmet\.(?:contentSecurityPolicy|hsts|frameguard)\s*\(\s*\{\s*(?:enable|enabled)\s*:\s*false|app\.disable\s*\(\s*['"]x-powered-by['"])/,
|
|
842
|
+
languages: JS_TS, description: 'Security feature explicitly disabled.',
|
|
843
|
+
remediation: 'Re-enable security features or document the specific reason for disabling.',
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
id: 'MISC008', name: 'Potential Timing Attack', severity: Severity.MEDIUM, confidence: 'low',
|
|
847
|
+
cwe: 'CWE-208', owasp: 'A02',
|
|
848
|
+
pattern: /(?:===?\s*(?:password|token|secret|apiKey|hash)|(?:password|token|secret|apiKey|hash)\s*===?)/i,
|
|
849
|
+
antiPattern: /(?:timingSafe|crypto\.timingSafeEqual|constantTime|hmac\.compare|secrets\.compare_digest)/i,
|
|
850
|
+
languages: BACKEND, description: 'String comparison of secrets vulnerable to timing attacks.',
|
|
851
|
+
remediation: 'Use crypto.timingSafeEqual() or constant-time comparison for secrets.',
|
|
852
|
+
},
|
|
853
|
+
{
|
|
854
|
+
id: 'MISC009', name: 'Unvalidated Redirect URL', severity: Severity.MEDIUM, confidence: 'medium',
|
|
855
|
+
cwe: 'CWE-601', owasp: 'A01',
|
|
856
|
+
pattern: /(?:redirect|302|301|location)\s*[\(=:]\s*(?:req\.query|request\.args|params\[|searchParams)/i,
|
|
857
|
+
languages: WEB_LANGS, description: 'Redirect destination from query parameter — open redirect risk.',
|
|
858
|
+
remediation: 'Validate redirect URLs against an allowlist of trusted paths/domains.',
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
id: 'MISC010', name: 'Electron Node Integration', severity: Severity.HIGH, confidence: 'high',
|
|
862
|
+
cwe: 'CWE-94', owasp: 'A03',
|
|
863
|
+
pattern: /nodeIntegration\s*:\s*true/,
|
|
864
|
+
languages: JS_TS, description: 'Electron nodeIntegration enabled — XSS can lead to RCE.',
|
|
865
|
+
remediation: 'Disable nodeIntegration and use contextBridge for IPC.',
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
id: 'MISC011', name: 'Electron Context Isolation Disabled', severity: Severity.HIGH, confidence: 'high',
|
|
869
|
+
cwe: 'CWE-94', owasp: 'A03',
|
|
870
|
+
pattern: /contextIsolation\s*:\s*false/,
|
|
871
|
+
languages: JS_TS, description: 'Electron contextIsolation disabled — preload scripts can be exploited.',
|
|
872
|
+
remediation: 'Enable contextIsolation and use contextBridge.',
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
id: 'MISC012', name: 'postMessage Without Origin Check', severity: Severity.MEDIUM, confidence: 'medium',
|
|
876
|
+
cwe: 'CWE-346', owasp: 'A01',
|
|
877
|
+
pattern: /addEventListener\s*\(\s*['"]message['"]/,
|
|
878
|
+
antiPattern: /(?:origin|source)/i,
|
|
879
|
+
languages: JS_TS, description: 'postMessage event handler without origin validation.',
|
|
880
|
+
remediation: 'Always verify event.origin before processing postMessage data.',
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
id: 'MISC013', name: 'Unsafe innerHTML Assignment', severity: Severity.HIGH, confidence: 'high',
|
|
884
|
+
cwe: 'CWE-79', owasp: 'A03',
|
|
885
|
+
pattern: /\.(?:outerHTML|insertAdjacentHTML)\s*(?:=|\()\s*/,
|
|
886
|
+
languages: JS_TS, description: 'Unsafe HTML insertion that may lead to XSS.',
|
|
887
|
+
remediation: 'Use textContent or sanitize HTML before insertion.',
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
id: 'MISC014', name: 'Unsafe window.open', severity: Severity.LOW, confidence: 'medium',
|
|
891
|
+
cwe: 'CWE-1022', owasp: 'A01',
|
|
892
|
+
pattern: /window\.open\s*\(/,
|
|
893
|
+
antiPattern: /(?:noopener|noreferrer)/,
|
|
894
|
+
languages: JS_TS, description: 'window.open without noopener/noreferrer — reverse tabnabbing risk.',
|
|
895
|
+
remediation: 'Add "noopener,noreferrer" to window.open features.',
|
|
896
|
+
},
|
|
897
|
+
{
|
|
898
|
+
id: 'MISC015', name: 'Race Condition (TOCTOU)', severity: Severity.MEDIUM, confidence: 'low',
|
|
899
|
+
cwe: 'CWE-367', owasp: 'A04',
|
|
900
|
+
pattern: /(?:fs\.(?:exists|access|stat)(?:Sync)?\s*\([^)]+\)[^;]*(?:fs\.(?:read|write|unlink|mkdir|rmdir)|os\.path\.exists|os\.access))/,
|
|
901
|
+
languages: [...JS_TS, 'python'], description: 'Time-of-check to time-of-use file operation race condition.',
|
|
902
|
+
remediation: 'Use atomic file operations or file locking.',
|
|
903
|
+
},
|
|
904
|
+
{
|
|
905
|
+
id: 'MISC016', name: 'Exposed Docker Socket', severity: Severity.CRITICAL, confidence: 'high',
|
|
906
|
+
cwe: 'CWE-250', owasp: 'A05',
|
|
907
|
+
pattern: /(?:\/var\/run\/docker\.sock|docker\.sock|DOCKER_HOST.*tcp:\/\/)/,
|
|
908
|
+
languages: ALL_LANGS, description: 'Docker socket access — container escape risk.',
|
|
909
|
+
remediation: 'Restrict Docker socket access. Use rootless Docker or Podman.',
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
id: 'MISC017', name: 'Kubernetes Secrets in Env', severity: Severity.MEDIUM, confidence: 'medium',
|
|
913
|
+
cwe: 'CWE-798', owasp: 'A05',
|
|
914
|
+
pattern: /(?:secretKeyRef|valueFrom.*secretKeyRef|kind:\s*Secret)/,
|
|
915
|
+
languages: ALL_LANGS, description: 'Kubernetes secrets may be exposed in environment variables.',
|
|
916
|
+
remediation: 'Use mounted volumes for secrets, enable encryption at rest.',
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
id: 'MISC018', name: 'SQL Injection (Java)', severity: Severity.CRITICAL, confidence: 'high',
|
|
920
|
+
cwe: 'CWE-89', owasp: 'A03',
|
|
921
|
+
pattern: /(?:Statement|createStatement|executeQuery|executeUpdate)\s*\(?\s*['"](?:SELECT|INSERT|UPDATE|DELETE|DROP)\s[^'"]*['"]\s*\+/i,
|
|
922
|
+
languages: ['java', 'kotlin'], description: 'SQL query built with string concatenation in Java.',
|
|
923
|
+
remediation: 'Use PreparedStatement with parameterized queries.',
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
id: 'MISC019', name: 'Go SQL Injection', severity: Severity.CRITICAL, confidence: 'high',
|
|
927
|
+
cwe: 'CWE-89', owasp: 'A03',
|
|
928
|
+
pattern: /(?:db\.(?:Query|Exec|QueryRow))\s*\(\s*(?:fmt\.Sprintf|"[^"]*"\s*\+)/,
|
|
929
|
+
languages: ['go'], description: 'SQL query built with string formatting in Go.',
|
|
930
|
+
remediation: 'Use parameterized queries with $1, $2 placeholders.',
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
id: 'MISC020', name: 'PHP SQL Injection', severity: Severity.CRITICAL, confidence: 'high',
|
|
934
|
+
cwe: 'CWE-89', owasp: 'A03',
|
|
935
|
+
pattern: /(?:mysql_query|mysqli_query|pg_query)\s*\(\s*['"](?:SELECT|INSERT|UPDATE|DELETE)\s[^'"]*['"]\s*\.\s*\$/i,
|
|
936
|
+
languages: ['php'], description: 'SQL query built with string concatenation in PHP.',
|
|
937
|
+
remediation: 'Use PDO prepared statements.',
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
id: 'MISC021', name: 'PHP Command Injection', severity: Severity.CRITICAL, confidence: 'high',
|
|
941
|
+
cwe: 'CWE-78', owasp: 'A03',
|
|
942
|
+
pattern: /(?:system|exec|passthru|shell_exec|popen|proc_open)\s*\(\s*\$(?!_SERVER)/,
|
|
943
|
+
languages: ['php'], description: 'PHP command execution with variable input.',
|
|
944
|
+
remediation: 'Use escapeshellarg() and escapeshellcmd() for any user input.',
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
id: 'MISC022', name: 'Ruby Command Injection', severity: Severity.CRITICAL, confidence: 'high',
|
|
948
|
+
cwe: 'CWE-78', owasp: 'A03',
|
|
949
|
+
pattern: /(?:system|exec|`|\%x)\s*[\(\[{]?\s*(?:#\{|['"].*['"].*\+)/,
|
|
950
|
+
languages: ['ruby'], description: 'Ruby command execution with interpolated input.',
|
|
951
|
+
remediation: 'Use array form of system() or Open3 module.',
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
id: 'MISC023', name: 'Insufficient Entropy', severity: Severity.MEDIUM, confidence: 'medium',
|
|
955
|
+
cwe: 'CWE-331', owasp: 'A02',
|
|
956
|
+
pattern: /(?:uuid|token|session|nonce|salt)\s*[:=]\s*(?:Date\.now|time\.time|System\.currentTimeMillis|Time\.now)/i,
|
|
957
|
+
languages: ALL_LANGS, description: 'Using timestamp for security-sensitive value — insufficient entropy.',
|
|
958
|
+
remediation: 'Use cryptographically secure random number generator.',
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
id: 'MISC024', name: 'Missing Content-Type Validation', severity: Severity.LOW, confidence: 'low',
|
|
962
|
+
cwe: 'CWE-20', owasp: 'A04',
|
|
963
|
+
pattern: /(?:app\.(?:post|put|patch)|router\.(?:post|put|patch))\s*\(/i,
|
|
964
|
+
antiPattern: /(?:content-type|Content-Type|bodyParser|express\.json|express\.urlencoded|multer)/i,
|
|
965
|
+
languages: JS_TS, description: 'Endpoint may not validate Content-Type header.',
|
|
966
|
+
remediation: 'Validate Content-Type headers for incoming requests.',
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
id: 'MISC025', name: 'Unsafe Object Spread from User Input', severity: Severity.MEDIUM, confidence: 'medium',
|
|
970
|
+
cwe: 'CWE-915', owasp: 'A04',
|
|
971
|
+
pattern: /\{\s*\.\.\.(?:req\.body|req\.query|request\.body|request\.data)/,
|
|
972
|
+
languages: JS_TS, description: 'Spreading user input into objects — mass assignment risk.',
|
|
973
|
+
remediation: 'Explicitly destructure only the fields you expect.',
|
|
974
|
+
},
|
|
975
|
+
{
|
|
976
|
+
id: 'MISC026', name: 'C/C++ Buffer Overflow', severity: Severity.CRITICAL, confidence: 'medium',
|
|
977
|
+
cwe: 'CWE-120', owasp: 'A03',
|
|
978
|
+
pattern: /(?:strcpy|strcat|sprintf|gets|scanf)\s*\(/,
|
|
979
|
+
languages: ['c', 'cpp'], description: 'Unsafe C function prone to buffer overflow.',
|
|
980
|
+
remediation: 'Use strncpy, strncat, snprintf, fgets, or sscanf with size limits.',
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
id: 'MISC027', name: 'C/C++ Format String', severity: Severity.HIGH, confidence: 'medium',
|
|
984
|
+
cwe: 'CWE-134', owasp: 'A03',
|
|
985
|
+
pattern: /(?:printf|fprintf|sprintf|syslog)\s*\(\s*(?!['"][^'"]*['"])[a-zA-Z_]\w*\s*\)/,
|
|
986
|
+
languages: ['c', 'cpp'], description: 'Format string vulnerability — variable used as format string.',
|
|
987
|
+
remediation: 'Always use a literal format string: printf("%s", variable).',
|
|
988
|
+
},
|
|
989
|
+
{
|
|
990
|
+
id: 'MISC028', name: 'C/C++ Use After Free Risk', severity: Severity.HIGH, confidence: 'low',
|
|
991
|
+
cwe: 'CWE-416', owasp: 'A08',
|
|
992
|
+
pattern: /free\s*\(\s*(\w+)\s*\)\s*;(?!.*\1\s*=\s*NULL)/,
|
|
993
|
+
languages: ['c', 'cpp'], description: 'Pointer not nullified after free — use-after-free risk.',
|
|
994
|
+
remediation: 'Set pointer to NULL after freeing.',
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
id: 'MISC029', name: 'Go Unsafe Package', severity: Severity.MEDIUM, confidence: 'high',
|
|
998
|
+
cwe: 'CWE-242', owasp: 'A04',
|
|
999
|
+
pattern: /import\s+['"]unsafe['"]/,
|
|
1000
|
+
languages: ['go'], description: 'Go unsafe package used — bypasses type safety.',
|
|
1001
|
+
remediation: 'Avoid unsafe package unless absolutely necessary.',
|
|
1002
|
+
},
|
|
1003
|
+
{
|
|
1004
|
+
id: 'MISC030', name: 'Rust Unsafe Block', severity: Severity.MEDIUM, confidence: 'medium',
|
|
1005
|
+
cwe: 'CWE-242', owasp: 'A04',
|
|
1006
|
+
pattern: /unsafe\s*\{/,
|
|
1007
|
+
languages: ['rust'], description: 'Rust unsafe block — memory safety guarantees suspended.',
|
|
1008
|
+
remediation: 'Minimize unsafe usage. Document safety invariants.',
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
id: 'MISC031', name: 'Swift Force Unwrap', severity: Severity.LOW, confidence: 'medium',
|
|
1012
|
+
cwe: 'CWE-476', owasp: 'A04',
|
|
1013
|
+
pattern: /\w+!/,
|
|
1014
|
+
antiPattern: /(?:IBOutlet|IBAction|@objc|override|func\s|import|!=|!==)/,
|
|
1015
|
+
languages: ['swift'], description: 'Force unwrapping may cause runtime crash.',
|
|
1016
|
+
remediation: 'Use optional binding (if let, guard let) instead of force unwrapping.',
|
|
1017
|
+
},
|
|
1018
|
+
{
|
|
1019
|
+
id: 'MISC032', name: 'Hardcoded Database Credentials', severity: Severity.CRITICAL, confidence: 'high',
|
|
1020
|
+
cwe: 'CWE-798', owasp: 'A07',
|
|
1021
|
+
pattern: /(?:(?:db|database|mysql|postgres|mongo|redis)[-_.]?(?:password|passwd|pass|pwd))\s*[:=]\s*['"][^'"]{4,}['"]/i,
|
|
1022
|
+
antiPattern: /(?:process\.env|os\.environ|config\.|env\[|example|sample|test|your_)/i,
|
|
1023
|
+
languages: ALL_LANGS, description: 'Database password hardcoded in source code.',
|
|
1024
|
+
remediation: 'Use environment variables or a secrets manager.',
|
|
1025
|
+
},
|
|
1026
|
+
{
|
|
1027
|
+
id: 'MISC033', name: 'Email in Code', severity: Severity.INFO, confidence: 'medium',
|
|
1028
|
+
cwe: 'CWE-200', owasp: 'A05',
|
|
1029
|
+
pattern: /['"][a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}['"]/,
|
|
1030
|
+
antiPattern: /(?:example|test|placeholder|noreply|no-reply|@example\.com|@test\.com|schema|pattern|regex)/i,
|
|
1031
|
+
languages: ALL_LANGS, description: 'Email address found in source code.',
|
|
1032
|
+
remediation: 'Use configuration for email addresses, avoid hardcoding.',
|
|
1033
|
+
},
|
|
1034
|
+
{
|
|
1035
|
+
id: 'MISC034', name: 'TODO Security Comment', severity: Severity.INFO, confidence: 'high',
|
|
1036
|
+
cwe: 'CWE-546', owasp: 'A04',
|
|
1037
|
+
pattern: /(?:\/\/|#|\/\*)\s*(?:TODO|FIXME|HACK|XXX|BUG)\s*:?\s*.*(?:security|vuln|auth|password|credential|encrypt|token|secret|injection|xss|csrf|sanitize)/i,
|
|
1038
|
+
languages: ALL_LANGS, description: 'Security-related TODO comment indicates incomplete security work.',
|
|
1039
|
+
remediation: 'Address the security concern documented in the TODO comment.',
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
id: 'MISC035', name: 'Terraform Sensitive Variable Exposed', severity: Severity.HIGH, confidence: 'medium',
|
|
1043
|
+
cwe: 'CWE-200', owasp: 'A05',
|
|
1044
|
+
pattern: /(?:output\s+['"][^'"]+['"]\s*\{[^}]*value\s*=\s*(?:var|local)\.(?:password|secret|key|token|credential))/i,
|
|
1045
|
+
languages: ALL_LANGS, description: 'Terraform output may expose sensitive variable.',
|
|
1046
|
+
remediation: 'Mark outputs as sensitive = true.',
|
|
1047
|
+
},
|
|
1048
|
+
];
|
|
1049
|
+
|
|
1050
|
+
// ============================================================
|
|
1051
|
+
// Combine all rules
|
|
1052
|
+
// ============================================================
|
|
1053
|
+
export const ALL_RULES: Rule[] = [
|
|
1054
|
+
...brokenAccessControl,
|
|
1055
|
+
...cryptoFailures,
|
|
1056
|
+
...injection,
|
|
1057
|
+
...insecureDesign,
|
|
1058
|
+
...misconfig,
|
|
1059
|
+
...vulnerableComponents,
|
|
1060
|
+
...authFailures,
|
|
1061
|
+
...dataIntegrity,
|
|
1062
|
+
...loggingFailures,
|
|
1063
|
+
...ssrf,
|
|
1064
|
+
...secrets,
|
|
1065
|
+
...prototypePollution,
|
|
1066
|
+
...pathTraversal,
|
|
1067
|
+
...additional,
|
|
1068
|
+
];
|
|
1069
|
+
|
|
1070
|
+
export function getRulesForLanguage(language: string): Rule[] {
|
|
1071
|
+
return ALL_RULES.filter(r => r.languages.includes(language));
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
export function getRuleById(id: string): Rule | undefined {
|
|
1075
|
+
return ALL_RULES.find(r => r.id === id);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
export function getRulesByOwasp(category: string): Rule[] {
|
|
1079
|
+
return ALL_RULES.filter(r => r.owasp === category);
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
export function getRulesBySeverity(severity: Severity): Rule[] {
|
|
1083
|
+
return ALL_RULES.filter(r => r.severity === severity);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
export function getEnabledRules(disabled: string[] = []): Rule[] {
|
|
1087
|
+
const disabledSet = new Set(disabled);
|
|
1088
|
+
return ALL_RULES.filter(r => !disabledSet.has(r.id));
|
|
1089
|
+
}
|