@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.
- package/dist/concept-rules/boundary-mutation.d.ts +13 -0
- package/dist/concept-rules/boundary-mutation.js +40 -0
- package/dist/concept-rules/boundary-mutation.js.map +1 -0
- package/dist/concept-rules/ignored-error.d.ts +13 -0
- package/dist/concept-rules/ignored-error.js +40 -0
- package/dist/concept-rules/ignored-error.js.map +1 -0
- package/dist/concept-rules/illegal-dependency.d.ts +13 -0
- package/dist/concept-rules/illegal-dependency.js +49 -0
- package/dist/concept-rules/illegal-dependency.js.map +1 -0
- package/dist/concept-rules/index.d.ts +15 -0
- package/dist/concept-rules/index.js +27 -0
- package/dist/concept-rules/index.js.map +1 -0
- package/dist/concept-rules/unguarded-effect.d.ts +13 -0
- package/dist/concept-rules/unguarded-effect.js +58 -0
- package/dist/concept-rules/unguarded-effect.js.map +1 -0
- package/dist/concept-rules/unrecovered-effect.d.ts +13 -0
- package/dist/concept-rules/unrecovered-effect.js +61 -0
- package/dist/concept-rules/unrecovered-effect.js.map +1 -0
- package/dist/confidence.d.ts +92 -0
- package/dist/confidence.js +263 -0
- package/dist/confidence.js.map +1 -0
- package/dist/differ.js +4 -2
- package/dist/differ.js.map +1 -1
- package/dist/external-tools.js +7 -3
- package/dist/external-tools.js.map +1 -1
- package/dist/file-role.d.ts +10 -0
- package/dist/file-role.js +80 -0
- package/dist/file-role.js.map +1 -0
- package/dist/graph.d.ts +11 -0
- package/dist/graph.js +152 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +46 -3
- package/dist/index.js +313 -27
- package/dist/index.js.map +1 -1
- package/dist/inferrer.js +123 -25
- package/dist/inferrer.js.map +1 -1
- package/dist/kern-lint.d.ts +18 -0
- package/dist/kern-lint.js +24 -0
- package/dist/kern-lint.js.map +1 -0
- package/dist/llm-bridge.d.ts +42 -0
- package/dist/llm-bridge.js +176 -0
- package/dist/llm-bridge.js.map +1 -0
- package/dist/llm-review.d.ts +8 -1
- package/dist/llm-review.js +20 -7
- package/dist/llm-review.js.map +1 -1
- package/dist/mappers/ts-concepts.d.ts +9 -0
- package/dist/mappers/ts-concepts.js +512 -0
- package/dist/mappers/ts-concepts.js.map +1 -0
- package/dist/quality-rules.d.ts +3 -3
- package/dist/quality-rules.js +3 -11
- package/dist/quality-rules.js.map +1 -1
- package/dist/reporter.d.ts +19 -3
- package/dist/reporter.js +232 -20
- package/dist/reporter.js.map +1 -1
- package/dist/rules/base.js +164 -15
- package/dist/rules/base.js.map +1 -1
- package/dist/rules/confidence.d.ts +37 -0
- package/dist/rules/confidence.js +159 -0
- package/dist/rules/confidence.js.map +1 -0
- package/dist/rules/dead-logic.d.ts +13 -0
- package/dist/rules/dead-logic.js +386 -0
- package/dist/rules/dead-logic.js.map +1 -0
- package/dist/rules/express.js +69 -2
- package/dist/rules/express.js.map +1 -1
- package/dist/rules/ground-layer.d.ts +23 -0
- package/dist/rules/ground-layer.js +132 -0
- package/dist/rules/ground-layer.js.map +1 -0
- package/dist/rules/index.d.ts +1 -1
- package/dist/rules/index.js +8 -2
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/kern-source.d.ts +16 -0
- package/dist/rules/kern-source.js +726 -0
- package/dist/rules/kern-source.js.map +1 -0
- package/dist/rules/nextjs.js +38 -10
- package/dist/rules/nextjs.js.map +1 -1
- package/dist/rules/null-safety.d.ts +12 -0
- package/dist/rules/null-safety.js +121 -0
- package/dist/rules/null-safety.js.map +1 -0
- package/dist/rules/react.js +64 -1
- package/dist/rules/react.js.map +1 -1
- package/dist/rules/security-v2.d.ts +12 -0
- package/dist/rules/security-v2.js +415 -0
- package/dist/rules/security-v2.js.map +1 -0
- package/dist/rules/security-v3.d.ts +12 -0
- package/dist/rules/security-v3.js +397 -0
- package/dist/rules/security-v3.js.map +1 -0
- package/dist/rules/security-v4.d.ts +22 -0
- package/dist/rules/security-v4.js +688 -0
- package/dist/rules/security-v4.js.map +1 -0
- package/dist/rules/security.d.ts +12 -0
- package/dist/rules/security.js +286 -0
- package/dist/rules/security.js.map +1 -0
- package/dist/rules/utils.d.ts +7 -0
- package/dist/rules/utils.js +21 -0
- package/dist/rules/utils.js.map +1 -0
- package/dist/rules/vue.js +1 -1
- package/dist/rules/vue.js.map +1 -1
- package/dist/spec-checker.d.ts +83 -0
- package/dist/spec-checker.js +405 -0
- package/dist/spec-checker.js.map +1 -0
- package/dist/suppression/apply-suppression.d.ts +17 -0
- package/dist/suppression/apply-suppression.js +94 -0
- package/dist/suppression/apply-suppression.js.map +1 -0
- package/dist/suppression/index.d.ts +6 -0
- package/dist/suppression/index.js +6 -0
- package/dist/suppression/index.js.map +1 -0
- package/dist/suppression/parse-directives.d.ts +25 -0
- package/dist/suppression/parse-directives.js +161 -0
- package/dist/suppression/parse-directives.js.map +1 -0
- package/dist/suppression/types.d.ts +32 -0
- package/dist/suppression/types.js +5 -0
- package/dist/suppression/types.js.map +1 -0
- package/dist/taint.d.ts +115 -0
- package/dist/taint.js +1052 -0
- package/dist/taint.js.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.js.map +1 -1
- 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 {};
|