@kernlang/review 2.0.0 → 3.1.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 +518 -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 +167 -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 +393 -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 +123 -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 +7 -4
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dead logic rules — catches code that can't do what it claims.
|
|
3
|
+
*
|
|
4
|
+
* Covers: identical conditions, identical expressions, all-identical branches,
|
|
5
|
+
* constant conditions, one-iteration loops, unused collections, empty collections,
|
|
6
|
+
* redundant jumps.
|
|
7
|
+
*
|
|
8
|
+
* All AST-based. Always active. High signal, cheap to run.
|
|
9
|
+
*/
|
|
10
|
+
import { SyntaxKind } from 'ts-morph';
|
|
11
|
+
import { createFingerprint } from '../types.js';
|
|
12
|
+
function span(file, line, col = 1) {
|
|
13
|
+
return { file, startLine: line, startCol: col, endLine: line, endCol: col };
|
|
14
|
+
}
|
|
15
|
+
function finding(ruleId, severity, category, message, file, line, extra) {
|
|
16
|
+
return {
|
|
17
|
+
source: 'kern',
|
|
18
|
+
ruleId,
|
|
19
|
+
severity,
|
|
20
|
+
category,
|
|
21
|
+
message,
|
|
22
|
+
primarySpan: span(file, line),
|
|
23
|
+
fingerprint: createFingerprint(ruleId, line, 1),
|
|
24
|
+
...extra,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
28
|
+
/** Normalize whitespace for structural comparison */
|
|
29
|
+
function normalize(text) {
|
|
30
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
31
|
+
}
|
|
32
|
+
// ── Rule D1: identical-conditions ────────────────────────────────────────
|
|
33
|
+
// if (a) ... else if (a) — same condition in if/else-if chain
|
|
34
|
+
function identicalConditions(ctx) {
|
|
35
|
+
const findings = [];
|
|
36
|
+
for (const ifStmt of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.IfStatement)) {
|
|
37
|
+
const conditions = [];
|
|
38
|
+
let current = ifStmt;
|
|
39
|
+
// Walk the if/else-if chain
|
|
40
|
+
while (current) {
|
|
41
|
+
const condText = normalize(current.getExpression().getText());
|
|
42
|
+
const condLine = current.getStartLineNumber();
|
|
43
|
+
// Check for duplicate
|
|
44
|
+
const duplicate = conditions.find(c => c.text === condText);
|
|
45
|
+
if (duplicate) {
|
|
46
|
+
findings.push(finding('identical-conditions', 'error', 'bug', `Duplicate condition '${condText.substring(0, 60)}' — already checked at line ${duplicate.line}`, ctx.filePath, condLine, { relatedSpans: [span(ctx.filePath, duplicate.line)] }));
|
|
47
|
+
}
|
|
48
|
+
conditions.push({ text: condText, line: condLine });
|
|
49
|
+
// Follow else-if chain
|
|
50
|
+
const elseStmt = current.getElseStatement();
|
|
51
|
+
current = elseStmt?.getKind() === SyntaxKind.IfStatement
|
|
52
|
+
? elseStmt
|
|
53
|
+
: undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return findings;
|
|
57
|
+
}
|
|
58
|
+
// ── Rule D2: identical-expressions ───────────────────────────────────────
|
|
59
|
+
// a === a, x - x, x && x — both sides of binary operator are the same
|
|
60
|
+
const IDENTITY_OPERATORS = new Set([
|
|
61
|
+
SyntaxKind.EqualsEqualsToken,
|
|
62
|
+
SyntaxKind.EqualsEqualsEqualsToken,
|
|
63
|
+
SyntaxKind.ExclamationEqualsToken,
|
|
64
|
+
SyntaxKind.ExclamationEqualsEqualsToken,
|
|
65
|
+
SyntaxKind.MinusToken,
|
|
66
|
+
SyntaxKind.SlashToken,
|
|
67
|
+
SyntaxKind.PercentToken,
|
|
68
|
+
SyntaxKind.AmpersandAmpersandToken,
|
|
69
|
+
SyntaxKind.BarBarToken,
|
|
70
|
+
SyntaxKind.LessThanToken,
|
|
71
|
+
SyntaxKind.LessThanEqualsToken,
|
|
72
|
+
SyntaxKind.GreaterThanToken,
|
|
73
|
+
SyntaxKind.GreaterThanEqualsToken,
|
|
74
|
+
]);
|
|
75
|
+
function identicalExpressions(ctx) {
|
|
76
|
+
const findings = [];
|
|
77
|
+
for (const binExpr of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.BinaryExpression)) {
|
|
78
|
+
const op = binExpr.getOperatorToken().getKind();
|
|
79
|
+
if (!IDENTITY_OPERATORS.has(op))
|
|
80
|
+
continue;
|
|
81
|
+
const leftText = normalize(binExpr.getLeft().getText());
|
|
82
|
+
const rightText = normalize(binExpr.getRight().getText());
|
|
83
|
+
if (leftText === rightText && leftText.length > 0) {
|
|
84
|
+
// Skip intentional patterns: NaN check (x !== x)
|
|
85
|
+
if (op === SyntaxKind.ExclamationEqualsEqualsToken || op === SyntaxKind.ExclamationEqualsToken) {
|
|
86
|
+
if (/^\w+$/.test(leftText))
|
|
87
|
+
continue; // x !== x is NaN check
|
|
88
|
+
}
|
|
89
|
+
// Skip simple literals like 0 - 0 in constant expressions
|
|
90
|
+
if (/^[0-9]+$/.test(leftText))
|
|
91
|
+
continue;
|
|
92
|
+
const opText = binExpr.getOperatorToken().getText();
|
|
93
|
+
findings.push(finding('identical-expressions', 'error', 'bug', `Identical expressions on both sides of '${opText}': ${leftText.substring(0, 40)}`, ctx.filePath, binExpr.getStartLineNumber()));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return findings;
|
|
97
|
+
}
|
|
98
|
+
// ── Rule D3: all-identical-branches ──────────────────────────────────────
|
|
99
|
+
// if (x) { A } else { A } — all branches do the same thing
|
|
100
|
+
function allIdenticalBranches(ctx) {
|
|
101
|
+
const findings = [];
|
|
102
|
+
for (const ifStmt of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.IfStatement)) {
|
|
103
|
+
// Only check top-level if (not else-if in a chain we already processed)
|
|
104
|
+
const parent = ifStmt.getParent();
|
|
105
|
+
if (parent?.getKind() === SyntaxKind.IfStatement)
|
|
106
|
+
continue;
|
|
107
|
+
const branches = [];
|
|
108
|
+
let current = ifStmt;
|
|
109
|
+
let hasElse = false;
|
|
110
|
+
while (current) {
|
|
111
|
+
const thenBlock = current.getThenStatement();
|
|
112
|
+
branches.push(normalize(thenBlock.getText()));
|
|
113
|
+
const elseStmt = current.getElseStatement();
|
|
114
|
+
if (!elseStmt)
|
|
115
|
+
break;
|
|
116
|
+
if (elseStmt.getKind() === SyntaxKind.IfStatement) {
|
|
117
|
+
current = elseStmt;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// Final else
|
|
121
|
+
branches.push(normalize(elseStmt.getText()));
|
|
122
|
+
hasElse = true;
|
|
123
|
+
current = undefined;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Need at least if + else to flag, and all branches must be identical
|
|
127
|
+
if (!hasElse || branches.length < 2)
|
|
128
|
+
continue;
|
|
129
|
+
const allSame = branches.every(b => b === branches[0]);
|
|
130
|
+
if (allSame) {
|
|
131
|
+
findings.push(finding('all-identical-branches', 'error', 'bug', `All ${branches.length} branches have identical code — condition has no effect`, ctx.filePath, ifStmt.getStartLineNumber()));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Ternary: x ? A : A
|
|
135
|
+
for (const ternary of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.ConditionalExpression)) {
|
|
136
|
+
const whenTrue = normalize(ternary.getWhenTrue().getText());
|
|
137
|
+
const whenFalse = normalize(ternary.getWhenFalse().getText());
|
|
138
|
+
if (whenTrue === whenFalse) {
|
|
139
|
+
findings.push(finding('all-identical-branches', 'warning', 'bug', 'Ternary has identical true/false expressions — condition has no effect', ctx.filePath, ternary.getStartLineNumber()));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return findings;
|
|
143
|
+
}
|
|
144
|
+
// ── Rule D4: constant-condition ──────────────────────────────────────────
|
|
145
|
+
// if (true), if (false), while (false), x ? ... where x is literal
|
|
146
|
+
function constantCondition(ctx) {
|
|
147
|
+
const findings = [];
|
|
148
|
+
for (const ifStmt of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.IfStatement)) {
|
|
149
|
+
const exprKind = ifStmt.getExpression().getKind();
|
|
150
|
+
if (exprKind === SyntaxKind.TrueKeyword) {
|
|
151
|
+
findings.push(finding('constant-condition', 'warning', 'bug', 'Condition is always true — else branch is dead code', ctx.filePath, ifStmt.getStartLineNumber()));
|
|
152
|
+
}
|
|
153
|
+
if (exprKind === SyntaxKind.FalseKeyword) {
|
|
154
|
+
findings.push(finding('constant-condition', 'error', 'bug', 'Condition is always false — then branch is dead code', ctx.filePath, ifStmt.getStartLineNumber()));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// while (false) — dead loop
|
|
158
|
+
for (const whileStmt of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.WhileStatement)) {
|
|
159
|
+
if (whileStmt.getExpression().getKind() === SyntaxKind.FalseKeyword) {
|
|
160
|
+
findings.push(finding('constant-condition', 'error', 'bug', 'while(false) — loop body is dead code', ctx.filePath, whileStmt.getStartLineNumber()));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Ternary with constant
|
|
164
|
+
for (const ternary of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.ConditionalExpression)) {
|
|
165
|
+
const condKind = ternary.getCondition().getKind();
|
|
166
|
+
if (condKind === SyntaxKind.TrueKeyword || condKind === SyntaxKind.FalseKeyword) {
|
|
167
|
+
const branch = condKind === SyntaxKind.TrueKeyword ? 'false' : 'true';
|
|
168
|
+
findings.push(finding('constant-condition', 'warning', 'bug', `Ternary condition is always ${condKind === SyntaxKind.TrueKeyword ? 'true' : 'false'} — ${branch} branch is dead`, ctx.filePath, ternary.getStartLineNumber()));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return findings;
|
|
172
|
+
}
|
|
173
|
+
// ── Rule D5: one-iteration-loop ──────────────────────────────────────────
|
|
174
|
+
// Loop that always breaks/returns on first iteration
|
|
175
|
+
function oneIterationLoop(ctx) {
|
|
176
|
+
const findings = [];
|
|
177
|
+
const loopKinds = [
|
|
178
|
+
SyntaxKind.ForStatement,
|
|
179
|
+
SyntaxKind.ForOfStatement,
|
|
180
|
+
SyntaxKind.ForInStatement,
|
|
181
|
+
SyntaxKind.WhileStatement,
|
|
182
|
+
SyntaxKind.DoStatement,
|
|
183
|
+
];
|
|
184
|
+
for (const kind of loopKinds) {
|
|
185
|
+
for (const loop of ctx.sourceFile.getDescendantsOfKind(kind)) {
|
|
186
|
+
// Get the loop body block
|
|
187
|
+
let bodyBlock;
|
|
188
|
+
const body = loop.getStatement?.();
|
|
189
|
+
if (body?.getKind() === SyntaxKind.Block) {
|
|
190
|
+
bodyBlock = body;
|
|
191
|
+
}
|
|
192
|
+
if (!bodyBlock)
|
|
193
|
+
continue;
|
|
194
|
+
const stmts = bodyBlock.getStatements();
|
|
195
|
+
if (stmts.length === 0)
|
|
196
|
+
continue;
|
|
197
|
+
// Check if every path through the first level of statements exits the loop
|
|
198
|
+
const lastStmt = stmts[stmts.length - 1];
|
|
199
|
+
const lastKind = lastStmt.getKind();
|
|
200
|
+
// Unconditional break/return/throw at end of body
|
|
201
|
+
if (lastKind === SyntaxKind.BreakStatement ||
|
|
202
|
+
lastKind === SyntaxKind.ReturnStatement ||
|
|
203
|
+
lastKind === SyntaxKind.ThrowStatement) {
|
|
204
|
+
// Make sure there's no continue before it (which would indicate real looping)
|
|
205
|
+
const hasContinue = bodyBlock.getDescendantsOfKind(SyntaxKind.ContinueStatement).length > 0;
|
|
206
|
+
if (!hasContinue) {
|
|
207
|
+
findings.push(finding('one-iteration-loop', 'warning', 'bug', 'Loop runs at most one iteration — unconditional exit at end of body', ctx.filePath, loop.getStartLineNumber(), { suggestion: 'If intentional, use an if statement instead of a loop' }));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return findings;
|
|
213
|
+
}
|
|
214
|
+
// ── Rule D6: unused-collection ───────────────────────────────────────────
|
|
215
|
+
// Array/Map/Set populated but never read
|
|
216
|
+
function unusedCollection(ctx) {
|
|
217
|
+
const findings = [];
|
|
218
|
+
const writeOps = new Set(['push', 'add', 'set', 'unshift', 'splice', 'fill']);
|
|
219
|
+
const readOps = new Set(['get', 'has', 'includes', 'indexOf', 'find', 'filter', 'map',
|
|
220
|
+
'reduce', 'some', 'every', 'forEach', 'entries', 'values', 'keys', 'join',
|
|
221
|
+
'flat', 'flatMap', 'slice', 'at', 'length', 'size']);
|
|
222
|
+
// Find ALL variable declarations (not just top-level) for collection patterns
|
|
223
|
+
for (const decl of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) {
|
|
224
|
+
const init = decl.getInitializer();
|
|
225
|
+
if (!init)
|
|
226
|
+
continue;
|
|
227
|
+
const initText = init.getText();
|
|
228
|
+
const isCollection = init.getKind() === SyntaxKind.ArrayLiteralExpression ||
|
|
229
|
+
initText.startsWith('new Map') || initText.startsWith('new Set') ||
|
|
230
|
+
initText.startsWith('new Array');
|
|
231
|
+
if (!isCollection)
|
|
232
|
+
continue;
|
|
233
|
+
const varName = decl.getName();
|
|
234
|
+
const declLine = decl.getStartLineNumber();
|
|
235
|
+
// Scope: only scan references within the same block/function scope
|
|
236
|
+
const scope = decl.getFirstAncestorByKind(SyntaxKind.Block) || ctx.sourceFile;
|
|
237
|
+
let hasWrite = false;
|
|
238
|
+
let hasRead = false;
|
|
239
|
+
for (const ident of scope.getDescendantsOfKind(SyntaxKind.Identifier)) {
|
|
240
|
+
if (ident.getText() !== varName)
|
|
241
|
+
continue;
|
|
242
|
+
if (ident.getStartLineNumber() === declLine && ident.getStart() <= decl.getEnd())
|
|
243
|
+
continue;
|
|
244
|
+
const parent = ident.getParent();
|
|
245
|
+
if (!parent)
|
|
246
|
+
continue;
|
|
247
|
+
if (parent.getKind() === SyntaxKind.PropertyAccessExpression) {
|
|
248
|
+
const pa = parent;
|
|
249
|
+
if (pa.getExpression() === ident) {
|
|
250
|
+
const method = pa.getName();
|
|
251
|
+
if (writeOps.has(method))
|
|
252
|
+
hasWrite = true;
|
|
253
|
+
if (readOps.has(method))
|
|
254
|
+
hasRead = true;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
else if (parent.getKind() !== SyntaxKind.VariableDeclaration) {
|
|
258
|
+
// Any other usage (arg, return, spread, etc.) is a read
|
|
259
|
+
hasRead = true;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (hasWrite && !hasRead) {
|
|
263
|
+
findings.push(finding('unused-collection', 'warning', 'bug', `Collection '${varName}' is populated but never read`, ctx.filePath, declLine));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return findings;
|
|
267
|
+
}
|
|
268
|
+
// ── Rule D7: empty-collection-access ─────────────────────────────────────
|
|
269
|
+
// Collection read but never populated
|
|
270
|
+
function emptyCollectionAccess(ctx) {
|
|
271
|
+
const findings = [];
|
|
272
|
+
const writeOps = new Set(['push', 'add', 'set', 'unshift', 'splice', 'fill']);
|
|
273
|
+
for (const decl of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) {
|
|
274
|
+
const init = decl.getInitializer();
|
|
275
|
+
if (!init)
|
|
276
|
+
continue;
|
|
277
|
+
// Only flag collections initialized as empty
|
|
278
|
+
const initText = init.getText().trim();
|
|
279
|
+
const isEmpty = initText === '[]' || initText === 'new Map()' ||
|
|
280
|
+
initText === 'new Set()' || initText === 'new Array()';
|
|
281
|
+
if (!isEmpty)
|
|
282
|
+
continue;
|
|
283
|
+
const varName = decl.getName();
|
|
284
|
+
const declLine = decl.getStartLineNumber();
|
|
285
|
+
// Scope: only scan within the same block
|
|
286
|
+
const scope = decl.getFirstAncestorByKind(SyntaxKind.Block) || ctx.sourceFile;
|
|
287
|
+
let hasWrite = false;
|
|
288
|
+
let hasRead = false;
|
|
289
|
+
for (const ident of scope.getDescendantsOfKind(SyntaxKind.Identifier)) {
|
|
290
|
+
if (ident.getText() !== varName)
|
|
291
|
+
continue;
|
|
292
|
+
if (ident.getStartLineNumber() === declLine && ident.getStart() <= decl.getEnd())
|
|
293
|
+
continue;
|
|
294
|
+
const parent = ident.getParent();
|
|
295
|
+
if (!parent)
|
|
296
|
+
continue;
|
|
297
|
+
if (parent.getKind() === SyntaxKind.PropertyAccessExpression) {
|
|
298
|
+
const pa = parent;
|
|
299
|
+
if (pa.getExpression() === ident) {
|
|
300
|
+
if (writeOps.has(pa.getName()))
|
|
301
|
+
hasWrite = true;
|
|
302
|
+
else
|
|
303
|
+
hasRead = true;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
else if (parent.getKind() === SyntaxKind.BinaryExpression) {
|
|
307
|
+
const bin = parent;
|
|
308
|
+
if (bin.getOperatorToken().getKind() === SyntaxKind.EqualsToken && bin.getLeft() === ident) {
|
|
309
|
+
hasWrite = true;
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
hasRead = true;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
else if (parent.getKind() === SyntaxKind.CallExpression) {
|
|
316
|
+
const callExpr = parent;
|
|
317
|
+
if (callExpr.getExpression() !== ident)
|
|
318
|
+
hasWrite = true;
|
|
319
|
+
else
|
|
320
|
+
hasRead = true;
|
|
321
|
+
}
|
|
322
|
+
else if (parent.getKind() !== SyntaxKind.VariableDeclaration) {
|
|
323
|
+
hasRead = true;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (hasRead && !hasWrite) {
|
|
327
|
+
findings.push(finding('empty-collection-access', 'warning', 'bug', `Collection '${varName}' is initialized empty and never populated — reads will always be empty`, ctx.filePath, declLine));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return findings;
|
|
331
|
+
}
|
|
332
|
+
// ── Rule D8: redundant-jump ──────────────────────────────────────────────
|
|
333
|
+
// return/continue at end of block where it's the natural flow
|
|
334
|
+
function redundantJump(ctx) {
|
|
335
|
+
const findings = [];
|
|
336
|
+
// Redundant continue at end of loop body
|
|
337
|
+
const loopKinds = [
|
|
338
|
+
SyntaxKind.ForStatement,
|
|
339
|
+
SyntaxKind.ForOfStatement,
|
|
340
|
+
SyntaxKind.ForInStatement,
|
|
341
|
+
SyntaxKind.WhileStatement,
|
|
342
|
+
];
|
|
343
|
+
for (const kind of loopKinds) {
|
|
344
|
+
for (const loop of ctx.sourceFile.getDescendantsOfKind(kind)) {
|
|
345
|
+
const body = loop.getStatement?.();
|
|
346
|
+
if (body?.getKind() !== SyntaxKind.Block)
|
|
347
|
+
continue;
|
|
348
|
+
const block = body;
|
|
349
|
+
const stmts = block.getStatements();
|
|
350
|
+
if (stmts.length === 0)
|
|
351
|
+
continue;
|
|
352
|
+
const last = stmts[stmts.length - 1];
|
|
353
|
+
if (last.getKind() === SyntaxKind.ContinueStatement) {
|
|
354
|
+
const contStmt = last;
|
|
355
|
+
// Only flag unlabeled continue
|
|
356
|
+
if (!contStmt.getLabel()) {
|
|
357
|
+
findings.push(finding('redundant-jump', 'info', 'style', 'Redundant continue at end of loop body', ctx.filePath, last.getStartLineNumber(), { suggestion: 'Remove — loop naturally continues at end of body' }));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
// Redundant return at end of void function
|
|
363
|
+
for (const fn of ctx.sourceFile.getFunctions()) {
|
|
364
|
+
const body = fn.getBody();
|
|
365
|
+
if (!body || body.getKind() !== SyntaxKind.Block)
|
|
366
|
+
continue;
|
|
367
|
+
const block = body;
|
|
368
|
+
const stmts = block.getStatements();
|
|
369
|
+
if (stmts.length === 0)
|
|
370
|
+
continue;
|
|
371
|
+
const last = stmts[stmts.length - 1];
|
|
372
|
+
if (last.getKind() === SyntaxKind.ReturnStatement) {
|
|
373
|
+
const retStmt = last;
|
|
374
|
+
// Only flag bare return (no value)
|
|
375
|
+
if (!retStmt.getExpression()) {
|
|
376
|
+
findings.push(finding('redundant-jump', 'info', 'style', 'Redundant return at end of function', ctx.filePath, last.getStartLineNumber(), { suggestion: 'Remove — function returns void naturally' }));
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return findings;
|
|
381
|
+
}
|
|
382
|
+
// ── Exported Dead Logic Rules ────────────────────────────────────────────
|
|
383
|
+
export const deadLogicRules = [
|
|
384
|
+
identicalConditions,
|
|
385
|
+
identicalExpressions,
|
|
386
|
+
allIdenticalBranches,
|
|
387
|
+
constantCondition,
|
|
388
|
+
oneIterationLoop,
|
|
389
|
+
unusedCollection,
|
|
390
|
+
emptyCollectionAccess,
|
|
391
|
+
redundantJump,
|
|
392
|
+
];
|
|
393
|
+
//# sourceMappingURL=dead-logic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dead-logic.js","sourceRoot":"","sources":["../../src/rules/dead-logic.ts"],"names":[],"mappings":"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"}
|
package/dist/rules/express.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Catches common Express security and performance issues.
|
|
5
5
|
*/
|
|
6
|
+
import { SyntaxKind, Node } from 'ts-morph';
|
|
6
7
|
import { createFingerprint } from '../types.js';
|
|
7
8
|
function span(file, line, col = 1) {
|
|
8
9
|
return { file, startLine: line, startCol: col, endLine: line, endCol: col };
|
|
@@ -53,11 +54,11 @@ function missingErrorMiddleware(ctx) {
|
|
|
53
54
|
const findings = [];
|
|
54
55
|
const fullText = ctx.sourceFile.getFullText();
|
|
55
56
|
// Check if this file creates an Express app
|
|
56
|
-
const hasApp = /(?:const|let)\
|
|
57
|
+
const hasApp = /(?:const|let) \w+ ?= ?express\s?\(\s?\)/.test(fullText);
|
|
57
58
|
if (!hasApp)
|
|
58
59
|
return findings;
|
|
59
60
|
// Check for error middleware (4-parameter function: err, req, res, next)
|
|
60
|
-
const has4ParamMiddleware = /app\.use\s
|
|
61
|
+
const has4ParamMiddleware = /app\.use\s?\(\s?(?:function\s+)?\(\s?\w+,\s?\w+,\s?\w+,\s?\w+\s?\)/.test(fullText);
|
|
61
62
|
const hasErrorHandler = has4ParamMiddleware || fullText.includes('errorHandler') || fullText.includes('error-handler');
|
|
62
63
|
if (!hasErrorHandler) {
|
|
63
64
|
// Find the app declaration line
|
|
@@ -98,10 +99,76 @@ function syncInHandler(ctx) {
|
|
|
98
99
|
}
|
|
99
100
|
return findings;
|
|
100
101
|
}
|
|
102
|
+
// ── Rule: double-response ────────────────────────────────────────────────
|
|
103
|
+
// Express handler sends response (res.json/res.send) more than once without early return
|
|
104
|
+
const RESPONSE_METHODS = new Set(['json', 'send', 'end', 'redirect', 'render', 'sendFile', 'sendStatus']);
|
|
105
|
+
function doubleResponse(ctx) {
|
|
106
|
+
const findings = [];
|
|
107
|
+
const fullText = ctx.sourceFile.getFullText();
|
|
108
|
+
// Find functions with (req, res) or (req, res, next) parameters
|
|
109
|
+
const allFns = [
|
|
110
|
+
...ctx.sourceFile.getDescendantsOfKind(SyntaxKind.FunctionDeclaration),
|
|
111
|
+
...ctx.sourceFile.getDescendantsOfKind(SyntaxKind.ArrowFunction),
|
|
112
|
+
...ctx.sourceFile.getDescendantsOfKind(SyntaxKind.FunctionExpression),
|
|
113
|
+
...ctx.sourceFile.getDescendantsOfKind(SyntaxKind.MethodDeclaration),
|
|
114
|
+
];
|
|
115
|
+
for (const fn of allFns) {
|
|
116
|
+
const params = fn.getParameters();
|
|
117
|
+
const resParam = params.find(p => /^(res|response)$/i.test(p.getName()) || /\bResponse\b/.test(p.getType().getText(p)));
|
|
118
|
+
const reqLike = params.some(p => /^(req|request|ctx)$/i.test(p.getName()) || /\b(Request|NextFunction)\b/.test(p.getType().getText(p)));
|
|
119
|
+
if (!resParam || !reqLike)
|
|
120
|
+
continue;
|
|
121
|
+
const resName = resParam.getName();
|
|
122
|
+
const body = fn.getBody();
|
|
123
|
+
if (!body || !Node.isBlock(body))
|
|
124
|
+
continue;
|
|
125
|
+
// Find all response calls in the body (excluding nested functions)
|
|
126
|
+
const responseCalls = [];
|
|
127
|
+
for (const call of body.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
128
|
+
// Skip if inside a nested function
|
|
129
|
+
let isNested = false;
|
|
130
|
+
let cur = call.getParent();
|
|
131
|
+
while (cur && cur !== body) {
|
|
132
|
+
if (Node.isArrowFunction(cur) || Node.isFunctionExpression(cur) ||
|
|
133
|
+
Node.isFunctionDeclaration(cur) || Node.isMethodDeclaration(cur)) {
|
|
134
|
+
isNested = true;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
cur = cur.getParent();
|
|
138
|
+
}
|
|
139
|
+
if (isNested)
|
|
140
|
+
continue;
|
|
141
|
+
const expr = call.getExpression();
|
|
142
|
+
if (!Node.isPropertyAccessExpression(expr))
|
|
143
|
+
continue;
|
|
144
|
+
const methodName = expr.getName();
|
|
145
|
+
if (!RESPONSE_METHODS.has(methodName))
|
|
146
|
+
continue;
|
|
147
|
+
// Check if the object is res or res.status(...)
|
|
148
|
+
const obj = expr.getExpression();
|
|
149
|
+
const isResCall = Node.isIdentifier(obj) && obj.getText() === resName;
|
|
150
|
+
const isChainedRes = Node.isCallExpression(obj) && obj.getExpression().getText().startsWith(resName);
|
|
151
|
+
if (!isResCall && !isChainedRes)
|
|
152
|
+
continue;
|
|
153
|
+
responseCalls.push({ line: call.getStartLineNumber(), method: methodName });
|
|
154
|
+
}
|
|
155
|
+
// Check for response calls that aren't in mutually exclusive if/else branches
|
|
156
|
+
// Simple heuristic: if there are 2+ response calls and any is NOT followed by return/throw, flag it
|
|
157
|
+
if (responseCalls.length < 2)
|
|
158
|
+
continue;
|
|
159
|
+
// Flag from the second call onwards
|
|
160
|
+
for (let i = 1; i < responseCalls.length; i++) {
|
|
161
|
+
const { line, method } = responseCalls[i];
|
|
162
|
+
findings.push(finding('double-response', 'error', 'bug', `Possible double response: ${resName}.${method}() may execute after an earlier response — add return after first send`, ctx.filePath, line, { suggestion: 'Return immediately after sending a response to prevent double-send errors' }));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return findings;
|
|
166
|
+
}
|
|
101
167
|
// ── Exported Express Rules ───────────────────────────────────────────────
|
|
102
168
|
export const expressRules = [
|
|
103
169
|
unvalidatedInput,
|
|
104
170
|
missingErrorMiddleware,
|
|
105
171
|
syncInHandler,
|
|
172
|
+
doubleResponse,
|
|
106
173
|
];
|
|
107
174
|
//# sourceMappingURL=express.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/rules/express.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/rules/express.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ground-layer KERN-IR lint rules.
|
|
3
|
+
*
|
|
4
|
+
* These operate on IRNode[], not ts-morph SourceFile.
|
|
5
|
+
* 4 are codegen errors (compilation fails via KernCodegenError).
|
|
6
|
+
* 7 are lint warnings/info (reported but compilation succeeds).
|
|
7
|
+
*/
|
|
8
|
+
import type { KernLintRule } from '../kern-lint.js';
|
|
9
|
+
/** guard without else action (lint warning) */
|
|
10
|
+
export declare const guardWithoutElse: KernLintRule;
|
|
11
|
+
/** action missing idempotent annotation (lint info) */
|
|
12
|
+
export declare const actionMissingIdempotent: KernLintRule;
|
|
13
|
+
/** branch missing known variants (lint warning) */
|
|
14
|
+
export declare const branchNonExhaustive: KernLintRule;
|
|
15
|
+
/** collect without limit (lint info) */
|
|
16
|
+
export declare const collectUnbounded: KernLintRule;
|
|
17
|
+
/** reason without basis field (lint info) */
|
|
18
|
+
export declare const reasonWithoutBasis: KernLintRule;
|
|
19
|
+
/** assume with basis but no evidence (lint info — low trust) */
|
|
20
|
+
export declare const assumeLowTrust: KernLintRule;
|
|
21
|
+
/** expect range inverted (lint warning — min > max) */
|
|
22
|
+
export declare const expectRangeInverted: KernLintRule;
|
|
23
|
+
export declare const GROUND_LAYER_RULES: KernLintRule[];
|