circle-ir 3.9.10 → 3.12.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 (33) hide show
  1. package/README.md +92 -34
  2. package/dist/analysis/passes/cleanup-verify-pass.d.ts +28 -0
  3. package/dist/analysis/passes/cleanup-verify-pass.js +130 -0
  4. package/dist/analysis/passes/cleanup-verify-pass.js.map +1 -0
  5. package/dist/analysis/passes/missing-guard-dom-pass.d.ts +25 -0
  6. package/dist/analysis/passes/missing-guard-dom-pass.js +99 -0
  7. package/dist/analysis/passes/missing-guard-dom-pass.js.map +1 -0
  8. package/dist/analysis/passes/missing-override-pass.d.ts +27 -0
  9. package/dist/analysis/passes/missing-override-pass.js +110 -0
  10. package/dist/analysis/passes/missing-override-pass.js.map +1 -0
  11. package/dist/analysis/passes/sink-filter-pass.js +81 -8
  12. package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
  13. package/dist/analysis/passes/taint-matcher-pass.js +6 -1
  14. package/dist/analysis/passes/taint-matcher-pass.js.map +1 -1
  15. package/dist/analysis/passes/taint-propagation-pass.js +2 -3
  16. package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
  17. package/dist/analysis/passes/unused-interface-method-pass.d.ts +27 -0
  18. package/dist/analysis/passes/unused-interface-method-pass.js +62 -0
  19. package/dist/analysis/passes/unused-interface-method-pass.js.map +1 -0
  20. package/dist/analysis/taint-matcher.d.ts +2 -1
  21. package/dist/analysis/taint-matcher.js +9 -5
  22. package/dist/analysis/taint-matcher.js.map +1 -1
  23. package/dist/analyzer.d.ts +5 -1
  24. package/dist/analyzer.js +13 -1
  25. package/dist/analyzer.js.map +1 -1
  26. package/dist/browser/circle-ir.js +1069 -18
  27. package/dist/core/circle-ir-core.cjs +8 -5
  28. package/dist/core/circle-ir-core.js +8 -5
  29. package/dist/languages/plugins/java.d.ts +9 -0
  30. package/dist/languages/plugins/java.js +48 -6
  31. package/dist/languages/plugins/java.js.map +1 -1
  32. package/docs/SPEC.md +13 -4
  33. package/package.json +1 -1
package/README.md CHANGED
@@ -1,13 +1,14 @@
1
1
  # circle-ir
2
2
 
3
- A high-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis, and code quality findings through an extensible analysis-pass pipeline. Works in Node.js and browsers.
3
+ A high-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis, and code quality findings through an extensible 36-pass analysis pipeline. Works in Node.js and browsers.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - **Taint Analysis**: Track data flow from sources (user input) to sinks (dangerous operations)
8
8
  - **Multi-language Support**: Java, JavaScript/TypeScript, Python, Rust, Bash/Shell
9
9
  - **High Accuracy**: 100% on OWASP Benchmark, 100% on Juliet Test Suite, 97.7% TPR on SecuriBench Micro
10
- - **11-Pass Pipeline**: Security taint passes + quality passes (dead code, missing await, N+1, doc coverage, TODO markers)
10
+ - **36-Pass Pipeline**: 19 security taint passes + 17 reliability/performance/maintainability/architecture quality passes
11
+ - **Metrics Engine**: 24 software quality metrics (cyclomatic complexity, Halstead, CBO, RFC, LCOM, DIT, and 4 composite scores)
11
12
  - **Cross-File Analysis**: `analyzeProject()` surfaces taint flows that span multiple files
12
13
  - **Universal**: Works in Node.js and browsers with environment-agnostic core
13
14
  - **Zero External Dependencies**: Core analysis runs without network calls or external services
@@ -40,12 +41,20 @@ for (const flow of result.taint.flows || []) {
40
41
  console.log(` Sink: line ${flow.sink_line}`);
41
42
  }
42
43
 
43
- // Quality findings from analysis passes (dead-code, missing-await, n-plus-one, etc.)
44
+ // Quality findings from all 36 analysis passes
44
45
  for (const finding of result.findings || []) {
45
46
  console.log(`[${finding.severity}] ${finding.rule_id} at line ${finding.line}`);
46
47
  console.log(` ${finding.message}`);
47
48
  if (finding.fix) console.log(` Fix: ${finding.fix}`);
48
49
  }
50
+
51
+ // Software quality metrics
52
+ const m = result.metrics;
53
+ if (m) {
54
+ console.log(`Cyclomatic complexity: ${m.cyclomatic_complexity}`);
55
+ console.log(`Maintainability index: ${m.maintainability_index}`);
56
+ console.log(`CBO (coupling): ${m.CBO}`);
57
+ }
49
58
  ```
50
59
 
51
60
  ### Browser
@@ -101,7 +110,8 @@ result.dfg // Data flow graph
101
110
  result.taint // Taint sources, sinks, flows
102
111
  result.imports // Import statements
103
112
  result.exports // Exported symbols
104
- result.findings // SastFinding[] from all 11 analysis passes
113
+ result.findings // SastFinding[] from all 36 analysis passes
114
+ result.metrics // FileMetrics — 24 software quality metrics (always populated)
105
115
  ```
106
116
 
107
117
  ### `analyzeProject(files, options?)`
@@ -181,20 +191,29 @@ const pyResult = await analyze(pyCode, 'app.py', 'python');
181
191
  const rsResult = await analyze(rsCode, 'main.rs', 'rust');
182
192
  ```
183
193
 
184
- ## Detected Vulnerabilities
185
-
186
- | Type | CWE | Description |
187
- |------|-----|-------------|
188
- | SQL Injection | CWE-89 | User input in SQL queries |
189
- | Command Injection | CWE-78 | User input in system commands |
190
- | XSS | CWE-79 | User input in HTML output |
191
- | Path Traversal | CWE-22 | User input in file paths |
192
- | LDAP Injection | CWE-90 | User input in LDAP queries |
193
- | XPath Injection | CWE-643 | User input in XPath queries |
194
- | Deserialization | CWE-502 | Untrusted deserialization |
195
- | SSRF | CWE-918 | Server-side request forgery |
196
- | Code Injection | CWE-94 | Dynamic code execution |
197
- | XXE | CWE-611 | XML external entity injection |
194
+ ## Detected Security Vulnerabilities
195
+
196
+ | Type | CWE | Severity | Description |
197
+ |------|-----|----------|-------------|
198
+ | SQL Injection | CWE-89 | Critical | User input in SQL queries |
199
+ | Command Injection | CWE-78 | Critical | User input in system commands |
200
+ | Deserialization | CWE-502 | Critical | Untrusted deserialization |
201
+ | XXE | CWE-611 | Critical | XML external entity injection |
202
+ | Code Injection | CWE-94 | Critical | Dynamic code execution |
203
+ | XSS | CWE-79 | High | User input in HTML output |
204
+ | Path Traversal | CWE-22 | High | User input in file paths |
205
+ | SSRF | CWE-918 | High | Server-side request forgery |
206
+ | LDAP Injection | CWE-90 | High | User input in LDAP queries |
207
+ | XPath Injection | CWE-643 | High | User input in XPath queries |
208
+ | NoSQL Injection | CWE-943 | High | User input in NoSQL queries |
209
+ | Open Redirect | CWE-601 | Medium | User controls redirect destination |
210
+ | Log Injection | CWE-117 | Medium | User input in logs |
211
+ | Trust Boundary | CWE-501 | Medium | Data crosses trust boundary |
212
+ | External Taint | CWE-668 | Medium | External input reaches sensitive sink |
213
+ | Weak Random | CWE-330 | Low | Weak random number generator |
214
+ | Weak Hash | CWE-327 | Low | Weak hashing algorithm |
215
+ | Weak Crypto | CWE-327 | Low | Weak cryptographic algorithm |
216
+ | Insecure Cookie | CWE-614 | Low | Cookie without Secure/HttpOnly flags |
198
217
 
199
218
  ## Configuration
200
219
 
@@ -212,7 +231,7 @@ sources:
212
231
 
213
232
  ## SAST Findings & Quality Passes
214
233
 
215
- The 11-pass pipeline emits `SastFinding[]` via `result.findings`. Each finding is SARIF 2.1.0-aligned:
234
+ The 36-pass pipeline emits `SastFinding[]` via `result.findings`. Each finding is SARIF 2.1.0-aligned:
216
235
 
217
236
  ```typescript
218
237
  interface SastFinding {
@@ -230,21 +249,57 @@ interface SastFinding {
230
249
  }
231
250
  ```
232
251
 
233
- **Current passes** (see [docs/PASSES.md](docs/PASSES.md) for the full registry):
234
-
235
- | Pass | rule_id | Category | CWE | Level |
236
- |------|---------|----------|-----|-------|
237
- | TaintMatcherPass | _(produces flows)_ | security | | error |
238
- | ConstantPropagationPass | _(reduces FP)_ | security | | |
239
- | LanguageSourcesPass | _(enriches sources)_ | security | | |
240
- | SinkFilterPass | _(filters sinks)_ | security | | |
241
- | TaintPropagationPass | _(propagates taint)_ | security | | error |
242
- | InterproceduralPass | _(cross-method)_ | security | — | error |
243
- | DeadCodePass | `dead-code` | reliability | CWE-561 | warning |
244
- | MissingAwaitPass | `missing-await` | reliability | CWE-252 | warning |
245
- | NPlusOnePass | `n-plus-one` | performance | CWE-1049 | warning |
246
- | MissingPublicDocPass | `missing-public-doc` | maintainability | — | note |
247
- | TodoInProdPass | `todo-in-prod` | maintainability | — | note |
252
+ **Pass categories** (see [docs/PASSES.md](docs/PASSES.md) for the full registry with all 36 rule IDs and CWEs):
253
+
254
+ | Category | Passes | Example rule_ids |
255
+ |----------|--------|-----------------|
256
+ | `security` (19) | Taint matching, propagation, inter-procedural | _(produces `taint.flows`)_ |
257
+ | `reliability` (16) | Resource management, control flow, exception handling | `null-deref`, `resource-leak`, `infinite-loop`, `double-close`, `use-after-close`, `missing-guard-dom`, `cleanup-verify`, `unhandled-exception`, `broad-catch`, `swallowed-exception` |
258
+ | `performance` (5) | Loop efficiency, async patterns | `n-plus-one`, `redundant-loop-computation`, `unbounded-collection`, `serial-await`, `react-inline-jsx` |
259
+ | `maintainability` (3) | Documentation, markers | `missing-public-doc`, `todo-in-prod`, `stale-doc-ref` |
260
+ | `architecture` (6) | Coupling, inheritance, interface contracts | `circular-dependency`, `orphan-module`, `dependency-fan-out`, `deep-inheritance`, `missing-override`, `unused-interface-method` |
261
+
262
+ ## Metrics Engine
263
+
264
+ `result.metrics` is always populated with 24 software quality metrics:
265
+
266
+ ```typescript
267
+ interface FileMetrics {
268
+ // Complexity
269
+ cyclomatic_complexity: number; // v(G) per method average
270
+ WMC: number; // Weighted methods per class
271
+ halstead_volume: number; // Halstead volume
272
+ halstead_difficulty: number;
273
+ halstead_effort: number;
274
+ halstead_bugs: number;
275
+
276
+ // Size
277
+ LOC: number; // Lines of code
278
+ NLOC: number; // Non-blank lines
279
+ comment_density: number; // Comment lines / total lines
280
+ function_count: number;
281
+
282
+ // Coupling
283
+ CBO: number; // Coupling between objects
284
+ RFC: number; // Response for a class
285
+
286
+ // Inheritance
287
+ DIT: number; // Depth of inheritance tree
288
+ NOC: number; // Number of children
289
+
290
+ // Cohesion
291
+ LCOM: number; // Lack of cohesion in methods
292
+
293
+ // Documentation
294
+ doc_coverage: number; // Fraction of public APIs documented
295
+
296
+ // Composite scores (0–100)
297
+ maintainability_index: number;
298
+ code_quality_index: number;
299
+ bug_hotspot_score: number;
300
+ refactoring_roi: number;
301
+ }
302
+ ```
248
303
 
249
304
  ## Key Analysis Features
250
305
 
@@ -253,6 +308,9 @@ interface SastFinding {
253
308
  - **Inter-Procedural Analysis**: Tracks taint across method boundaries
254
309
  - **Sanitizer Recognition**: Detects PreparedStatement, ESAPI, escapeHtml, and other sanitizers
255
310
  - **Collection Tracking**: Precise taint tracking through List/Map operations with index shifting
311
+ - **Dominator Tree Analysis**: Powers `missing-guard-dom` (CWE-285) and `cleanup-verify` (CWE-772) via post-dominator computation
312
+ - **TypeHierarchy Resolution**: `PreparedStatement.executeQuery()` matches `Statement`-level sink configs — no duplicate config entries needed
313
+ - **Exception Flow Graph**: Tracks try/catch structure for `unhandled-exception`, `broad-catch`, `swallowed-exception`
256
314
 
257
315
  ## Benchmark Results
258
316
 
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Pass: cleanup-verify (#54, CWE-772)
3
+ *
4
+ * Detects resources that have a close() call but that close() does not
5
+ * post-dominate the acquisition point — meaning some control-flow paths
6
+ * skip the cleanup entirely.
7
+ *
8
+ * Detection strategy:
9
+ * 1. Find resource-opening calls (same set as ResourceLeakPass).
10
+ * 2. Locate the corresponding close() call within the enclosing method.
11
+ * 3. Build a post-dominator graph by reversing all CFG edges and computing
12
+ * a DominatorGraph from the exit block.
13
+ * 4. If close() block does NOT post-dominate the open block → emit finding.
14
+ *
15
+ * Languages: Java, Python, JavaScript/TypeScript.
16
+ * Skips: Rust (RAII guarantees cleanup), Bash.
17
+ *
18
+ * Note: complements ResourceLeakPass, which handles the no-close() case.
19
+ */
20
+ import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
21
+ export interface CleanupVerifyResult {
22
+ findings: number;
23
+ }
24
+ export declare class CleanupVerifyPass implements AnalysisPass<CleanupVerifyResult> {
25
+ readonly name = "cleanup-verify";
26
+ readonly category: "reliability";
27
+ run(ctx: PassContext): CleanupVerifyResult;
28
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Pass: cleanup-verify (#54, CWE-772)
3
+ *
4
+ * Detects resources that have a close() call but that close() does not
5
+ * post-dominate the acquisition point — meaning some control-flow paths
6
+ * skip the cleanup entirely.
7
+ *
8
+ * Detection strategy:
9
+ * 1. Find resource-opening calls (same set as ResourceLeakPass).
10
+ * 2. Locate the corresponding close() call within the enclosing method.
11
+ * 3. Build a post-dominator graph by reversing all CFG edges and computing
12
+ * a DominatorGraph from the exit block.
13
+ * 4. If close() block does NOT post-dominate the open block → emit finding.
14
+ *
15
+ * Languages: Java, Python, JavaScript/TypeScript.
16
+ * Skips: Rust (RAII guarantees cleanup), Bash.
17
+ *
18
+ * Note: complements ResourceLeakPass, which handles the no-close() case.
19
+ */
20
+ import { DominatorGraph } from '../../graph/dominator-graph.js';
21
+ /** Resource-opening constructors (same set as ResourceLeakPass). */
22
+ const RESOURCE_CTORS = new Set([
23
+ 'FileInputStream', 'FileOutputStream', 'FileReader', 'FileWriter',
24
+ 'BufferedReader', 'BufferedWriter', 'PrintWriter', 'InputStreamReader',
25
+ 'OutputStreamWriter', 'RandomAccessFile', 'DataInputStream', 'DataOutputStream',
26
+ 'ObjectInputStream', 'ObjectOutputStream', 'ZipInputStream', 'ZipOutputStream',
27
+ 'JarInputStream', 'JarOutputStream', 'GZIPInputStream', 'GZIPOutputStream',
28
+ 'FileChannel', 'Socket', 'ServerSocket', 'DatagramSocket',
29
+ ]);
30
+ /** Factory / open methods that return closeable resources. */
31
+ const RESOURCE_FACTORY_METHODS = new Set([
32
+ 'openConnection', 'openStream', 'newInputStream', 'newOutputStream',
33
+ 'newBufferedReader', 'newBufferedWriter', 'newByteChannel',
34
+ 'open', 'createReadStream', 'createWriteStream', 'createConnection',
35
+ ]);
36
+ /** Methods that release a resource. */
37
+ const CLOSE_METHODS = new Set([
38
+ 'close', 'dispose', 'shutdown', 'disconnect', 'release', 'destroy', 'free',
39
+ 'shutdownNow', 'terminate',
40
+ ]);
41
+ /**
42
+ * Build a post-dominator graph by reversing all CFG edges and running
43
+ * the dominator algorithm from the exit block.
44
+ * `postDom.dominates(A, B)` means "A post-dominates B in the original CFG".
45
+ */
46
+ function buildPostDomGraph(cfg) {
47
+ const exitBlock = cfg.blocks.find(b => b.type === 'exit') ??
48
+ cfg.blocks.find(b => !cfg.edges.some(e => e.from === b.id));
49
+ if (!exitBlock || cfg.blocks.length === 0) {
50
+ return new DominatorGraph({ blocks: [], edges: [] });
51
+ }
52
+ const reversed = {
53
+ blocks: cfg.blocks,
54
+ edges: cfg.edges.map(e => ({ from: e.to, to: e.from, type: e.type })),
55
+ };
56
+ return new DominatorGraph(reversed, exitBlock.id);
57
+ }
58
+ export class CleanupVerifyPass {
59
+ name = 'cleanup-verify';
60
+ category = 'reliability';
61
+ run(ctx) {
62
+ const { graph, language } = ctx;
63
+ // Rust RAII guarantees cleanup; Bash has no structured resource model
64
+ if (language === 'rust' || language === 'bash')
65
+ return { findings: 0 };
66
+ const { cfg, calls } = graph.ir;
67
+ const file = graph.ir.meta.file;
68
+ if (cfg.blocks.length === 0)
69
+ return { findings: 0 };
70
+ const postDom = buildPostDomGraph(cfg);
71
+ const blockContainingLine = (line) => cfg.blocks.find(b => b.start_line <= line && line <= b.end_line) ?? null;
72
+ let count = 0;
73
+ for (const call of calls) {
74
+ const name = call.method_name;
75
+ const isConstructor = call.is_constructor === true && RESOURCE_CTORS.has(name);
76
+ const isFactory = !call.is_constructor && RESOURCE_FACTORY_METHODS.has(name);
77
+ if (!isConstructor && !isFactory)
78
+ continue;
79
+ const openLine = call.location.line;
80
+ // Resource must be captured in a variable to be trackable
81
+ const defs = graph.defsAtLine(openLine);
82
+ if (defs.length === 0)
83
+ continue;
84
+ const resourceVar = defs[0].variable;
85
+ const methodInfo = graph.methodAtLine(openLine);
86
+ if (!methodInfo)
87
+ continue;
88
+ const methodEnd = methodInfo.method.end_line;
89
+ // Find the first close() call for this resource within the enclosing method
90
+ const closeCall = calls.find(c => CLOSE_METHODS.has(c.method_name) &&
91
+ c.receiver === resourceVar &&
92
+ c.location.line > openLine &&
93
+ c.location.line <= methodEnd);
94
+ // ResourceLeakPass handles the no-close() case; we only care about
95
+ // close() calls that may be skipped on some paths
96
+ if (!closeCall)
97
+ continue;
98
+ const openBlock = blockContainingLine(openLine);
99
+ const closeBlock = blockContainingLine(closeCall.location.line);
100
+ if (!openBlock || !closeBlock)
101
+ continue;
102
+ // If close post-dominates open, cleanup is guaranteed on every exit path
103
+ if (postDom.dominates(closeBlock.id, openBlock.id))
104
+ continue;
105
+ count++;
106
+ ctx.addFinding({
107
+ id: `cleanup-verify-${file}-${openLine}`,
108
+ pass: this.name,
109
+ category: this.category,
110
+ rule_id: 'cleanup-verify',
111
+ cwe: 'CWE-772',
112
+ severity: 'medium',
113
+ level: 'warning',
114
+ message: `Resource \`${resourceVar}\` opened at line ${openLine} may not close on all ` +
115
+ `paths — close() at line ${closeCall.location.line} does not post-dominate ` +
116
+ `the acquisition`,
117
+ file,
118
+ line: openLine,
119
+ fix: 'Use try-with-resources (Java) or a finally block to guarantee cleanup on all paths',
120
+ evidence: {
121
+ resource: name,
122
+ variable: resourceVar,
123
+ close_line: closeCall.location.line,
124
+ },
125
+ });
126
+ }
127
+ return { findings: count };
128
+ }
129
+ }
130
+ //# sourceMappingURL=cleanup-verify-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup-verify-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/cleanup-verify-pass.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Pass: missing-guard-dom (#53, CWE-285)
3
+ *
4
+ * Detects sensitive operations that are not dominated by an authentication
5
+ * or authorization check on all control-flow paths within the same method.
6
+ *
7
+ * Detection strategy:
8
+ * 1. Identify calls to known authentication methods and sensitive operations.
9
+ * 2. Build a DominatorGraph from the file-level CFG.
10
+ * 3. For each sensitive operation, find the CFG block containing it and check
11
+ * whether any auth-check block in the same method dominates that block.
12
+ * 4. If no auth-check block dominates the sensitive-op block → emit finding.
13
+ *
14
+ * Language: Java only (other languages handled differently or not yet).
15
+ * Dedup: at most one finding per method.
16
+ */
17
+ import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
18
+ export interface MissingGuardDomResult {
19
+ findings: number;
20
+ }
21
+ export declare class MissingGuardDomPass implements AnalysisPass<MissingGuardDomResult> {
22
+ readonly name = "missing-guard-dom";
23
+ readonly category: "security";
24
+ run(ctx: PassContext): MissingGuardDomResult;
25
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Pass: missing-guard-dom (#53, CWE-285)
3
+ *
4
+ * Detects sensitive operations that are not dominated by an authentication
5
+ * or authorization check on all control-flow paths within the same method.
6
+ *
7
+ * Detection strategy:
8
+ * 1. Identify calls to known authentication methods and sensitive operations.
9
+ * 2. Build a DominatorGraph from the file-level CFG.
10
+ * 3. For each sensitive operation, find the CFG block containing it and check
11
+ * whether any auth-check block in the same method dominates that block.
12
+ * 4. If no auth-check block dominates the sensitive-op block → emit finding.
13
+ *
14
+ * Language: Java only (other languages handled differently or not yet).
15
+ * Dedup: at most one finding per method.
16
+ */
17
+ import { DominatorGraph } from '../../graph/dominator-graph.js';
18
+ const AUTH_METHODS = new Set([
19
+ 'authenticate', 'isAuthenticated', 'isAuthorized', 'isAdmin',
20
+ 'checkAuth', 'hasPermission', 'requiresAuth', 'verifyToken',
21
+ 'validateToken', 'checkRole', 'authorize', 'isLoggedIn',
22
+ ]);
23
+ const SENSITIVE_METHODS = new Set([
24
+ 'delete', 'deleteById', 'drop', 'truncate', 'executeUpdate',
25
+ 'createUser', 'createAdmin', 'modifyPermission', 'grantRole',
26
+ 'setAdmin', 'elevatePrivilege',
27
+ ]);
28
+ export class MissingGuardDomPass {
29
+ name = 'missing-guard-dom';
30
+ category = 'security';
31
+ run(ctx) {
32
+ const { graph, language } = ctx;
33
+ if (language !== 'java')
34
+ return { findings: 0 };
35
+ const { cfg, calls } = graph.ir;
36
+ if (cfg.blocks.length === 0 || cfg.edges.length === 0)
37
+ return { findings: 0 };
38
+ const dom = new DominatorGraph(cfg);
39
+ const file = graph.ir.meta.file;
40
+ // Collect auth-check and sensitive-op call lines from the IR
41
+ const authCallLines = [];
42
+ const sensitiveOps = [];
43
+ for (const call of calls) {
44
+ if (AUTH_METHODS.has(call.method_name)) {
45
+ authCallLines.push(call.location.line);
46
+ }
47
+ if (SENSITIVE_METHODS.has(call.method_name)) {
48
+ sensitiveOps.push({ line: call.location.line, method: call.method_name });
49
+ }
50
+ }
51
+ if (sensitiveOps.length === 0)
52
+ return { findings: 0 };
53
+ // Helper: find the CFG block whose [start_line, end_line] contains a given line
54
+ const blockContainingLine = (line) => cfg.blocks.find(b => b.start_line <= line && line <= b.end_line) ?? null;
55
+ // Emit at most one finding per method to avoid noise
56
+ const reportedMethods = new Set();
57
+ let count = 0;
58
+ for (const op of sensitiveOps) {
59
+ const opBlock = blockContainingLine(op.line);
60
+ if (!opBlock)
61
+ continue;
62
+ const methodInfo = graph.methodAtLine(op.line);
63
+ if (!methodInfo)
64
+ continue;
65
+ const methodKey = `${methodInfo.type.name}::${methodInfo.method.name}`;
66
+ if (reportedMethods.has(methodKey))
67
+ continue;
68
+ const { start_line, end_line } = methodInfo.method;
69
+ // Restrict auth checks to those inside the same method
70
+ const authInMethod = authCallLines.filter(l => l >= start_line && l <= end_line);
71
+ // Check whether any auth-check block dominates the sensitive-op block
72
+ const dominated = authInMethod.some(authLine => {
73
+ const authBlock = blockContainingLine(authLine);
74
+ return authBlock !== null && dom.dominates(authBlock.id, opBlock.id);
75
+ });
76
+ if (!dominated) {
77
+ reportedMethods.add(methodKey);
78
+ count++;
79
+ ctx.addFinding({
80
+ id: `missing-guard-dom-${file}-${op.line}`,
81
+ pass: this.name,
82
+ category: this.category,
83
+ rule_id: 'missing-guard-dom',
84
+ cwe: 'CWE-285',
85
+ severity: 'high',
86
+ level: 'error',
87
+ message: `Sensitive operation \`${op.method}()\` at line ${op.line} is not dominated ` +
88
+ `by an authentication check`,
89
+ file,
90
+ line: op.line,
91
+ fix: `Add authentication/authorization check on all paths leading to line ${op.line}`,
92
+ evidence: { method: op.method },
93
+ });
94
+ }
95
+ }
96
+ return { findings: count };
97
+ }
98
+ }
99
+ //# sourceMappingURL=missing-guard-dom-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-guard-dom-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/missing-guard-dom-pass.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Pass: missing-override (#64)
3
+ *
4
+ * Detects Java methods that override a parent class method but lack the
5
+ * @Override annotation. Without @Override the compiler cannot catch signature
6
+ * mismatches introduced by a parent-class refactoring.
7
+ *
8
+ * Detection strategy:
9
+ * 1. Build a map of class → method names from all types in the IR.
10
+ * 2. Build a parent map: class name → direct parent class name (strip generics).
11
+ * 3. For each class that has a parent in the same file, walk the inheritance
12
+ * chain (max 10 hops, cycle guard) to collect all ancestor method names.
13
+ * 4. For each non-constructor, non-private, non-static, non-abstract method
14
+ * whose name appears in the ancestor set — if @Override is absent → finding.
15
+ *
16
+ * Language: Java only.
17
+ * Dedup: at most one finding per class:method pair.
18
+ */
19
+ import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
20
+ export interface MissingOverrideResult {
21
+ findings: number;
22
+ }
23
+ export declare class MissingOverridePass implements AnalysisPass<MissingOverrideResult> {
24
+ readonly name = "missing-override";
25
+ readonly category: "maintainability";
26
+ run(ctx: PassContext): MissingOverrideResult;
27
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Pass: missing-override (#64)
3
+ *
4
+ * Detects Java methods that override a parent class method but lack the
5
+ * @Override annotation. Without @Override the compiler cannot catch signature
6
+ * mismatches introduced by a parent-class refactoring.
7
+ *
8
+ * Detection strategy:
9
+ * 1. Build a map of class → method names from all types in the IR.
10
+ * 2. Build a parent map: class name → direct parent class name (strip generics).
11
+ * 3. For each class that has a parent in the same file, walk the inheritance
12
+ * chain (max 10 hops, cycle guard) to collect all ancestor method names.
13
+ * 4. For each non-constructor, non-private, non-static, non-abstract method
14
+ * whose name appears in the ancestor set — if @Override is absent → finding.
15
+ *
16
+ * Language: Java only.
17
+ * Dedup: at most one finding per class:method pair.
18
+ */
19
+ export class MissingOverridePass {
20
+ name = 'missing-override';
21
+ category = 'maintainability';
22
+ run(ctx) {
23
+ const { graph, language } = ctx;
24
+ if (language !== 'java')
25
+ return { findings: 0 };
26
+ const { types } = graph.ir;
27
+ const file = graph.ir.meta.file;
28
+ if (types.length === 0)
29
+ return { findings: 0 };
30
+ // Build map: class name → Set<method name>
31
+ const methodsByClass = new Map();
32
+ for (const type of types) {
33
+ methodsByClass.set(type.name, new Set(type.methods.map(m => m.name)));
34
+ }
35
+ // Build parent map: class name → direct parent class name (generics stripped)
36
+ const parentMap = new Map();
37
+ for (const type of types) {
38
+ if (type.extends) {
39
+ const parent = type.extends.replace(/<[^>]*>/g, '').trim();
40
+ parentMap.set(type.name, parent);
41
+ }
42
+ }
43
+ if (parentMap.size === 0)
44
+ return { findings: 0 };
45
+ // Walk inheritance chain to collect all ancestor method names
46
+ const getAncestorMethods = (className) => {
47
+ const methods = new Set();
48
+ const visited = new Set();
49
+ let current = parentMap.get(className);
50
+ let hops = 0;
51
+ while (current && !visited.has(current) && hops < 10) {
52
+ visited.add(current);
53
+ const parentMethods = methodsByClass.get(current);
54
+ if (parentMethods) {
55
+ for (const m of parentMethods)
56
+ methods.add(m);
57
+ }
58
+ current = parentMap.get(current);
59
+ hops++;
60
+ }
61
+ return methods;
62
+ };
63
+ const dedup = new Set();
64
+ let count = 0;
65
+ for (const type of types) {
66
+ if (!parentMap.has(type.name))
67
+ continue;
68
+ const ancestorMethods = getAncestorMethods(type.name);
69
+ if (ancestorMethods.size === 0)
70
+ continue;
71
+ for (const method of type.methods) {
72
+ // Skip constructors (same name as class)
73
+ if (method.name === type.name)
74
+ continue;
75
+ // Skip private / static / abstract methods
76
+ if (method.modifiers.includes('private'))
77
+ continue;
78
+ if (method.modifiers.includes('static'))
79
+ continue;
80
+ if (method.modifiers.includes('abstract'))
81
+ continue;
82
+ if (!ancestorMethods.has(method.name))
83
+ continue;
84
+ if (method.annotations.includes('Override'))
85
+ continue;
86
+ const key = `${type.name}:${method.name}`;
87
+ if (dedup.has(key))
88
+ continue;
89
+ dedup.add(key);
90
+ count++;
91
+ ctx.addFinding({
92
+ id: `missing-override-${file}-${method.start_line}`,
93
+ pass: this.name,
94
+ category: this.category,
95
+ rule_id: 'missing-override',
96
+ severity: 'low',
97
+ level: 'warning',
98
+ message: `Method \`${method.name}()\` in \`${type.name}\` overrides a parent method ` +
99
+ `but lacks @Override`,
100
+ file,
101
+ line: method.start_line,
102
+ fix: 'Add @Override to make the intent explicit and catch signature mismatches at compile time',
103
+ evidence: { className: type.name, methodName: method.name },
104
+ });
105
+ }
106
+ }
107
+ return { findings: count };
108
+ }
109
+ }
110
+ //# sourceMappingURL=missing-override-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-override-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/missing-override-pass.ts"],"names":[],"mappings":"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"}