milens 0.6.3 → 0.6.4
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/.agents/skills/adapters/SKILL.md +31 -0
- package/.agents/skills/analyzer/SKILL.md +55 -0
- package/.agents/skills/apps/SKILL.md +42 -0
- package/.agents/skills/docs/SKILL.md +46 -0
- package/.agents/skills/milens/SKILL.md +168 -0
- package/.agents/skills/milens-code-review/SKILL.md +186 -0
- package/.agents/skills/milens-eval/SKILL.md +221 -0
- package/.agents/skills/milens-plan/SKILL.md +227 -0
- package/.agents/skills/milens-refactor-clean/SKILL.md +209 -0
- package/.agents/skills/milens-security-review/SKILL.md +224 -0
- package/.agents/skills/milens-tdd/SKILL.md +156 -0
- package/.agents/skills/parser/SKILL.md +60 -0
- package/.agents/skills/root/SKILL.md +64 -0
- package/.agents/skills/scripts/SKILL.md +27 -0
- package/.agents/skills/security/SKILL.md +44 -0
- package/.agents/skills/server/SKILL.md +46 -0
- package/.agents/skills/store/SKILL.md +53 -0
- package/.agents/skills/test/SKILL.md +73 -0
- package/LICENSE +75 -75
- package/README.md +508 -432
- package/adapters/README.md +107 -0
- package/adapters/claude-code/.claude/mcp.json +9 -0
- package/adapters/claude-code/CLAUDE.md +58 -0
- package/adapters/codex/.codex/codex.md +52 -0
- package/adapters/copilot/.github/copilot-instructions.md +62 -0
- package/adapters/cursor/.cursorrules +9 -0
- package/adapters/gemini/.gemini/context.md +58 -0
- package/adapters/opencode/.opencode/config.json +9 -0
- package/adapters/opencode/AGENTS.md +58 -0
- package/adapters/zed/.zed/settings.json +8 -0
- package/dist/agents-md.d.ts +3 -0
- package/dist/agents-md.d.ts.map +1 -0
- package/dist/agents-md.js +112 -0
- package/dist/agents-md.js.map +1 -0
- package/dist/analyzer/engine.js +1 -1
- package/dist/analyzer/engine.js.map +1 -1
- package/dist/cli.js +1190 -401
- package/dist/cli.js.map +1 -1
- package/dist/metrics.d.ts +51 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +64 -0
- package/dist/metrics.js.map +1 -0
- package/dist/parser/lang-go.js +47 -47
- package/dist/parser/lang-java.js +29 -29
- package/dist/parser/lang-js.js +105 -105
- package/dist/parser/lang-php.js +38 -38
- package/dist/parser/lang-py.js +34 -34
- package/dist/parser/lang-ruby.js +14 -14
- package/dist/parser/lang-rust.js +30 -30
- package/dist/parser/lang-ts.js +191 -191
- package/dist/security/deps.d.ts +38 -0
- package/dist/security/deps.d.ts.map +1 -0
- package/dist/security/deps.js +685 -0
- package/dist/security/deps.js.map +1 -0
- package/dist/security/rules.d.ts +42 -0
- package/dist/security/rules.d.ts.map +1 -0
- package/dist/security/rules.js +940 -0
- package/dist/security/rules.js.map +1 -0
- package/dist/server/hooks.d.ts +26 -0
- package/dist/server/hooks.d.ts.map +1 -0
- package/dist/server/hooks.js +253 -0
- package/dist/server/hooks.js.map +1 -0
- package/dist/server/mcp-prompts.d.ts +277 -0
- package/dist/server/mcp-prompts.d.ts.map +1 -0
- package/dist/server/mcp-prompts.js +627 -0
- package/dist/server/mcp-prompts.js.map +1 -0
- package/dist/server/mcp.d.ts.map +1 -1
- package/dist/server/mcp.js +618 -643
- package/dist/server/mcp.js.map +1 -1
- package/dist/server/test-plan.d.ts +20 -0
- package/dist/server/test-plan.d.ts.map +1 -0
- package/dist/server/test-plan.js +100 -0
- package/dist/server/test-plan.js.map +1 -0
- package/dist/skills.js +152 -152
- package/dist/store/annotations.d.ts +41 -0
- package/dist/store/annotations.d.ts.map +1 -0
- package/dist/store/annotations.js +192 -0
- package/dist/store/annotations.js.map +1 -0
- package/dist/store/confidence.d.ts +18 -0
- package/dist/store/confidence.d.ts.map +1 -0
- package/dist/store/confidence.js +82 -0
- package/dist/store/confidence.js.map +1 -0
- package/dist/store/db.d.ts +37 -14
- package/dist/store/db.d.ts.map +1 -1
- package/dist/store/db.js +332 -239
- package/dist/store/db.js.map +1 -1
- package/dist/store/schema.sql +128 -116
- package/dist/store/vectors.js +2 -2
- package/dist/types.d.ts +101 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +24 -0
- package/package.json +80 -66
- package/dist/gateway/analyzer.d.ts +0 -6
- package/dist/gateway/analyzer.d.ts.map +0 -1
- package/dist/gateway/analyzer.js +0 -218
- package/dist/gateway/analyzer.js.map +0 -1
- package/dist/gateway/cache.d.ts +0 -35
- package/dist/gateway/cache.d.ts.map +0 -1
- package/dist/gateway/cache.js +0 -175
- package/dist/gateway/cache.js.map +0 -1
- package/dist/gateway/config.d.ts +0 -10
- package/dist/gateway/config.d.ts.map +0 -1
- package/dist/gateway/config.js +0 -167
- package/dist/gateway/config.js.map +0 -1
- package/dist/gateway/context-memory.d.ts +0 -68
- package/dist/gateway/context-memory.d.ts.map +0 -1
- package/dist/gateway/context-memory.js +0 -157
- package/dist/gateway/context-memory.js.map +0 -1
- package/dist/gateway/observability.d.ts +0 -83
- package/dist/gateway/observability.d.ts.map +0 -1
- package/dist/gateway/observability.js +0 -152
- package/dist/gateway/observability.js.map +0 -1
- package/dist/gateway/privacy.d.ts +0 -27
- package/dist/gateway/privacy.d.ts.map +0 -1
- package/dist/gateway/privacy.js +0 -139
- package/dist/gateway/privacy.js.map +0 -1
- package/dist/gateway/providers.d.ts +0 -66
- package/dist/gateway/providers.d.ts.map +0 -1
- package/dist/gateway/providers.js +0 -377
- package/dist/gateway/providers.js.map +0 -1
- package/dist/gateway/router.d.ts +0 -18
- package/dist/gateway/router.d.ts.map +0 -1
- package/dist/gateway/router.js +0 -102
- package/dist/gateway/router.js.map +0 -1
- package/dist/gateway/server.d.ts +0 -20
- package/dist/gateway/server.d.ts.map +0 -1
- package/dist/gateway/server.js +0 -387
- package/dist/gateway/server.js.map +0 -1
- package/dist/gateway/translator.d.ts +0 -19
- package/dist/gateway/translator.d.ts.map +0 -1
- package/dist/gateway/translator.js +0 -340
- package/dist/gateway/translator.js.map +0 -1
- package/dist/gateway/types.d.ts +0 -215
- package/dist/gateway/types.d.ts.map +0 -1
- package/dist/gateway/types.js +0 -3
- package/dist/gateway/types.js.map +0 -1
- package/dist/store/gateway-schema.sql +0 -53
|
@@ -0,0 +1,940 @@
|
|
|
1
|
+
// ── Security rules engine for milens ──
|
|
2
|
+
export const OWASP_CATEGORIES = {
|
|
3
|
+
'A01:2021': 'Broken Access Control',
|
|
4
|
+
'A02:2021': 'Cryptographic Failures',
|
|
5
|
+
'A03:2021': 'Injection',
|
|
6
|
+
'A04:2021': 'Insecure Design',
|
|
7
|
+
'A05:2021': 'Security Misconfiguration',
|
|
8
|
+
'A06:2021': 'Vulnerable and Outdated Components',
|
|
9
|
+
'A07:2021': 'Identification and Authentication Failures',
|
|
10
|
+
'A08:2021': 'Software and Data Integrity Failures',
|
|
11
|
+
'A09:2021': 'Security Logging and Monitoring Failures',
|
|
12
|
+
'A10:2021': 'Server-Side Request Forgery (SSRF)',
|
|
13
|
+
};
|
|
14
|
+
const DEFAULT_EXCLUDE = '**/*.test.*,**/*.spec.*,**/node_modules/**';
|
|
15
|
+
// ── 50 built-in security rules ──
|
|
16
|
+
export const ALL_RULES = [
|
|
17
|
+
// ═══════════════════════════════════════════════════════════════
|
|
18
|
+
// SEC-001 – SEC-010 : Secrets (A02:2021)
|
|
19
|
+
// ═══════════════════════════════════════════════════════════════
|
|
20
|
+
{
|
|
21
|
+
id: 'SEC-001',
|
|
22
|
+
category: 'secrets',
|
|
23
|
+
owasp: 'A02:2021',
|
|
24
|
+
severity: 'CRITICAL',
|
|
25
|
+
name: 'Hardcoded password',
|
|
26
|
+
description: 'Password value appears to be hardcoded in source code. Use environment variables or a secrets manager instead.',
|
|
27
|
+
patterns: [
|
|
28
|
+
/(?:password|passwd|pwd)\s*[:=]\s*['"`][^'"`\n]{4,}['"`]/i,
|
|
29
|
+
/(?:password|passwd|pwd)\s*=\s*[^'"`\s;]{4,}/i,
|
|
30
|
+
],
|
|
31
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
32
|
+
fix: 'Replace hardcoded password with process.env.DB_PASSWORD or a secrets manager.',
|
|
33
|
+
confidence: 0.92,
|
|
34
|
+
enabled: true,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'SEC-002',
|
|
38
|
+
category: 'secrets',
|
|
39
|
+
owasp: 'A02:2021',
|
|
40
|
+
severity: 'CRITICAL',
|
|
41
|
+
name: 'Hardcoded secret key',
|
|
42
|
+
description: 'A secret or signing key appears to be hardcoded. Use a secure key management service.',
|
|
43
|
+
patterns: [
|
|
44
|
+
/(?:secret|secretKey|secret_key|clientSecret)\s*[:=]\s*['"`][A-Za-z0-9_\-+=/]{16,}['"`]/i,
|
|
45
|
+
/(?:signingKey|signing_key|encryptionKey|encryption_key)\s*[:=]\s*['"`][A-Za-z0-9_\-+=/]{16,}['"`]/i,
|
|
46
|
+
],
|
|
47
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
48
|
+
fix: 'Store secrets in a vault (HashiCorp Vault, AWS Secrets Manager, etc.) or environment variables.',
|
|
49
|
+
confidence: 0.90,
|
|
50
|
+
enabled: true,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'SEC-003',
|
|
54
|
+
category: 'secrets',
|
|
55
|
+
owasp: 'A02:2021',
|
|
56
|
+
severity: 'CRITICAL',
|
|
57
|
+
name: 'Hardcoded API key',
|
|
58
|
+
description: 'An API key literal is embedded in source code. Rotate the key immediately and use environment variables.',
|
|
59
|
+
patterns: [
|
|
60
|
+
/(?:api[_-]?key|apiKey|apikey)\s*[:=]\s*['"`][A-Za-z0-9_\-]{20,}['"`]/i,
|
|
61
|
+
/(?:api[_-]?secret|apiSecret)\s*[:=]\s*['"`][A-Za-z0-9_\-]{20,}['"`]/i,
|
|
62
|
+
],
|
|
63
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
64
|
+
fix: 'Move the API key to process.env.API_KEY and never commit credentials.',
|
|
65
|
+
confidence: 0.93,
|
|
66
|
+
enabled: true,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'SEC-004',
|
|
70
|
+
category: 'secrets',
|
|
71
|
+
owasp: 'A02:2021',
|
|
72
|
+
severity: 'CRITICAL',
|
|
73
|
+
name: 'AWS Access Key ID exposed',
|
|
74
|
+
description: 'An AWS access key ID (starting with AKIA) is exposed. Rotate the key immediately.',
|
|
75
|
+
patterns: [
|
|
76
|
+
/\bAKIA[0-9A-Z]{16}\b/,
|
|
77
|
+
/(?:aws[_-]?access[_-]?key|AWS_ACCESS_KEY_ID)\s*[:=]\s*['"`]AKIA[0-9A-Z]{16}['"`]/i,
|
|
78
|
+
],
|
|
79
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
80
|
+
fix: 'Use IAM roles, instance profiles, or AWS Secrets Manager. If the key is leaked, deactivate it in the AWS console immediately.',
|
|
81
|
+
confidence: 0.95,
|
|
82
|
+
enabled: true,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'SEC-005',
|
|
86
|
+
category: 'secrets',
|
|
87
|
+
owasp: 'A02:2021',
|
|
88
|
+
severity: 'CRITICAL',
|
|
89
|
+
name: 'Stripe / OpenAI secret key exposed',
|
|
90
|
+
description: 'A secret key starting with "sk-" (Stripe or OpenAI) is exposed in source code.',
|
|
91
|
+
patterns: [
|
|
92
|
+
/\b(sk-(?:live|test|ant|admin)-[A-Za-z0-9_\-]{30,})\b/,
|
|
93
|
+
/(?:stripe[_-]?key|openai[_-]?key|OPENAI_API_KEY)\s*[:=]\s*['"`]sk-[A-Za-z0-9_\-]+['"`]/i,
|
|
94
|
+
],
|
|
95
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
96
|
+
fix: 'Use environment variables (STRIPE_SECRET_KEY, OPENAI_API_KEY) and never commit sk- keys.',
|
|
97
|
+
confidence: 0.95,
|
|
98
|
+
enabled: true,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'SEC-006',
|
|
102
|
+
category: 'secrets',
|
|
103
|
+
owasp: 'A02:2021',
|
|
104
|
+
severity: 'CRITICAL',
|
|
105
|
+
name: 'GitHub personal access token exposed',
|
|
106
|
+
description: 'A GitHub personal access token (ghp_...) is exposed in source code.',
|
|
107
|
+
patterns: [
|
|
108
|
+
/\bghp_[A-Za-z0-9_]{36,}\b/,
|
|
109
|
+
/(?:github[_-]?token|GITHUB_TOKEN|gh[_-]?token)\s*[:=]\s*['"`]ghp_[A-Za-z0-9_]+['"`]/i,
|
|
110
|
+
],
|
|
111
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
112
|
+
fix: 'Use GITHUB_TOKEN in Actions workflows, or store PATs in repository secrets.',
|
|
113
|
+
confidence: 0.95,
|
|
114
|
+
enabled: true,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 'SEC-007',
|
|
118
|
+
category: 'secrets',
|
|
119
|
+
owasp: 'A02:2021',
|
|
120
|
+
severity: 'CRITICAL',
|
|
121
|
+
name: 'RSA private key exposed',
|
|
122
|
+
description: 'An RSA private key block is embedded in source code. Private keys must never be committed.',
|
|
123
|
+
patterns: [
|
|
124
|
+
/-----BEGIN\s+(?:RSA|ENCRYPTED)\s+PRIVATE\s+KEY-----/,
|
|
125
|
+
/['"`]-----BEGIN\s+(?:RSA|ENCRYPTED)\s+PRIVATE\s+KEY-----/,
|
|
126
|
+
],
|
|
127
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
128
|
+
fix: 'Store private keys outside the repository; use a secrets manager or PKI infrastructure.',
|
|
129
|
+
confidence: 0.95,
|
|
130
|
+
enabled: true,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: 'SEC-008',
|
|
134
|
+
category: 'secrets',
|
|
135
|
+
owasp: 'A02:2021',
|
|
136
|
+
severity: 'CRITICAL',
|
|
137
|
+
name: 'EC private key exposed',
|
|
138
|
+
description: 'An elliptic-curve (EC) private key block is embedded in source code.',
|
|
139
|
+
patterns: [
|
|
140
|
+
/-----BEGIN\s+EC\s+PRIVATE\s+KEY-----/,
|
|
141
|
+
/['"`]-----BEGIN\s+EC\s+PRIVATE\s+KEY-----/,
|
|
142
|
+
],
|
|
143
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
144
|
+
fix: 'Store private keys outside the repository; use a secrets manager or PKI infrastructure.',
|
|
145
|
+
confidence: 0.95,
|
|
146
|
+
enabled: true,
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: 'SEC-009',
|
|
150
|
+
category: 'secrets',
|
|
151
|
+
owasp: 'A02:2021',
|
|
152
|
+
severity: 'HIGH',
|
|
153
|
+
name: 'Generic token / bearer token hardcoded',
|
|
154
|
+
description: 'A token or bearer token appears to be hardcoded in source code.',
|
|
155
|
+
patterns: [
|
|
156
|
+
/(?:token|authToken|accessToken|bearerToken)\s*[:=]\s*['"`][A-Za-z0-9_\-+=.]{20,}['"`]/i,
|
|
157
|
+
/(?:authorization|Authorization)\s*[:=]\s*['"`]Bearer\s+[A-Za-z0-9_\-+=.]+['"`]/i,
|
|
158
|
+
],
|
|
159
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
160
|
+
fix: 'Use environment variables or a secure token store. Rotate the exposed token.',
|
|
161
|
+
confidence: 0.88,
|
|
162
|
+
enabled: true,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: 'SEC-010',
|
|
166
|
+
category: 'secrets',
|
|
167
|
+
owasp: 'A02:2021',
|
|
168
|
+
severity: 'HIGH',
|
|
169
|
+
name: 'Sensitive environment variable or AUTH_TOKEN',
|
|
170
|
+
description: 'AUTH_TOKEN or similar sensitive credential is set to a literal value rather than referencing an external secret.',
|
|
171
|
+
patterns: [
|
|
172
|
+
/AUTH_TOKEN\s*=\s*['"`][^'"`]{8,}['"`]/i,
|
|
173
|
+
/(?:\.env\s*(?:file)?\s*[:=]\s*['"`][^'"`]*['"`])/i,
|
|
174
|
+
/process\.env\.([A-Z_]+)\s*=\s*['"`][^'"`]{8,}['"`]/,
|
|
175
|
+
],
|
|
176
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
177
|
+
fix: 'Never assign secret values to process.env in code. Use external .env files (gitignored) or a secrets manager.',
|
|
178
|
+
confidence: 0.85,
|
|
179
|
+
enabled: true,
|
|
180
|
+
},
|
|
181
|
+
// ═══════════════════════════════════════════════════════════════
|
|
182
|
+
// SEC-011 – SEC-019 : Injection (A03:2021)
|
|
183
|
+
// ═══════════════════════════════════════════════════════════════
|
|
184
|
+
{
|
|
185
|
+
id: 'SEC-011',
|
|
186
|
+
category: 'injection',
|
|
187
|
+
owasp: 'A03:2021',
|
|
188
|
+
severity: 'CRITICAL',
|
|
189
|
+
name: 'Use of eval()',
|
|
190
|
+
description: 'eval() executes arbitrary code from a string and is extremely dangerous. Almost always avoidable.',
|
|
191
|
+
patterns: [
|
|
192
|
+
/\beval\s*\(\s*[^)]*\s*\)/,
|
|
193
|
+
/\beval\s*\(\s*`[^`]*\$\{[^}]*\}[^`]*`\s*\)/,
|
|
194
|
+
],
|
|
195
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
196
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
197
|
+
fix: 'Replace eval() with safer alternatives: JSON.parse() for data, or design a non-eval code path.',
|
|
198
|
+
confidence: 0.92,
|
|
199
|
+
enabled: true,
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
id: 'SEC-012',
|
|
203
|
+
category: 'injection',
|
|
204
|
+
owasp: 'A03:2021',
|
|
205
|
+
severity: 'CRITICAL',
|
|
206
|
+
name: 'Use of exec() in Python',
|
|
207
|
+
description: 'Python exec() executes arbitrary code from a string. Avoid using it.',
|
|
208
|
+
patterns: [
|
|
209
|
+
/\bexec\s*\(\s*[^)]*\s*\)/,
|
|
210
|
+
/\bexec\s*\(\s*(?:f['"]|['"]\s*%\s*)/,
|
|
211
|
+
],
|
|
212
|
+
fileGlob: '**/*.py',
|
|
213
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
214
|
+
fix: 'Avoid exec(). Use getattr(), importlib, or restructure logic to not require dynamic execution.',
|
|
215
|
+
confidence: 0.90,
|
|
216
|
+
enabled: true,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: 'SEC-013',
|
|
220
|
+
category: 'injection',
|
|
221
|
+
owasp: 'A03:2021',
|
|
222
|
+
severity: 'HIGH',
|
|
223
|
+
name: 'child_process.exec with dynamic input',
|
|
224
|
+
description: 'child_process.exec() spawns a shell — user-controlled input can lead to command injection.',
|
|
225
|
+
patterns: [
|
|
226
|
+
/child_process\s*\.\s*exec\s*\(\s*[^)]*(?:\+|\$\{|`)[^)]*\)/,
|
|
227
|
+
/require\s*\(\s*['"`]child_process['"`]\s*\)\s*\.\s*exec\s*\(/,
|
|
228
|
+
/\bexec\s*\(\s*['"`][^'"`]*\$\{[^}]*\}[^'"`]*['"`]/,
|
|
229
|
+
],
|
|
230
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
231
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
232
|
+
fix: 'Use child_process.execFile() or child_process.spawn() with argument arrays instead of string commands.',
|
|
233
|
+
confidence: 0.88,
|
|
234
|
+
enabled: true,
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
id: 'SEC-014',
|
|
238
|
+
category: 'injection',
|
|
239
|
+
owasp: 'A03:2021',
|
|
240
|
+
severity: 'CRITICAL',
|
|
241
|
+
name: 'SQL query built via string concatenation',
|
|
242
|
+
description: 'SQL query is built with string concatenation or interpolation — vulnerable to SQL injection.',
|
|
243
|
+
patterns: [
|
|
244
|
+
/(?:SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|CREATE)\s.*(?:\+\s*|`\$\{)/i,
|
|
245
|
+
/(?:query|sql|q)\s*=\s*`[^`]*(?:SELECT|INSERT|UPDATE|DELETE)\b[^`]*\$\{[^}]*\}[^`]*`/i,
|
|
246
|
+
/(?:query|sql|q)\s*=\s*['"].*\b(?:SELECT|INSERT|UPDATE|DELETE)\b.*(?:\+\s*["'`]|\+\s*\w+)/i,
|
|
247
|
+
],
|
|
248
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs,py,java,go,rs,rb,php}',
|
|
249
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
250
|
+
fix: 'Use parameterized queries or an ORM with safe query builders (e.g., $1 placeholders, Prisma, SQLAlchemy ORM).',
|
|
251
|
+
confidence: 0.91,
|
|
252
|
+
enabled: true,
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
id: 'SEC-015',
|
|
256
|
+
category: 'injection',
|
|
257
|
+
owasp: 'A03:2021',
|
|
258
|
+
severity: 'CRITICAL',
|
|
259
|
+
name: 'Use of new Function() constructor',
|
|
260
|
+
description: 'The Function constructor evaluates a string as code, similar to eval(). Extremely dangerous.',
|
|
261
|
+
patterns: [
|
|
262
|
+
/\bnew\s+Function\s*\(\s*[^)]*\s*\)/,
|
|
263
|
+
/\bFunction\s*\(\s*[^)]*(?:\+|\$\{)[^)]*\)/,
|
|
264
|
+
],
|
|
265
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
266
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
267
|
+
fix: 'Avoid the Function constructor. Use closures, higher-order functions, or refactor logic.',
|
|
268
|
+
confidence: 0.92,
|
|
269
|
+
enabled: true,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
id: 'SEC-016',
|
|
273
|
+
category: 'injection',
|
|
274
|
+
owasp: 'A03:2021',
|
|
275
|
+
severity: 'HIGH',
|
|
276
|
+
name: 'innerHTML assignment with dynamic content',
|
|
277
|
+
description: 'Assigning user-controlled content to innerHTML enables XSS attacks.',
|
|
278
|
+
patterns: [
|
|
279
|
+
/\.innerHTML\s*=\s*(?!['"`]\s*['"`])/,
|
|
280
|
+
/\.innerHTML\s*=\s*`[^`]*\$\{[^}]*\}[^`]*`/,
|
|
281
|
+
/\.innerHTML\s*=\s*\w+/,
|
|
282
|
+
],
|
|
283
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
284
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
285
|
+
fix: 'Use textContent, createElement, or a sanitization library like DOMPurify before setting innerHTML.',
|
|
286
|
+
confidence: 0.87,
|
|
287
|
+
enabled: true,
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
id: 'SEC-017',
|
|
291
|
+
category: 'injection',
|
|
292
|
+
owasp: 'A03:2021',
|
|
293
|
+
severity: 'HIGH',
|
|
294
|
+
name: 'dangerouslySetInnerHTML in React',
|
|
295
|
+
description: 'React dangerouslySetInnerHTML bypasses React XSS protections. Use with caution.',
|
|
296
|
+
patterns: [
|
|
297
|
+
/dangerouslySetInnerHTML\s*[=:]\s*\{/,
|
|
298
|
+
/dangerouslySetInnerHTML\s*:/,
|
|
299
|
+
/__html\s*:\s*(?!['"`]\s*['"`])/,
|
|
300
|
+
],
|
|
301
|
+
fileGlob: '**/*.{jsx,tsx}',
|
|
302
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
303
|
+
fix: 'Sanitize HTML content with DOMPurify before passing to dangerouslySetInnerHTML, or use React elements instead.',
|
|
304
|
+
confidence: 0.90,
|
|
305
|
+
enabled: true,
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
id: 'SEC-018',
|
|
309
|
+
category: 'injection',
|
|
310
|
+
owasp: 'A03:2021',
|
|
311
|
+
severity: 'HIGH',
|
|
312
|
+
name: 'Use of document.write()',
|
|
313
|
+
description: 'document.write() can be used for DOM-based XSS attacks and should be avoided.',
|
|
314
|
+
patterns: [
|
|
315
|
+
/\.write\s*\(\s*[^)]*(?:\+|\$\{|`)[^)]*\)/,
|
|
316
|
+
/\bdocument\s*\.\s*write\s*\(/,
|
|
317
|
+
/\bdocument\s*\.\s*writeln\s*\(/,
|
|
318
|
+
],
|
|
319
|
+
fileGlob: '**/*.{js,jsx,ts,tsx}',
|
|
320
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
321
|
+
fix: 'Use DOM manipulation APIs (createElement, appendChild) or framework-specific rendering.',
|
|
322
|
+
confidence: 0.88,
|
|
323
|
+
enabled: true,
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
id: 'SEC-019',
|
|
327
|
+
category: 'injection',
|
|
328
|
+
owasp: 'A03:2021',
|
|
329
|
+
severity: 'MEDIUM',
|
|
330
|
+
name: 'setTimeout / setInterval with string argument',
|
|
331
|
+
description: 'Passing a string to setTimeout or setInterval evaluates it as code (like eval).',
|
|
332
|
+
patterns: [
|
|
333
|
+
/\bsetTimeout\s*\(\s*['"`][^'"`]+\$\{[^}]*\}[^'"`]*['"`]/,
|
|
334
|
+
/\bsetInterval\s*\(\s*['"`][^'"`]+\$\{[^}]*\}[^'"`]*['"`]/,
|
|
335
|
+
/\bsetTimeout\s*\(\s*['"`][^'"`)]+\+\s*\w+/,
|
|
336
|
+
/\bsetInterval\s*\(\s*['"`][^'"`)]+\+\s*\w+/,
|
|
337
|
+
],
|
|
338
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
339
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
340
|
+
fix: 'Pass a function reference instead of a string to setTimeout/setInterval.',
|
|
341
|
+
confidence: 0.85,
|
|
342
|
+
enabled: true,
|
|
343
|
+
},
|
|
344
|
+
// ═══════════════════════════════════════════════════════════════
|
|
345
|
+
// SEC-020 – SEC-023 : Unicode (A03:2021 / various)
|
|
346
|
+
// ═══════════════════════════════════════════════════════════════
|
|
347
|
+
{
|
|
348
|
+
id: 'SEC-020',
|
|
349
|
+
category: 'unicode',
|
|
350
|
+
owasp: 'A03:2021',
|
|
351
|
+
severity: 'HIGH',
|
|
352
|
+
name: 'Bidi override characters detected',
|
|
353
|
+
description: 'Unicode bidirectional override characters (U+202A–U+202E, U+2066–U+2069) can be used for trojan source attacks.',
|
|
354
|
+
patterns: [
|
|
355
|
+
/[\u202A\u202B\u202C\u202D\u202E]/,
|
|
356
|
+
/[\u2066\u2067\u2068\u2069]/,
|
|
357
|
+
],
|
|
358
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
359
|
+
fix: 'Remove bidi override characters. Use explicit markup for bidirectional text if needed.',
|
|
360
|
+
confidence: 0.95,
|
|
361
|
+
enabled: true,
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
id: 'SEC-021',
|
|
365
|
+
category: 'unicode',
|
|
366
|
+
owasp: 'A03:2021',
|
|
367
|
+
severity: 'MEDIUM',
|
|
368
|
+
name: 'Zero-width characters detected',
|
|
369
|
+
description: 'Zero-width characters (U+200B, U+200C, U+200D, U+FEFF) can be used to hide code or confuse reviewers.',
|
|
370
|
+
patterns: [
|
|
371
|
+
/[\u200B\u200C\u200D\uFEFF]/,
|
|
372
|
+
/[\u00AD\u2060\u2061\u2062\u2063\u2064]/,
|
|
373
|
+
],
|
|
374
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
375
|
+
fix: 'Remove invisible/zero-width characters. These are often introduced accidentally or maliciously.',
|
|
376
|
+
confidence: 0.93,
|
|
377
|
+
enabled: true,
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
id: 'SEC-022',
|
|
381
|
+
category: 'unicode',
|
|
382
|
+
owasp: 'A03:2021',
|
|
383
|
+
severity: 'HIGH',
|
|
384
|
+
name: 'Potential homoglyph attack',
|
|
385
|
+
description: 'Confusable Unicode characters (e.g., Cyrillic "а" vs Latin "a") detected outside of string literals.',
|
|
386
|
+
patterns: [
|
|
387
|
+
/[а-яА-Я]/,
|
|
388
|
+
/[\u0391\u0392\u0395\u0396\u0397\u0399\u039A\u039C\u039D\u039F\u03A1\u03A4\u03A5\u03A7]/,
|
|
389
|
+
/[\u0430\u0435\u043E\u0441\u0443\u0445]/,
|
|
390
|
+
],
|
|
391
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,py,java,go,rs,rb,php}',
|
|
392
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
393
|
+
fix: 'Replace homoglyph characters with their intended ASCII/Latin equivalents. Review for malicious intent.',
|
|
394
|
+
confidence: 0.80,
|
|
395
|
+
enabled: true,
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
id: 'SEC-023',
|
|
399
|
+
category: 'unicode',
|
|
400
|
+
owasp: 'A03:2021',
|
|
401
|
+
severity: 'LOW',
|
|
402
|
+
name: 'Hidden Unicode in comments',
|
|
403
|
+
description: 'Unicode control or invisible characters found inside comments may indicate an attempt to hide code.',
|
|
404
|
+
patterns: [
|
|
405
|
+
/\/\/[^\n]*[\u200B\u200C\u200D\u200E\u200F\u202A-\u202E\u2066-\u2069][^\n]*/,
|
|
406
|
+
/\/\*[^*]*[\u200B\u200C\u200D\u200E\u200F\u202A-\u202E\u2066-\u2069][^*]*\*\//,
|
|
407
|
+
/#[^\n]*[\u200B\u200C\u200D\u200E\u200F\u202A-\u202E\u2066-\u2069][^\n]*/,
|
|
408
|
+
],
|
|
409
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
410
|
+
fix: 'Remove hidden Unicode characters from comments during code review.',
|
|
411
|
+
confidence: 0.78,
|
|
412
|
+
enabled: true,
|
|
413
|
+
},
|
|
414
|
+
// ═══════════════════════════════════════════════════════════════
|
|
415
|
+
// SEC-024 – SEC-030 : Dangerous (A03:2021 / A08:2021)
|
|
416
|
+
// ═══════════════════════════════════════════════════════════════
|
|
417
|
+
{
|
|
418
|
+
id: 'SEC-024',
|
|
419
|
+
category: 'dangerous',
|
|
420
|
+
owasp: 'A03:2021',
|
|
421
|
+
severity: 'CRITICAL',
|
|
422
|
+
name: 'Use of os.system() with dynamic input',
|
|
423
|
+
description: 'os.system() passes a string to the shell — user input can lead to command injection.',
|
|
424
|
+
patterns: [
|
|
425
|
+
/\bos\.system\s*\(\s*(?:f['"]|['"][^'"]*\s*%\s*)/,
|
|
426
|
+
/\bos\.system\s*\(\s*[^'")]*\s*\+\s*/,
|
|
427
|
+
/\bos\.system\s*\(\s*[^'")]*(?:\{|\+format\()/,
|
|
428
|
+
],
|
|
429
|
+
fileGlob: '**/*.py',
|
|
430
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
431
|
+
fix: 'Use subprocess.run() with a list of arguments and shell=False (the default).',
|
|
432
|
+
confidence: 0.90,
|
|
433
|
+
enabled: true,
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
id: 'SEC-025',
|
|
437
|
+
category: 'dangerous',
|
|
438
|
+
owasp: 'A03:2021',
|
|
439
|
+
severity: 'CRITICAL',
|
|
440
|
+
name: 'subprocess with shell=True',
|
|
441
|
+
description: 'subprocess calls with shell=True are vulnerable to command injection if input is not strictly sanitized.',
|
|
442
|
+
patterns: [
|
|
443
|
+
/subprocess\s*\.\s*(?:call|run|Popen|check_output|check_call)\s*\([^)]*shell\s*=\s*True/,
|
|
444
|
+
/\bshell\s*=\s*True\b.*subprocess/,
|
|
445
|
+
],
|
|
446
|
+
fileGlob: '**/*.py',
|
|
447
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
448
|
+
fix: 'Use shell=False (default) and pass command arguments as a list. Validate all user input.',
|
|
449
|
+
confidence: 0.90,
|
|
450
|
+
enabled: true,
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
id: 'SEC-026',
|
|
454
|
+
category: 'dangerous',
|
|
455
|
+
owasp: 'A03:2021',
|
|
456
|
+
severity: 'HIGH',
|
|
457
|
+
name: 'Runtime.exec() with dynamic input',
|
|
458
|
+
description: 'Java Runtime.exec() with string concatenation is vulnerable to command injection.',
|
|
459
|
+
patterns: [
|
|
460
|
+
/Runtime\s*\.\s*getRuntime\s*\(\s*\)\s*\.\s*exec\s*\([^)]*\s*\+\s*/,
|
|
461
|
+
/\bRuntime\s*\.\s*getRuntime\s*\(\s*\)\s*\.\s*exec\s*\(\s*"/,
|
|
462
|
+
],
|
|
463
|
+
fileGlob: '**/*.java',
|
|
464
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
465
|
+
fix: 'Use ProcessBuilder with a List<String> of arguments instead of Runtime.exec() with a single string.',
|
|
466
|
+
confidence: 0.85,
|
|
467
|
+
enabled: true,
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
id: 'SEC-027',
|
|
471
|
+
category: 'dangerous',
|
|
472
|
+
owasp: 'A03:2021',
|
|
473
|
+
severity: 'HIGH',
|
|
474
|
+
name: 'child_process.spawn with shell:true',
|
|
475
|
+
description: 'spawn() or exec() with shell:true enables shell interpretation — treat user input as untrusted.',
|
|
476
|
+
patterns: [
|
|
477
|
+
/spawn\s*\([^)]*\{\s*shell\s*:\s*true\s*\}/,
|
|
478
|
+
/\bshell\s*:\s*true\b/,
|
|
479
|
+
/spawn\s*\(\s*[^)]*(?:\+\s*|\$\{)[^)]*\)/,
|
|
480
|
+
],
|
|
481
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
482
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
483
|
+
fix: 'Set shell:false (default) and pass arguments as an array. Sanitize all user-supplied input.',
|
|
484
|
+
confidence: 0.86,
|
|
485
|
+
enabled: true,
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
id: 'SEC-028',
|
|
489
|
+
category: 'dangerous',
|
|
490
|
+
owasp: 'A08:2021',
|
|
491
|
+
severity: 'CRITICAL',
|
|
492
|
+
name: 'Use of pickle.loads() on untrusted data',
|
|
493
|
+
description: 'pickle.loads() can execute arbitrary code when deserializing untrusted data.',
|
|
494
|
+
patterns: [
|
|
495
|
+
/\bpickle\s*\.\s*loads?\s*\(/,
|
|
496
|
+
/\bpickle\s*\.\s*load\s*\(/,
|
|
497
|
+
/\bcPickle\s*\.\s*loads?\s*\(/,
|
|
498
|
+
],
|
|
499
|
+
fileGlob: '**/*.py',
|
|
500
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
501
|
+
fix: 'Use json.loads() or a safe serialization format. Never unpickle data from untrusted sources.',
|
|
502
|
+
confidence: 0.92,
|
|
503
|
+
enabled: true,
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
id: 'SEC-029',
|
|
507
|
+
category: 'dangerous',
|
|
508
|
+
owasp: 'A08:2021',
|
|
509
|
+
severity: 'CRITICAL',
|
|
510
|
+
name: 'Use of unserialize() on untrusted data',
|
|
511
|
+
description: 'PHP unserialize() can trigger object instantiation and lead to RCE via gadget chains.',
|
|
512
|
+
patterns: [
|
|
513
|
+
/\bunserialize\s*\(\s*/,
|
|
514
|
+
/\bunserialize\s*\(\s*\$/,
|
|
515
|
+
],
|
|
516
|
+
fileGlob: '**/*.php',
|
|
517
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
518
|
+
fix: 'Use json_decode() instead of unserialize(). If unserialize() is required, restrict allowed_classes.',
|
|
519
|
+
confidence: 0.90,
|
|
520
|
+
enabled: true,
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
id: 'SEC-030',
|
|
524
|
+
category: 'dangerous',
|
|
525
|
+
owasp: 'A03:2021',
|
|
526
|
+
severity: 'CRITICAL',
|
|
527
|
+
name: 'Dynamic require / import with variable path',
|
|
528
|
+
description: 'Loading modules via require() or import() with a dynamic path can enable code injection.',
|
|
529
|
+
patterns: [
|
|
530
|
+
/\brequire\s*\(\s*\w+\s*\+\s*/,
|
|
531
|
+
/\bimport\s*\(\s*\w+\s*\+\s*/,
|
|
532
|
+
/\brequire\s*\(\s*`[^`]*\$\{[^}]*\}[^`]*`\s*\)/,
|
|
533
|
+
/\bimport\s*\(\s*`[^`]*\$\{[^}]*\}[^`]*`\s*\)/,
|
|
534
|
+
],
|
|
535
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
536
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
537
|
+
fix: 'Avoid dynamic module loading. Use static imports or maintain an allowlist of safe module paths.',
|
|
538
|
+
confidence: 0.84,
|
|
539
|
+
enabled: true,
|
|
540
|
+
},
|
|
541
|
+
// ═══════════════════════════════════════════════════════════════
|
|
542
|
+
// SEC-031 – SEC-035 : Config (A05:2021 / A04:2021)
|
|
543
|
+
// ═══════════════════════════════════════════════════════════════
|
|
544
|
+
{
|
|
545
|
+
id: 'SEC-031',
|
|
546
|
+
category: 'config',
|
|
547
|
+
owasp: 'A05:2021',
|
|
548
|
+
severity: 'HIGH',
|
|
549
|
+
name: 'Dangerously skip middleware permissions',
|
|
550
|
+
description: 'Skipping authentication/authorization middleware (e.g., Next.js matcher config) exposes routes.',
|
|
551
|
+
patterns: [
|
|
552
|
+
/config\s*=\s*\{\s*matcher\s*:\s*\[\s*['"`](?!\/)/,
|
|
553
|
+
/dangerously.*skip.*permission/i,
|
|
554
|
+
/skipAuth\s*:\s*true/i,
|
|
555
|
+
/publicRoutes\s*:\s*\[\s*['"`]\*['"`]/,
|
|
556
|
+
],
|
|
557
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
558
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
559
|
+
fix: 'Ensure middleware matcher config explicitly lists protected routes. Do not use wildcard exclusions.',
|
|
560
|
+
confidence: 0.82,
|
|
561
|
+
enabled: true,
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
id: 'SEC-032',
|
|
565
|
+
category: 'config',
|
|
566
|
+
owasp: 'A05:2021',
|
|
567
|
+
severity: 'HIGH',
|
|
568
|
+
name: 'CORS configured with wildcard origin',
|
|
569
|
+
description: 'CORS Access-Control-Allow-Origin set to "*" combined with credentials:true is insecure.',
|
|
570
|
+
patterns: [
|
|
571
|
+
/Access-Control-Allow-Origin\s*[=:]\s*['"`]\*['"`]/,
|
|
572
|
+
/origin\s*:\s*['"`]\*['"`].*credentials\s*:\s*true/,
|
|
573
|
+
/credentials\s*:\s*true.*origin\s*:\s*['"`]\*['"`]/,
|
|
574
|
+
/\bcors\s*\(\s*\{\s*origin\s*:\s*['"`]\*['"`]\s*,?\s*credentials\s*:\s*true\s*\}/,
|
|
575
|
+
],
|
|
576
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs,py}',
|
|
577
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
578
|
+
fix: 'Replace "*" with an explicit allowlist of trusted origins. Never pair "*" with credentials:true.',
|
|
579
|
+
confidence: 0.88,
|
|
580
|
+
enabled: true,
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
id: 'SEC-033',
|
|
584
|
+
category: 'config',
|
|
585
|
+
owasp: 'A05:2021',
|
|
586
|
+
severity: 'HIGH',
|
|
587
|
+
name: 'Cookie set with secure:false',
|
|
588
|
+
description: 'Cookies without the Secure flag can be transmitted over unencrypted HTTP connections.',
|
|
589
|
+
patterns: [
|
|
590
|
+
/secure\s*:\s*false/,
|
|
591
|
+
/\bcookie\s*\(\s*[^)]*secure\s*:\s*false[^)]*\)/,
|
|
592
|
+
/Set-Cookie\s*[=:].*;\s*Secure\s*=\s*false/i,
|
|
593
|
+
/res\.cookie\s*\([^)]*\)(?!.*secure\s*:\s*true)/,
|
|
594
|
+
],
|
|
595
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs,py}',
|
|
596
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
597
|
+
fix: 'Set secure:true on all cookies in production. Use HTTPS everywhere.',
|
|
598
|
+
confidence: 0.85,
|
|
599
|
+
enabled: true,
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
id: 'SEC-034',
|
|
603
|
+
category: 'config',
|
|
604
|
+
owasp: 'A05:2021',
|
|
605
|
+
severity: 'HIGH',
|
|
606
|
+
name: 'Cookie set with httpOnly:false',
|
|
607
|
+
description: 'Cookies without the HttpOnly flag are accessible to client-side JavaScript, enabling XSS-based theft.',
|
|
608
|
+
patterns: [
|
|
609
|
+
/httpOnly\s*:\s*false/,
|
|
610
|
+
/\bcookie\s*\(\s*[^)]*httpOnly\s*:\s*false[^)]*\)/,
|
|
611
|
+
/Set-Cookie\s*[=:].*;\s*HttpOnly\s*=\s*false/i,
|
|
612
|
+
/res\.cookie\s*\([^)]*\)(?!.*httpOnly\s*:\s*true)/,
|
|
613
|
+
],
|
|
614
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs,py}',
|
|
615
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
616
|
+
fix: 'Set httpOnly:true on all session/authentication cookies to prevent JavaScript access.',
|
|
617
|
+
confidence: 0.85,
|
|
618
|
+
enabled: true,
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
id: 'SEC-035',
|
|
622
|
+
category: 'config',
|
|
623
|
+
owasp: 'A04:2021',
|
|
624
|
+
severity: 'MEDIUM',
|
|
625
|
+
name: 'Debug mode enabled in production',
|
|
626
|
+
description: 'Debug/verbose mode enabled in a production-like configuration may leak sensitive information.',
|
|
627
|
+
patterns: [
|
|
628
|
+
/(?:debug|DEBUG)\s*[=:]\s*(?:true|1|['"`](?:true|1|on)['"`])/,
|
|
629
|
+
/(?:NODE_ENV|ENV|environment)\s*[=:]\s*['"`]development['"`]/,
|
|
630
|
+
/DJANGO_DEBUG\s*=\s*True/,
|
|
631
|
+
/FLASK_ENV\s*=\s*['"`]development['"`]/,
|
|
632
|
+
/debugger\s*:\s*true/,
|
|
633
|
+
],
|
|
634
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,py,env,json,yaml,yml,toml,php,ini,cfg}',
|
|
635
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
636
|
+
fix: 'Disable debug mode in production. Use environment-specific config files.',
|
|
637
|
+
confidence: 0.78,
|
|
638
|
+
enabled: true,
|
|
639
|
+
},
|
|
640
|
+
// ═══════════════════════════════════════════════════════════════
|
|
641
|
+
// SEC-036 – SEC-040 : Data Leak (A09:2021)
|
|
642
|
+
// ═══════════════════════════════════════════════════════════════
|
|
643
|
+
{
|
|
644
|
+
id: 'SEC-036',
|
|
645
|
+
category: 'data-leak',
|
|
646
|
+
owasp: 'A09:2021',
|
|
647
|
+
severity: 'HIGH',
|
|
648
|
+
name: 'console.log with password',
|
|
649
|
+
description: 'Logging a variable or value that appears to be a password can leak credentials to log files.',
|
|
650
|
+
patterns: [
|
|
651
|
+
/console\.(?:log|warn|error|info|debug)\s*\([^)]*\bpassword\b[^)]*\)/i,
|
|
652
|
+
/console\.(?:log|warn|error|info|debug)\s*\([^)]*\bpasswd\b[^)]*\)/i,
|
|
653
|
+
/console\.(?:log|warn|error|info|debug)\s*\([^)]*\bpwd\b[^)]*\)/i,
|
|
654
|
+
],
|
|
655
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
656
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
657
|
+
fix: 'Remove or redact password values from log output. Use a logging library with automatic PII redaction.',
|
|
658
|
+
confidence: 0.82,
|
|
659
|
+
enabled: true,
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
id: 'SEC-037',
|
|
663
|
+
category: 'data-leak',
|
|
664
|
+
owasp: 'A09:2021',
|
|
665
|
+
severity: 'HIGH',
|
|
666
|
+
name: 'console.log with token or secret',
|
|
667
|
+
description: 'Logging tokens or secrets can expose credentials in monitoring systems and log aggregators.',
|
|
668
|
+
patterns: [
|
|
669
|
+
/console\.(?:log|warn|error|info|debug)\s*\([^)]*\b(?:token|secret)\b[^)]*\)/i,
|
|
670
|
+
/console\.(?:log|warn|error|info|debug)\s*\([^)]*\bapi[_-]?key\b[^)]*\)/i,
|
|
671
|
+
],
|
|
672
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
673
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
674
|
+
fix: 'Never log tokens or secrets. Mask sensitive values before logging (e.g., log only first 4 chars).',
|
|
675
|
+
confidence: 0.83,
|
|
676
|
+
enabled: true,
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
id: 'SEC-038',
|
|
680
|
+
category: 'data-leak',
|
|
681
|
+
owasp: 'A09:2021',
|
|
682
|
+
severity: 'MEDIUM',
|
|
683
|
+
name: 'Hardcoded URL with embedded credentials',
|
|
684
|
+
description: 'A URL contains embedded username:password credentials (e.g., https://user:pass@host).',
|
|
685
|
+
patterns: [
|
|
686
|
+
/https?:\/\/[^\/\s@]+:[^\/\s@]+@/i,
|
|
687
|
+
/['"`]https?:\/\/[^:'"\s]+:[^@'"\s]+@[^'"`\s]+['"`]/i,
|
|
688
|
+
/(?:DATABASE_URL|DB_URL|MONGO_URL|REDIS_URL)\s*[=:]\s*['"`][^'"`]*:\/\/[^\/\s@]+:[^\/\s@]+@/i,
|
|
689
|
+
],
|
|
690
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,py,env,json,yaml,yml,toml,ini,cfg}',
|
|
691
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
692
|
+
fix: 'Store connection strings without credentials in code; inject credentials from environment variables at runtime.',
|
|
693
|
+
confidence: 0.89,
|
|
694
|
+
enabled: true,
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
id: 'SEC-039',
|
|
698
|
+
category: 'data-leak',
|
|
699
|
+
owasp: 'A09:2021',
|
|
700
|
+
severity: 'MEDIUM',
|
|
701
|
+
name: 'Hardcoded URLs that may expose internal infrastructure',
|
|
702
|
+
description: 'Hardcoded URLs (especially non-HTTPS or internal IPs) may reveal internal network topology.',
|
|
703
|
+
patterns: [
|
|
704
|
+
/['"`]http:\/\/(?:\d{1,3}\.){3}\d{1,3}['"`]/,
|
|
705
|
+
/['"`]http:\/\/localhost:\d+['"`]/,
|
|
706
|
+
/['"`]http:\/\/192\.168\.\d+\.\d+['"`]/,
|
|
707
|
+
/['"`]http:\/\/10\.\d+\.\d+\.\d+['"`]/,
|
|
708
|
+
],
|
|
709
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,py,java,go,rs}',
|
|
710
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
711
|
+
fix: 'Use HTTPS URLs from environment variables. Never hardcode internal IPs.',
|
|
712
|
+
confidence: 0.75,
|
|
713
|
+
enabled: true,
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
id: 'SEC-040',
|
|
717
|
+
category: 'data-leak',
|
|
718
|
+
owasp: 'A09:2021',
|
|
719
|
+
severity: 'LOW',
|
|
720
|
+
name: 'General console.log left in production code',
|
|
721
|
+
description: 'Unconditional console.log statements may leak internal state to the browser console in production.',
|
|
722
|
+
patterns: [
|
|
723
|
+
/^\s*console\.log\s*\(/m,
|
|
724
|
+
/^\s*console\.warn\s*\(/m,
|
|
725
|
+
/^\s*console\.debug\s*\(/m,
|
|
726
|
+
],
|
|
727
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
728
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
729
|
+
fix: 'Replace console.log with a proper logging framework that supports log levels and can be silenced in production.',
|
|
730
|
+
confidence: 0.70,
|
|
731
|
+
enabled: true,
|
|
732
|
+
},
|
|
733
|
+
// ═══════════════════════════════════════════════════════════════
|
|
734
|
+
// SEC-041 – SEC-044 : Crypto (A02:2021)
|
|
735
|
+
// ═══════════════════════════════════════════════════════════════
|
|
736
|
+
{
|
|
737
|
+
id: 'SEC-041',
|
|
738
|
+
category: 'crypto',
|
|
739
|
+
owasp: 'A02:2021',
|
|
740
|
+
severity: 'HIGH',
|
|
741
|
+
name: 'Use of MD5 hash',
|
|
742
|
+
description: 'MD5 is cryptographically broken and unsuitable for security purposes. Use SHA-256 or better.',
|
|
743
|
+
patterns: [
|
|
744
|
+
/\bmd5\s*\(/i,
|
|
745
|
+
/['"`]md5['"`]/i,
|
|
746
|
+
/\bcreateHash\s*\(\s*['"`]md5['"`]/,
|
|
747
|
+
/\bhashlib\.md5\s*\(/,
|
|
748
|
+
/\bMessageDigest\.getInstance\s*\(\s*['"`]MD5['"`]/,
|
|
749
|
+
/\bcrypto\.createHash\s*\(\s*['"`]md5['"`]/,
|
|
750
|
+
],
|
|
751
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,py,java,go,rs}',
|
|
752
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
753
|
+
fix: 'Replace MD5 with SHA-256 or SHA-3 for security-sensitive hashing. MD5 is only acceptable for non-cryptographic checksums.',
|
|
754
|
+
confidence: 0.93,
|
|
755
|
+
enabled: true,
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
id: 'SEC-042',
|
|
759
|
+
category: 'crypto',
|
|
760
|
+
owasp: 'A02:2021',
|
|
761
|
+
severity: 'HIGH',
|
|
762
|
+
name: 'Use of SHA-1 hash',
|
|
763
|
+
description: 'SHA-1 is considered weak. Use SHA-256 or better for cryptographic operations.',
|
|
764
|
+
patterns: [
|
|
765
|
+
/['"`]sha1['"`]/i,
|
|
766
|
+
/\bSHA-1\b/,
|
|
767
|
+
/\bcreateHash\s*\(\s*['"`]sha1?['"`]/i,
|
|
768
|
+
/\bhashlib\.sha1\s*\(/,
|
|
769
|
+
/\bMessageDigest\.getInstance\s*\(\s*['"`]SHA-?1['"`]/,
|
|
770
|
+
],
|
|
771
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,py,java,go,rs,rb}',
|
|
772
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
773
|
+
fix: 'Use SHA-256 or SHA-3 instead of SHA-1 for any security-sensitive operation.',
|
|
774
|
+
confidence: 0.90,
|
|
775
|
+
enabled: true,
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
id: 'SEC-043',
|
|
779
|
+
category: 'crypto',
|
|
780
|
+
owasp: 'A02:2021',
|
|
781
|
+
severity: 'MEDIUM',
|
|
782
|
+
name: 'Math.random() used for cryptographic purposes',
|
|
783
|
+
description: 'Math.random() is not cryptographically secure. Use crypto.randomBytes() or crypto.getRandomValues().',
|
|
784
|
+
patterns: [
|
|
785
|
+
/Math\.random\s*\(\s*\).*(?:token|key|secret|password|nonce|salt|iv)/i,
|
|
786
|
+
/(?:token|key|secret|password|nonce|salt|iv).*Math\.random\s*\(\)/i,
|
|
787
|
+
],
|
|
788
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
789
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
790
|
+
fix: 'Use crypto.randomBytes(), crypto.randomUUID(), or crypto.getRandomValues() for cryptographically secure randomness.',
|
|
791
|
+
confidence: 0.85,
|
|
792
|
+
enabled: true,
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
id: 'SEC-044',
|
|
796
|
+
category: 'crypto',
|
|
797
|
+
owasp: 'A02:2021',
|
|
798
|
+
severity: 'HIGH',
|
|
799
|
+
name: 'Hardcoded cryptographic salt or IV',
|
|
800
|
+
description: 'A hardcoded salt or initialization vector (IV) weakens encryption. Salts and IVs must be unique and random per operation.',
|
|
801
|
+
patterns: [
|
|
802
|
+
/(?:salt|Salt)\s*[:=]\s*['"`][A-Za-z0-9_\-+=/]{4,}['"`]/,
|
|
803
|
+
/(?:iv|IV|initVector|init_vector)\s*[:=]\s*['"`][A-Za-z0-9_\-+=/]{4,}['"`]/,
|
|
804
|
+
/(?:salt|Salt)\s*=\s*(?![A-Za-z_]\w*\.)(?:0x)?[A-Fa-f0-9]{8,}/,
|
|
805
|
+
/(?:iv|IV)\s*=\s*(?![A-Za-z_]\w*\.)(?:0x)?[A-Fa-f0-9]{8,}/,
|
|
806
|
+
],
|
|
807
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,py,java,go,rs,rb,php}',
|
|
808
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
809
|
+
fix: 'Generate a unique, cryptographically random salt/IV per operation using crypto.randomBytes(). Never hardcode.',
|
|
810
|
+
confidence: 0.86,
|
|
811
|
+
enabled: true,
|
|
812
|
+
},
|
|
813
|
+
// ═══════════════════════════════════════════════════════════════
|
|
814
|
+
// SEC-045 – SEC-048 : Auth (A01:2021 / A07:2021)
|
|
815
|
+
// ═══════════════════════════════════════════════════════════════
|
|
816
|
+
{
|
|
817
|
+
id: 'SEC-045',
|
|
818
|
+
category: 'auth',
|
|
819
|
+
owasp: 'A01:2021',
|
|
820
|
+
severity: 'HIGH',
|
|
821
|
+
name: 'Role check via string comparison',
|
|
822
|
+
description: 'Checking user role against a literal string (e.g., role === "admin") can be bypassed if roles are not validated upstream.',
|
|
823
|
+
patterns: [
|
|
824
|
+
/\b(?:role|userRole|user\.role)\s*[=!]==?\s*['"`](?:admin|superadmin|superuser)['"`]/i,
|
|
825
|
+
/\bif\s*\(\s*(?:role|userRole|user\.role)\s*==\s*['"`](?:admin|superadmin|superuser)['"`]/i,
|
|
826
|
+
],
|
|
827
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,py}',
|
|
828
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
829
|
+
fix: 'Use a role hierarchy checker or a dedicated authorization library (e.g., CASL, Oso). Validate RBAC claims from a trusted source.',
|
|
830
|
+
confidence: 0.80,
|
|
831
|
+
enabled: true,
|
|
832
|
+
},
|
|
833
|
+
{
|
|
834
|
+
id: 'SEC-046',
|
|
835
|
+
category: 'auth',
|
|
836
|
+
owasp: 'A01:2021',
|
|
837
|
+
severity: 'CRITICAL',
|
|
838
|
+
name: 'Route handler missing auth middleware',
|
|
839
|
+
description: 'A route handler appears to be defined without authentication/authorization middleware.',
|
|
840
|
+
patterns: [
|
|
841
|
+
/\brouter\s*\.\s*(?:get|post|put|delete|patch)\s*\(\s*['"`]\/(?!auth|login|register|health)[^'"`]*['"`]\s*,\s*(?!.*auth)(?!.*middleware)(?!.*guard)\s*\(/i,
|
|
842
|
+
/\bapp\s*\.\s*(?:get|post|put|delete|patch)\s*\(\s*['"`]\/(?!auth|login|register|health|public)[^'"`]*['"`]\s*,\s*(?!.*auth)(?!.*middleware)(?!.*guard)\s*\(/i,
|
|
843
|
+
],
|
|
844
|
+
fileGlob: '**/*.{js,jsx,ts,tsx}',
|
|
845
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
846
|
+
fix: 'Apply authentication middleware to all protected routes. Use a route grouping pattern to apply middleware consistently.',
|
|
847
|
+
confidence: 0.78,
|
|
848
|
+
enabled: true,
|
|
849
|
+
},
|
|
850
|
+
{
|
|
851
|
+
id: 'SEC-047',
|
|
852
|
+
category: 'auth',
|
|
853
|
+
owasp: 'A01:2021',
|
|
854
|
+
severity: 'MEDIUM',
|
|
855
|
+
name: 'JWT created without expiration',
|
|
856
|
+
description: 'JWTs without an exp claim are valid indefinitely, increasing the blast radius if a token is leaked.',
|
|
857
|
+
patterns: [
|
|
858
|
+
/jwt\.sign\s*\(\s*[^)]*\{(?![^}]*\bexpiresIn\b)[^}]*\}\s*\)/i,
|
|
859
|
+
/jwt\.sign\s*\(\s*[^)]*\{(?![^}]*\bexp\b)[^}]*\}\s*\)/i,
|
|
860
|
+
/sign\s*\(\s*payload[^)]*\)(?!.*expiresIn)/i,
|
|
861
|
+
],
|
|
862
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,py}',
|
|
863
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
864
|
+
fix: 'Always set an expiresIn or exp claim when signing JWTs. Use short-lived tokens with refresh token rotation.',
|
|
865
|
+
confidence: 0.82,
|
|
866
|
+
enabled: true,
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
id: 'SEC-048',
|
|
870
|
+
category: 'auth',
|
|
871
|
+
owasp: 'A07:2021',
|
|
872
|
+
severity: 'HIGH',
|
|
873
|
+
name: 'Session ID passed in URL',
|
|
874
|
+
description: 'Session IDs in URLs are logged in server logs, browser history, and Referer headers.',
|
|
875
|
+
patterns: [
|
|
876
|
+
/session[_-]?id\s*=\s*req\.query/i,
|
|
877
|
+
/req\.query\.(?:session|sid|token)/i,
|
|
878
|
+
/window\.location.*session/i,
|
|
879
|
+
/\?session=/i,
|
|
880
|
+
/\?sid=/i,
|
|
881
|
+
/\?token=\w+/i,
|
|
882
|
+
],
|
|
883
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,py,java,go,rs,php}',
|
|
884
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
885
|
+
fix: 'Use HttpOnly, Secure cookies for session tokens. Never pass session IDs in URL query parameters.',
|
|
886
|
+
confidence: 0.86,
|
|
887
|
+
enabled: true,
|
|
888
|
+
},
|
|
889
|
+
// ═══════════════════════════════════════════════════════════════
|
|
890
|
+
// SEC-049 – SEC-050 : File Access (A01:2021)
|
|
891
|
+
// ═══════════════════════════════════════════════════════════════
|
|
892
|
+
{
|
|
893
|
+
id: 'SEC-049',
|
|
894
|
+
category: 'file-access',
|
|
895
|
+
owasp: 'A01:2021',
|
|
896
|
+
severity: 'CRITICAL',
|
|
897
|
+
name: 'Path traversal with ../',
|
|
898
|
+
description: 'User-controlled input used in file paths with "../" can enable path traversal attacks.',
|
|
899
|
+
patterns: [
|
|
900
|
+
/\b(?:req\.(?:query|params|body)|request\.(?:query|params|body)|input|userInput|filePath)\b[^;]*\.\.\//,
|
|
901
|
+
/path\.join\s*\([^)]*(?:req\.|request\.|params\.|query\.)/,
|
|
902
|
+
/path\.resolve\s*\([^)]*(?:req\.|request\.|params\.|query\.)/,
|
|
903
|
+
/\bfs\s*\.\s*(?:read|write|append|unlink|rmdir|mkdir|open|create)\w*\s*\([^)]*\+\s*/,
|
|
904
|
+
],
|
|
905
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,py,go,rs}',
|
|
906
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
907
|
+
fix: 'Validate and sanitize user input. Use path.resolve() and check that the resolved path is within the allowed directory.',
|
|
908
|
+
confidence: 0.88,
|
|
909
|
+
enabled: true,
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
id: 'SEC-050',
|
|
913
|
+
category: 'file-access',
|
|
914
|
+
owasp: 'A01:2021',
|
|
915
|
+
severity: 'CRITICAL',
|
|
916
|
+
name: 'fs.readFile with user-controlled path',
|
|
917
|
+
description: 'fs.readFile or fs.readFileSync with a user-influenced path enables arbitrary file read attacks.',
|
|
918
|
+
patterns: [
|
|
919
|
+
/\bfs\s*\.\s*readFile(?:Sync)?\s*\(\s*(?:req\.|request\.|params\.|query\.|body\.|\w+\.\w+\s*\+)/,
|
|
920
|
+
/\bfs\s*\.\s*readFile(?:Sync)?\s*\(\s*`[^`]*\$\{[^}]*\}[^`]*`/,
|
|
921
|
+
/\bfs\s*\.\s*readFile(?:Sync)?\s*\(\s*[^)]*(?:req\.params|req\.query|req\.body|request\.params|request\.query|request\.body)/,
|
|
922
|
+
],
|
|
923
|
+
fileGlob: '**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
924
|
+
excludeGlob: DEFAULT_EXCLUDE,
|
|
925
|
+
fix: 'Sanitize file paths. Use an allowlist of permitted paths. Validate that resolved paths stay within an allowed root directory.',
|
|
926
|
+
confidence: 0.90,
|
|
927
|
+
enabled: true,
|
|
928
|
+
},
|
|
929
|
+
];
|
|
930
|
+
// ── Utility functions ──
|
|
931
|
+
export function loadRules() {
|
|
932
|
+
return ALL_RULES.filter((r) => r.enabled);
|
|
933
|
+
}
|
|
934
|
+
export function getRulesByCategory(category) {
|
|
935
|
+
return ALL_RULES.filter((r) => r.category === category);
|
|
936
|
+
}
|
|
937
|
+
export function getRulesBySeverity(severity) {
|
|
938
|
+
return ALL_RULES.filter((r) => r.severity === severity);
|
|
939
|
+
}
|
|
940
|
+
//# sourceMappingURL=rules.js.map
|