@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,386 @@
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.VariableDeclaration) {
316
+ hasRead = true;
317
+ }
318
+ }
319
+ if (hasRead && !hasWrite) {
320
+ findings.push(finding('empty-collection-access', 'warning', 'bug', `Collection '${varName}' is initialized empty and never populated — reads will always be empty`, ctx.filePath, declLine));
321
+ }
322
+ }
323
+ return findings;
324
+ }
325
+ // ── Rule D8: redundant-jump ──────────────────────────────────────────────
326
+ // return/continue at end of block where it's the natural flow
327
+ function redundantJump(ctx) {
328
+ const findings = [];
329
+ // Redundant continue at end of loop body
330
+ const loopKinds = [
331
+ SyntaxKind.ForStatement,
332
+ SyntaxKind.ForOfStatement,
333
+ SyntaxKind.ForInStatement,
334
+ SyntaxKind.WhileStatement,
335
+ ];
336
+ for (const kind of loopKinds) {
337
+ for (const loop of ctx.sourceFile.getDescendantsOfKind(kind)) {
338
+ const body = loop.getStatement?.();
339
+ if (body?.getKind() !== SyntaxKind.Block)
340
+ continue;
341
+ const block = body;
342
+ const stmts = block.getStatements();
343
+ if (stmts.length === 0)
344
+ continue;
345
+ const last = stmts[stmts.length - 1];
346
+ if (last.getKind() === SyntaxKind.ContinueStatement) {
347
+ const contStmt = last;
348
+ // Only flag unlabeled continue
349
+ if (!contStmt.getLabel()) {
350
+ 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' }));
351
+ }
352
+ }
353
+ }
354
+ }
355
+ // Redundant return at end of void function
356
+ for (const fn of ctx.sourceFile.getFunctions()) {
357
+ const body = fn.getBody();
358
+ if (!body || body.getKind() !== SyntaxKind.Block)
359
+ continue;
360
+ const block = body;
361
+ const stmts = block.getStatements();
362
+ if (stmts.length === 0)
363
+ continue;
364
+ const last = stmts[stmts.length - 1];
365
+ if (last.getKind() === SyntaxKind.ReturnStatement) {
366
+ const retStmt = last;
367
+ // Only flag bare return (no value)
368
+ if (!retStmt.getExpression()) {
369
+ findings.push(finding('redundant-jump', 'info', 'style', 'Redundant return at end of function', ctx.filePath, last.getStartLineNumber(), { suggestion: 'Remove — function returns void naturally' }));
370
+ }
371
+ }
372
+ }
373
+ return findings;
374
+ }
375
+ // ── Exported Dead Logic Rules ────────────────────────────────────────────
376
+ export const deadLogicRules = [
377
+ identicalConditions,
378
+ identicalExpressions,
379
+ allIdenticalBranches,
380
+ constantCondition,
381
+ oneIterationLoop,
382
+ unusedCollection,
383
+ emptyCollectionAccess,
384
+ redundantJump,
385
+ ];
386
+ //# 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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"}
@@ -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)\s+\w+\s*=\s*express\s*\(\s*\)/g.test(fullText);
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*\(\s*(?:function\s*)?\(\s*\w+\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*\)/g.test(fullText);
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;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,SAAS,IAAI,CAAC,IAAY,EAAE,IAAY,EAAE,GAAG,GAAG,CAAC;IAC/C,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAC9E,CAAC;AAED,SAAS,OAAO,CACd,MAAc,EACd,QAAsC,EACtC,QAAmC,EACnC,OAAe,EACf,IAAY,EACZ,IAAY,EACZ,KAA8B;IAE9B,OAAO;QACL,MAAM,EAAE,MAAM;QACd,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,OAAO;QACP,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;QAC7B,WAAW,EAAE,iBAAiB,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,GAAG,KAAK;KACT,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,0CAA0C;AAE1C,SAAS,gBAAgB,CAAC,GAAgB;IACxC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;IAC9C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEnC,mDAAmD;IACnD,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IACpF,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IACpF,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IACpF,MAAM,aAAa,GAAG,MAAM,IAAI,MAAM,IAAI,MAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAEnF,IAAI,aAAa;QAAE,OAAO,QAAQ,CAAC,CAAC,iCAAiC;IAErE,qCAAqC;IACrC,MAAM,YAAY,GAAG,yCAAyC,CAAC;IAC/D,IAAI,KAAK,CAAC;IAEV,OAAO,CAAC,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACtD,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;QACnE,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAEvC,wDAAwD;QACxD,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACxD,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;YAC7D,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,SAAS;QAE1C,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,OAAO,EAAE,KAAK,EACvD,OAAO,KAAK,CAAC,CAAC,CAAC,uDAAuD,EACtE,GAAG,CAAC,QAAQ,EAAE,IAAI,EAClB,EAAE,UAAU,EAAE,wEAAwE,EAAE,CAAC,CAAC,CAAC;IAC/F,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,6EAA6E;AAC7E,yDAAyD;AAEzD,SAAS,sBAAsB,CAAC,GAAgB;IAC9C,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;IAE9C,4CAA4C;IAC5C,MAAM,MAAM,GAAG,8CAA8C,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7E,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAE7B,yEAAyE;IACzE,MAAM,mBAAmB,GAAG,8EAA8E,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1H,MAAM,eAAe,GAAG,mBAAmB,IAAI,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IAEvH,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,gCAAgC;QAChC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACjF,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;YAC7E,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,0BAA0B,EAAE,SAAS,EAAE,SAAS,EACpE,uFAAuF,EACvF,GAAG,CAAC,QAAQ,EAAE,IAAI,EAClB,EAAE,UAAU,EAAE,qFAAqF,EAAE,CAAC,CAAC,CAAC;QAC5G,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,4EAA4E;AAC5E,uDAAuD;AAEvD,SAAS,aAAa,CAAC,GAAgB;IACrC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;IAE9C,MAAM,OAAO,GAAG;QACd,EAAE,OAAO,EAAE,sBAAsB,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,UAAU,EAAE;QAC5E,EAAE,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,WAAW,EAAE;QAC/E,EAAE,OAAO,EAAE,oBAAoB,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE;QACtE,EAAE,OAAO,EAAE,mBAAmB,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE;QACnE,EAAE,OAAO,EAAE,qBAAqB,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,SAAS,EAAE;QACzE,EAAE,OAAO,EAAE,kBAAkB,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE;QAChE,EAAE,OAAO,EAAE,4BAA4B,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE;QAC9E,EAAE,OAAO,EAAE,4BAA4B,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE;QAC9E,EAAE,OAAO,EAAE,6BAA6B,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,wBAAwB,EAAE;KACxG,CAAC;IAEF,4CAA4C;IAC5C,MAAM,WAAW,GAAG,sDAAsD,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1F,IAAI,CAAC,WAAW;QAAE,OAAO,QAAQ,CAAC;IAElC,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,OAAO,EAAE,CAAC;QAC1D,IAAI,KAAK,CAAC;QACV,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;YACnE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAC3D,GAAG,IAAI,mDAAmD,SAAS,UAAU,EAC7E,GAAG,CAAC,QAAQ,EAAE,IAAI,EAClB,EAAE,UAAU,EAAE,WAAW,IAAI,eAAe,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,4EAA4E;AAE5E,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,gBAAgB;IAChB,sBAAsB;IACtB,aAAa;CACd,CAAC"}
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[];