@kernlang/review 2.0.0 → 3.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.
Files changed (118) hide show
  1. package/dist/concept-rules/boundary-mutation.d.ts +13 -0
  2. package/dist/concept-rules/boundary-mutation.js +40 -0
  3. package/dist/concept-rules/boundary-mutation.js.map +1 -0
  4. package/dist/concept-rules/ignored-error.d.ts +13 -0
  5. package/dist/concept-rules/ignored-error.js +40 -0
  6. package/dist/concept-rules/ignored-error.js.map +1 -0
  7. package/dist/concept-rules/illegal-dependency.d.ts +13 -0
  8. package/dist/concept-rules/illegal-dependency.js +49 -0
  9. package/dist/concept-rules/illegal-dependency.js.map +1 -0
  10. package/dist/concept-rules/index.d.ts +15 -0
  11. package/dist/concept-rules/index.js +27 -0
  12. package/dist/concept-rules/index.js.map +1 -0
  13. package/dist/concept-rules/unguarded-effect.d.ts +13 -0
  14. package/dist/concept-rules/unguarded-effect.js +58 -0
  15. package/dist/concept-rules/unguarded-effect.js.map +1 -0
  16. package/dist/concept-rules/unrecovered-effect.d.ts +13 -0
  17. package/dist/concept-rules/unrecovered-effect.js +61 -0
  18. package/dist/concept-rules/unrecovered-effect.js.map +1 -0
  19. package/dist/confidence.d.ts +92 -0
  20. package/dist/confidence.js +263 -0
  21. package/dist/confidence.js.map +1 -0
  22. package/dist/differ.js +4 -2
  23. package/dist/differ.js.map +1 -1
  24. package/dist/external-tools.js +7 -3
  25. package/dist/external-tools.js.map +1 -1
  26. package/dist/file-role.d.ts +10 -0
  27. package/dist/file-role.js +80 -0
  28. package/dist/file-role.js.map +1 -0
  29. package/dist/graph.d.ts +11 -0
  30. package/dist/graph.js +152 -0
  31. package/dist/graph.js.map +1 -0
  32. package/dist/index.d.ts +46 -3
  33. package/dist/index.js +313 -27
  34. package/dist/index.js.map +1 -1
  35. package/dist/inferrer.js +123 -25
  36. package/dist/inferrer.js.map +1 -1
  37. package/dist/kern-lint.d.ts +18 -0
  38. package/dist/kern-lint.js +24 -0
  39. package/dist/kern-lint.js.map +1 -0
  40. package/dist/llm-bridge.d.ts +42 -0
  41. package/dist/llm-bridge.js +176 -0
  42. package/dist/llm-bridge.js.map +1 -0
  43. package/dist/llm-review.d.ts +8 -1
  44. package/dist/llm-review.js +20 -7
  45. package/dist/llm-review.js.map +1 -1
  46. package/dist/mappers/ts-concepts.d.ts +9 -0
  47. package/dist/mappers/ts-concepts.js +512 -0
  48. package/dist/mappers/ts-concepts.js.map +1 -0
  49. package/dist/quality-rules.d.ts +3 -3
  50. package/dist/quality-rules.js +3 -11
  51. package/dist/quality-rules.js.map +1 -1
  52. package/dist/reporter.d.ts +19 -3
  53. package/dist/reporter.js +232 -20
  54. package/dist/reporter.js.map +1 -1
  55. package/dist/rules/base.js +164 -15
  56. package/dist/rules/base.js.map +1 -1
  57. package/dist/rules/confidence.d.ts +37 -0
  58. package/dist/rules/confidence.js +159 -0
  59. package/dist/rules/confidence.js.map +1 -0
  60. package/dist/rules/dead-logic.d.ts +13 -0
  61. package/dist/rules/dead-logic.js +386 -0
  62. package/dist/rules/dead-logic.js.map +1 -0
  63. package/dist/rules/express.js +69 -2
  64. package/dist/rules/express.js.map +1 -1
  65. package/dist/rules/ground-layer.d.ts +23 -0
  66. package/dist/rules/ground-layer.js +132 -0
  67. package/dist/rules/ground-layer.js.map +1 -0
  68. package/dist/rules/index.d.ts +1 -1
  69. package/dist/rules/index.js +8 -2
  70. package/dist/rules/index.js.map +1 -1
  71. package/dist/rules/kern-source.d.ts +16 -0
  72. package/dist/rules/kern-source.js +726 -0
  73. package/dist/rules/kern-source.js.map +1 -0
  74. package/dist/rules/nextjs.js +38 -10
  75. package/dist/rules/nextjs.js.map +1 -1
  76. package/dist/rules/null-safety.d.ts +12 -0
  77. package/dist/rules/null-safety.js +121 -0
  78. package/dist/rules/null-safety.js.map +1 -0
  79. package/dist/rules/react.js +64 -1
  80. package/dist/rules/react.js.map +1 -1
  81. package/dist/rules/security-v2.d.ts +12 -0
  82. package/dist/rules/security-v2.js +415 -0
  83. package/dist/rules/security-v2.js.map +1 -0
  84. package/dist/rules/security-v3.d.ts +12 -0
  85. package/dist/rules/security-v3.js +397 -0
  86. package/dist/rules/security-v3.js.map +1 -0
  87. package/dist/rules/security-v4.d.ts +22 -0
  88. package/dist/rules/security-v4.js +688 -0
  89. package/dist/rules/security-v4.js.map +1 -0
  90. package/dist/rules/security.d.ts +12 -0
  91. package/dist/rules/security.js +286 -0
  92. package/dist/rules/security.js.map +1 -0
  93. package/dist/rules/utils.d.ts +7 -0
  94. package/dist/rules/utils.js +21 -0
  95. package/dist/rules/utils.js.map +1 -0
  96. package/dist/rules/vue.js +1 -1
  97. package/dist/rules/vue.js.map +1 -1
  98. package/dist/spec-checker.d.ts +83 -0
  99. package/dist/spec-checker.js +405 -0
  100. package/dist/spec-checker.js.map +1 -0
  101. package/dist/suppression/apply-suppression.d.ts +17 -0
  102. package/dist/suppression/apply-suppression.js +94 -0
  103. package/dist/suppression/apply-suppression.js.map +1 -0
  104. package/dist/suppression/index.d.ts +6 -0
  105. package/dist/suppression/index.js +6 -0
  106. package/dist/suppression/index.js.map +1 -0
  107. package/dist/suppression/parse-directives.d.ts +25 -0
  108. package/dist/suppression/parse-directives.js +161 -0
  109. package/dist/suppression/parse-directives.js.map +1 -0
  110. package/dist/suppression/types.d.ts +32 -0
  111. package/dist/suppression/types.js +5 -0
  112. package/dist/suppression/types.js.map +1 -0
  113. package/dist/taint.d.ts +115 -0
  114. package/dist/taint.js +1052 -0
  115. package/dist/taint.js.map +1 -0
  116. package/dist/types.d.ts +71 -0
  117. package/dist/types.js.map +1 -1
  118. package/package.json +6 -3
@@ -0,0 +1,415 @@
1
+ /**
2
+ * Security v2 rules — deeper security analysis beyond OWASP basics.
3
+ *
4
+ * Covers: JWT verification, cookie hardening, CSRF, CSP strength,
5
+ * path traversal, weak password hashing.
6
+ *
7
+ * All AST-based. Always active regardless of target.
8
+ */
9
+ import { SyntaxKind } from 'ts-morph';
10
+ import { createFingerprint } from '../types.js';
11
+ function span(file, line, col = 1) {
12
+ return { file, startLine: line, startCol: col, endLine: line, endCol: col };
13
+ }
14
+ function finding(ruleId, severity, category, message, file, line, extra) {
15
+ return {
16
+ source: 'kern',
17
+ ruleId,
18
+ severity,
19
+ category,
20
+ message,
21
+ primarySpan: span(file, line),
22
+ fingerprint: createFingerprint(ruleId, line, 1),
23
+ ...extra,
24
+ };
25
+ }
26
+ // ── Rule S9: jwt-weak-verification ───────────────────────────────────────
27
+ // jwt.decode() used for auth (no signature verification),
28
+ // jwt.verify() without algorithms allowlist,
29
+ // weak signing algorithms (HS256 with short secret)
30
+ function jwtWeakVerification(ctx) {
31
+ const findings = [];
32
+ for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
33
+ const callee = call.getExpression();
34
+ // jwt.decode() — no verification at all
35
+ if (callee.getKind() === SyntaxKind.PropertyAccessExpression) {
36
+ const pa = callee;
37
+ const methodName = pa.getName();
38
+ const objText = pa.getExpression().getText();
39
+ if (methodName === 'decode' && /jwt|jsonwebtoken/i.test(objText)) {
40
+ // Check context: if result is used in auth decisions, it's dangerous.
41
+ // Skip if clearly inspection/logging (variable named like 'debug', 'log', 'inspect', 'preview')
42
+ let contextVar = '';
43
+ const parent = call.getParent();
44
+ if (parent?.getKind() === SyntaxKind.VariableDeclaration) {
45
+ contextVar = parent.getName().toLowerCase();
46
+ }
47
+ const isInspection = /debug|log|inspect|preview|display|print/.test(contextVar);
48
+ if (!isInspection) {
49
+ findings.push(finding('jwt-weak-verification', 'warning', 'bug', 'jwt.decode() does not verify signatures — use jwt.verify() for authentication decisions', ctx.filePath, call.getStartLineNumber(), { suggestion: 'Replace jwt.decode() with jwt.verify(token, secret, { algorithms: ["RS256"] })' }));
50
+ }
51
+ }
52
+ // jwt.verify() — check for missing algorithms option
53
+ if (methodName === 'verify' && /jwt|jsonwebtoken/i.test(objText)) {
54
+ const args = call.getArguments();
55
+ // verify(token, secret) — no options at all
56
+ if (args.length < 3) {
57
+ findings.push(finding('jwt-weak-verification', 'warning', 'bug', 'jwt.verify() without algorithms allowlist — accepts any algorithm including "none"', ctx.filePath, call.getStartLineNumber(), { suggestion: 'Add { algorithms: ["RS256"] } as third argument' }));
58
+ }
59
+ else {
60
+ const thirdArg = args[2];
61
+ // verify(token, secret, callback) — callback form, no options
62
+ if (thirdArg.getKind() === SyntaxKind.ArrowFunction ||
63
+ thirdArg.getKind() === SyntaxKind.FunctionExpression ||
64
+ thirdArg.getKind() === SyntaxKind.Identifier) {
65
+ // If 3rd is callback and no 4th arg (options), flag it
66
+ if (args.length < 4 || args[3]?.getKind() === SyntaxKind.ArrowFunction || args[3]?.getKind() === SyntaxKind.FunctionExpression) {
67
+ findings.push(finding('jwt-weak-verification', 'warning', 'bug', 'jwt.verify() callback form without algorithms allowlist', ctx.filePath, call.getStartLineNumber(), { suggestion: 'Add { algorithms: ["RS256"] } as options: jwt.verify(token, secret, options, callback)' }));
68
+ }
69
+ }
70
+ else if (thirdArg.getKind() === SyntaxKind.ObjectLiteralExpression) {
71
+ // verify(token, secret, options) — check options for algorithms
72
+ const obj = thirdArg;
73
+ const hasAlgorithms = obj.getProperties().some(p => {
74
+ if (p.getKind() !== SyntaxKind.PropertyAssignment)
75
+ return false;
76
+ return p.getName() === 'algorithms';
77
+ });
78
+ if (!hasAlgorithms) {
79
+ findings.push(finding('jwt-weak-verification', 'warning', 'bug', 'jwt.verify() options missing "algorithms" — vulnerable to algorithm confusion attacks', ctx.filePath, call.getStartLineNumber(), { suggestion: 'Add algorithms: ["RS256"] to the options object' }));
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ // jose: jwtVerify / SignJWT — check for weak algorithms
86
+ if (callee.getKind() === SyntaxKind.Identifier) {
87
+ const name = callee.getText();
88
+ if (name === 'jwtVerify') {
89
+ const args = call.getArguments();
90
+ if (args.length >= 3) {
91
+ const text = args[2].getText();
92
+ if (text.includes("'none'") || text.includes('"none"')) {
93
+ findings.push(finding('jwt-weak-verification', 'error', 'bug', 'JWT verification allows "none" algorithm — tokens can be forged', ctx.filePath, call.getStartLineNumber()));
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ return findings;
100
+ }
101
+ // ── Rule S10: cookie-hardening ───────────────────────────────────────────
102
+ // Cookies missing httpOnly, secure, sameSite flags
103
+ function cookieHardening(ctx) {
104
+ const findings = [];
105
+ // Pattern 1: res.cookie('name', value, options) — Express
106
+ for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
107
+ const callee = call.getExpression();
108
+ if (callee.getKind() !== SyntaxKind.PropertyAccessExpression)
109
+ continue;
110
+ const pa = callee;
111
+ if (pa.getName() !== 'cookie')
112
+ continue;
113
+ const args = call.getArguments();
114
+ if (args.length < 2)
115
+ continue;
116
+ // Get cookie name to check if it's session/auth related
117
+ const cookieName = args[0].getText().replace(/['"]/g, '').toLowerCase();
118
+ // CSRF cookies must be JS-readable — exclude from auth cookie checks
119
+ const isCsrfCookie = /csrf|xsrf/i.test(cookieName);
120
+ const isAuthCookie = !isCsrfCookie && /session|token|auth|jwt|sid|refresh/i.test(cookieName);
121
+ if (args.length < 3) {
122
+ // No options at all
123
+ if (isAuthCookie) {
124
+ findings.push(finding('cookie-hardening', 'error', 'bug', `Auth cookie '${cookieName}' set without security flags — missing httpOnly, secure, sameSite`, ctx.filePath, call.getStartLineNumber(), { suggestion: "Add { httpOnly: true, secure: true, sameSite: 'strict' } options" }));
125
+ }
126
+ continue;
127
+ }
128
+ const optionsArg = args[2];
129
+ if (optionsArg.getKind() !== SyntaxKind.ObjectLiteralExpression)
130
+ continue;
131
+ const obj = optionsArg;
132
+ const propNames = new Set(obj.getProperties()
133
+ .filter(p => p.getKind() === SyntaxKind.PropertyAssignment)
134
+ .map(p => p.getName()));
135
+ const missing = [];
136
+ if (!propNames.has('httpOnly'))
137
+ missing.push('httpOnly');
138
+ if (!propNames.has('secure'))
139
+ missing.push('secure');
140
+ if (!propNames.has('sameSite'))
141
+ missing.push('sameSite');
142
+ if (missing.length > 0 && isAuthCookie) {
143
+ findings.push(finding('cookie-hardening', 'error', 'bug', `Auth cookie '${cookieName}' missing: ${missing.join(', ')}`, ctx.filePath, call.getStartLineNumber(), { suggestion: `Add ${missing.map(m => `${m}: true`).join(', ')} to cookie options` }));
144
+ }
145
+ else if (missing.length > 0) {
146
+ findings.push(finding('cookie-hardening', 'warning', 'bug', `Cookie '${cookieName}' missing: ${missing.join(', ')}`, ctx.filePath, call.getStartLineNumber()));
147
+ }
148
+ // Check for httpOnly: false on auth cookies
149
+ for (const prop of obj.getProperties()) {
150
+ if (prop.getKind() !== SyntaxKind.PropertyAssignment)
151
+ continue;
152
+ const pa2 = prop;
153
+ if (pa2.getName() === 'httpOnly' && pa2.getInitializer()?.getKind() === SyntaxKind.FalseKeyword && isAuthCookie) {
154
+ findings.push(finding('cookie-hardening', 'error', 'bug', `Auth cookie '${cookieName}' has httpOnly: false — XSS can steal it`, ctx.filePath, call.getStartLineNumber()));
155
+ }
156
+ }
157
+ }
158
+ return findings;
159
+ }
160
+ // ── Rule S11: csrf-detection ─────────────────────────────────────────────
161
+ // Disabled CSRF protection in cookie-auth apps
162
+ function csrfDetection(ctx) {
163
+ const findings = [];
164
+ const fullText = ctx.sourceFile.getFullText();
165
+ // Only fire if the app uses cookie-based auth (per Codex: don't nag bearer-token APIs)
166
+ // Passport with session: false is stateless JWT — not cookie auth
167
+ const hasPassportStateless = fullText.includes('session: false') && fullText.includes('passport');
168
+ const usesCookieAuth = (fullText.includes('cookie-session') ||
169
+ fullText.includes('express-session') ||
170
+ fullText.includes('cookie-parser') ||
171
+ (fullText.includes('passport') && !hasPassportStateless) ||
172
+ /res\.cookie\s*\([^)]*(?:session|auth|token)/i.test(fullText));
173
+ if (!usesCookieAuth)
174
+ return findings;
175
+ // Check for explicit CSRF disable
176
+ for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
177
+ const callee = call.getExpression();
178
+ const callText = callee.getText();
179
+ // csurf with { ignoreMethods: ['POST'] } or similar
180
+ if (callText === 'csrf' || callText === 'csurf') {
181
+ const args = call.getArguments();
182
+ if (args.length > 0) {
183
+ const optText = args[0].getText();
184
+ if (optText.includes('ignoreMethods') && /POST|PUT|DELETE|PATCH/.test(optText)) {
185
+ findings.push(finding('csrf-detection', 'error', 'bug', 'CSRF protection ignores state-changing methods — defeats the purpose', ctx.filePath, call.getStartLineNumber()));
186
+ }
187
+ }
188
+ }
189
+ }
190
+ // Check: cookie-session app with state-changing routes but no CSRF middleware
191
+ const hasCsrf = fullText.includes('csrf') || fullText.includes('csurf') ||
192
+ fullText.includes('csrfToken') || fullText.includes('_csrf');
193
+ const hasStateChangingRoutes = /\.(post|put|delete|patch)\s*\(/.test(fullText);
194
+ if (!hasCsrf && hasStateChangingRoutes) {
195
+ // Find the first route handler line
196
+ const routeMatch = fullText.match(/\.(post|put|delete|patch)\s*\(/);
197
+ if (routeMatch) {
198
+ const line = fullText.substring(0, routeMatch.index).split('\n').length;
199
+ findings.push(finding('csrf-detection', 'warning', 'bug', 'Cookie-auth app has state-changing routes but no CSRF protection', ctx.filePath, line, { suggestion: 'Add CSRF middleware (csrf-csrf, csurf) or use SameSite=Strict cookies' }));
200
+ }
201
+ }
202
+ return findings;
203
+ }
204
+ // ── Rule S12: csp-strength ───────────────────────────────────────────────
205
+ // Weak Content-Security-Policy directives
206
+ function cspStrength(ctx) {
207
+ const findings = [];
208
+ const weakDirectives = [
209
+ { pattern: /unsafe-inline/g, label: 'unsafe-inline', risk: 'allows inline scripts — XSS vector' },
210
+ { pattern: /unsafe-eval/g, label: 'unsafe-eval', risk: 'allows eval() — code injection vector' },
211
+ { pattern: /script-src\s+\*/g, label: "script-src '*'", risk: 'allows scripts from any origin' },
212
+ { pattern: /default-src\s+\*/g, label: "default-src '*'", risk: 'allows all resources from any origin' },
213
+ ];
214
+ // Check string literals that look like CSP directives
215
+ for (const str of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.StringLiteral)) {
216
+ const value = str.getLiteralValue();
217
+ if (!value.includes('-src') && !value.includes('default-src'))
218
+ continue;
219
+ for (const { pattern, label, risk } of weakDirectives) {
220
+ pattern.lastIndex = 0;
221
+ if (pattern.test(value)) {
222
+ findings.push(finding('csp-strength', 'warning', 'bug', `Weak CSP: ${label} — ${risk}`, ctx.filePath, str.getStartLineNumber(), { suggestion: `Remove ${label} and use nonces or hashes instead` }));
223
+ }
224
+ }
225
+ }
226
+ // Check template literals too
227
+ for (const tmpl of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.NoSubstitutionTemplateLiteral)) {
228
+ const value = tmpl.getLiteralValue();
229
+ if (!value.includes('-src') && !value.includes('default-src'))
230
+ continue;
231
+ for (const { pattern, label, risk } of weakDirectives) {
232
+ pattern.lastIndex = 0;
233
+ if (pattern.test(value)) {
234
+ findings.push(finding('csp-strength', 'warning', 'bug', `Weak CSP: ${label} — ${risk}`, ctx.filePath, tmpl.getStartLineNumber()));
235
+ }
236
+ }
237
+ }
238
+ // Check for frame-ancestors missing (clickjacking)
239
+ const fullText = ctx.sourceFile.getFullText();
240
+ if (fullText.includes('helmet') || fullText.includes('contentSecurityPolicy')) {
241
+ const hasFrameAncestors = fullText.includes('frame-ancestors') || fullText.includes('frameAncestors');
242
+ if (!hasFrameAncestors) {
243
+ // Find the CSP config line
244
+ const cspMatch = fullText.match(/contentSecurityPolicy/);
245
+ if (cspMatch) {
246
+ const line = fullText.substring(0, cspMatch.index).split('\n').length;
247
+ findings.push(finding('csp-strength', 'info', 'bug', 'CSP missing frame-ancestors — consider adding to prevent clickjacking', ctx.filePath, line));
248
+ }
249
+ }
250
+ }
251
+ return findings;
252
+ }
253
+ // ── Rule S13: path-traversal ─────────────────────────────────────────────
254
+ // File system operations with user input (req.params, req.query, req.body)
255
+ const FS_READ_FUNCTIONS = new Set([
256
+ 'readFile', 'readFileSync', 'createReadStream',
257
+ 'readdir', 'readdirSync', 'stat', 'statSync',
258
+ 'access', 'accessSync', 'existsSync',
259
+ ]);
260
+ function pathTraversal(ctx) {
261
+ const findings = [];
262
+ for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
263
+ const callee = call.getExpression();
264
+ let funcName = '';
265
+ if (callee.getKind() === SyntaxKind.Identifier) {
266
+ funcName = callee.getText();
267
+ }
268
+ else if (callee.getKind() === SyntaxKind.PropertyAccessExpression) {
269
+ funcName = callee.getName();
270
+ }
271
+ if (!FS_READ_FUNCTIONS.has(funcName))
272
+ continue;
273
+ // Check if any argument contains user input
274
+ const args = call.getArguments();
275
+ if (args.length === 0)
276
+ continue;
277
+ const firstArgText = args[0].getText();
278
+ const hasUserInput = firstArgText.includes('req.params') ||
279
+ firstArgText.includes('req.query') ||
280
+ firstArgText.includes('req.body');
281
+ if (!hasUserInput)
282
+ continue;
283
+ // Check if the same function scope has path validation before the fs call
284
+ const parentBlock = call.getFirstAncestorByKind(SyntaxKind.Block);
285
+ const blockText = parentBlock?.getText() || '';
286
+ const callOffset = call.getStart() - (parentBlock?.getStart() || 0);
287
+ const textBeforeCall = blockText.substring(0, callOffset);
288
+ // Only count validation that appears BEFORE the fs call in the same block
289
+ const hasPathValidation = (textBeforeCall.includes('path.resolve') && textBeforeCall.includes('startsWith')) ||
290
+ (textBeforeCall.includes('path.normalize') && textBeforeCall.includes("'..'"));
291
+ if (!hasPathValidation) {
292
+ findings.push(finding('path-traversal', 'error', 'bug', `${funcName}() with user input — path traversal vulnerability`, ctx.filePath, call.getStartLineNumber(), { suggestion: 'Validate: const safe = path.resolve(baseDir, userInput); if (!safe.startsWith(baseDir)) throw' }));
293
+ }
294
+ }
295
+ // Also check res.sendFile with user input
296
+ for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
297
+ const callee = call.getExpression();
298
+ if (callee.getKind() !== SyntaxKind.PropertyAccessExpression)
299
+ continue;
300
+ const pa = callee;
301
+ if (pa.getName() !== 'sendFile' && pa.getName() !== 'download')
302
+ continue;
303
+ const args = call.getArguments();
304
+ if (args.length === 0)
305
+ continue;
306
+ const argText = args[0].getText();
307
+ if (argText.includes('req.params') || argText.includes('req.query')) {
308
+ // Check if options object with { root } is passed — this is the safe pattern
309
+ const hasRootOption = args.length >= 2 && args.some(a => {
310
+ if (a.getKind() !== SyntaxKind.ObjectLiteralExpression)
311
+ return false;
312
+ return a.getProperties().some(p => {
313
+ if (p.getKind() !== SyntaxKind.PropertyAssignment)
314
+ return false;
315
+ return p.getName() === 'root';
316
+ });
317
+ });
318
+ if (!hasRootOption) {
319
+ findings.push(finding('path-traversal', 'error', 'bug', `res.${pa.getName()}() with user input — path traversal vulnerability`, ctx.filePath, call.getStartLineNumber(), { suggestion: 'Use { root: __dirname } option and validate the path' }));
320
+ }
321
+ }
322
+ }
323
+ return findings;
324
+ }
325
+ // ── Rule S14: weak-password-hashing ──────────────────────────────────────
326
+ // MD5/SHA1 for passwords, raw createHash on passwords, low bcrypt rounds
327
+ function weakPasswordHashing(ctx) {
328
+ const findings = [];
329
+ for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
330
+ const callee = call.getExpression();
331
+ // crypto.createHash('md5'|'sha1') in password context
332
+ if (callee.getKind() === SyntaxKind.PropertyAccessExpression) {
333
+ const pa = callee;
334
+ if (pa.getName() === 'createHash') {
335
+ const args = call.getArguments();
336
+ if (args.length > 0 && args[0].getKind() === SyntaxKind.StringLiteral) {
337
+ const algo = args[0].getLiteralValue().toLowerCase();
338
+ if (algo === 'md5' || algo === 'sha1' || algo === 'sha256') {
339
+ // Check context — is this for password hashing?
340
+ let parent = call.getParent();
341
+ let contextName = '';
342
+ while (parent) {
343
+ if (parent.getKind() === SyntaxKind.FunctionDeclaration) {
344
+ contextName = parent.getName() || '';
345
+ break;
346
+ }
347
+ if (parent.getKind() === SyntaxKind.VariableDeclaration) {
348
+ contextName = parent.getName();
349
+ break;
350
+ }
351
+ parent = parent.getParent();
352
+ }
353
+ const isPasswordContext = /password|passwd|hash.*pass|pass.*hash|credential|secret/i.test(contextName);
354
+ if (algo === 'md5' || algo === 'sha1') {
355
+ // Only flag as error in password context; skip entirely for non-password use
356
+ // (MD5 for checksums/ETags/Gravatar is fine)
357
+ if (isPasswordContext) {
358
+ findings.push(finding('weak-password-hashing', 'error', 'bug', `createHash('${algo}') for password hashing — cryptographically broken`, ctx.filePath, call.getStartLineNumber(), { suggestion: 'Use bcrypt, scrypt, or argon2 for passwords.' }));
359
+ }
360
+ }
361
+ else if (algo === 'sha256' && isPasswordContext) {
362
+ findings.push(finding('weak-password-hashing', 'error', 'bug', 'Raw SHA-256 for password hashing — too fast, vulnerable to brute force', ctx.filePath, call.getStartLineNumber(), { suggestion: 'Use bcrypt (rounds: 12+), scrypt, or argon2 — they are intentionally slow' }));
363
+ }
364
+ }
365
+ }
366
+ }
367
+ }
368
+ // bcrypt.hash with low rounds
369
+ if (callee.getKind() === SyntaxKind.PropertyAccessExpression) {
370
+ const pa = callee;
371
+ if ((pa.getName() === 'hash' || pa.getName() === 'hashSync') &&
372
+ /bcrypt/i.test(pa.getExpression().getText())) {
373
+ const args = call.getArguments();
374
+ // bcrypt.hash(password, saltRounds) — check saltRounds
375
+ if (args.length >= 2) {
376
+ const roundsArg = args[1];
377
+ if (roundsArg.getKind() === SyntaxKind.NumericLiteral) {
378
+ const rounds = parseInt(roundsArg.getText(), 10);
379
+ if (rounds < 10) {
380
+ findings.push(finding('weak-password-hashing', 'warning', 'bug', `bcrypt with ${rounds} rounds — use at least 12 for adequate security`, ctx.filePath, call.getStartLineNumber(), { suggestion: 'Increase salt rounds to 12 or higher' }));
381
+ }
382
+ }
383
+ }
384
+ }
385
+ }
386
+ // pbkdf2 with low iterations
387
+ if (callee.getKind() === SyntaxKind.PropertyAccessExpression) {
388
+ const pa = callee;
389
+ if (pa.getName() === 'pbkdf2' || pa.getName() === 'pbkdf2Sync') {
390
+ const args = call.getArguments();
391
+ // pbkdf2(password, salt, iterations, keylen, digest)
392
+ if (args.length >= 3) {
393
+ const iterArg = args[2];
394
+ if (iterArg.getKind() === SyntaxKind.NumericLiteral) {
395
+ const iterations = parseInt(iterArg.getText(), 10);
396
+ if (iterations < 100000) {
397
+ findings.push(finding('weak-password-hashing', 'warning', 'bug', `pbkdf2 with ${iterations.toLocaleString()} iterations — OWASP recommends 600,000+`, ctx.filePath, call.getStartLineNumber(), { suggestion: 'Increase iterations to at least 600,000 (OWASP 2023 recommendation)' }));
398
+ }
399
+ }
400
+ }
401
+ }
402
+ }
403
+ }
404
+ return findings;
405
+ }
406
+ // ── Exported Security v2 Rules ───────────────────────────────────────────
407
+ export const securityV2Rules = [
408
+ jwtWeakVerification,
409
+ cookieHardening,
410
+ csrfDetection,
411
+ cspStrength,
412
+ pathTraversal,
413
+ weakPasswordHashing,
414
+ ];
415
+ //# sourceMappingURL=security-v2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-v2.js","sourceRoot":"","sources":["../../src/rules/security-v2.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Security v3 rules — OWASP gap closure.
3
+ *
4
+ * Covers: Regex DoS, missing input validation, prototype pollution,
5
+ * information exposure (stack traces in responses).
6
+ *
7
+ * All AST-based. Always active regardless of target.
8
+ */
9
+ import type { ReviewFinding, RuleContext } from '../types.js';
10
+ declare function regexDos(ctx: RuleContext): ReviewFinding[];
11
+ export declare const securityV3Rules: (typeof regexDos)[];
12
+ export {};