circle-ir 3.8.4 → 3.9.8

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 (178) hide show
  1. package/README.md +82 -5
  2. package/dist/analysis/dfg-verifier.d.ts +3 -14
  3. package/dist/analysis/dfg-verifier.js +43 -74
  4. package/dist/analysis/dfg-verifier.js.map +1 -1
  5. package/dist/analysis/interprocedural.d.ts +5 -1
  6. package/dist/analysis/interprocedural.js +62 -60
  7. package/dist/analysis/interprocedural.js.map +1 -1
  8. package/dist/analysis/metrics/index.d.ts +2 -0
  9. package/dist/analysis/metrics/index.js +2 -0
  10. package/dist/analysis/metrics/index.js.map +1 -0
  11. package/dist/analysis/metrics/metric-pass.d.ts +27 -0
  12. package/dist/analysis/metrics/metric-pass.js +2 -0
  13. package/dist/analysis/metrics/metric-pass.js.map +1 -0
  14. package/dist/analysis/metrics/metric-runner.d.ts +21 -0
  15. package/dist/analysis/metrics/metric-runner.js +47 -0
  16. package/dist/analysis/metrics/metric-runner.js.map +1 -0
  17. package/dist/analysis/metrics/passes/cohesion-metrics-pass.d.ts +21 -0
  18. package/dist/analysis/metrics/passes/cohesion-metrics-pass.js +100 -0
  19. package/dist/analysis/metrics/passes/cohesion-metrics-pass.js.map +1 -0
  20. package/dist/analysis/metrics/passes/complexity-metrics-pass.d.ts +15 -0
  21. package/dist/analysis/metrics/passes/complexity-metrics-pass.js +76 -0
  22. package/dist/analysis/metrics/passes/complexity-metrics-pass.js.map +1 -0
  23. package/dist/analysis/metrics/passes/composite-metrics-pass.d.ts +17 -0
  24. package/dist/analysis/metrics/passes/composite-metrics-pass.js +77 -0
  25. package/dist/analysis/metrics/passes/composite-metrics-pass.js.map +1 -0
  26. package/dist/analysis/metrics/passes/coupling-metrics-pass.d.ts +19 -0
  27. package/dist/analysis/metrics/passes/coupling-metrics-pass.js +94 -0
  28. package/dist/analysis/metrics/passes/coupling-metrics-pass.js.map +1 -0
  29. package/dist/analysis/metrics/passes/data-flow-metrics-pass.d.ts +14 -0
  30. package/dist/analysis/metrics/passes/data-flow-metrics-pass.js +25 -0
  31. package/dist/analysis/metrics/passes/data-flow-metrics-pass.js.map +1 -0
  32. package/dist/analysis/metrics/passes/documentation-metrics-pass.d.ts +15 -0
  33. package/dist/analysis/metrics/passes/documentation-metrics-pass.js +64 -0
  34. package/dist/analysis/metrics/passes/documentation-metrics-pass.js.map +1 -0
  35. package/dist/analysis/metrics/passes/halstead-metrics-pass.d.ts +16 -0
  36. package/dist/analysis/metrics/passes/halstead-metrics-pass.js +95 -0
  37. package/dist/analysis/metrics/passes/halstead-metrics-pass.js.map +1 -0
  38. package/dist/analysis/metrics/passes/inheritance-metrics-pass.d.ts +18 -0
  39. package/dist/analysis/metrics/passes/inheritance-metrics-pass.js +73 -0
  40. package/dist/analysis/metrics/passes/inheritance-metrics-pass.js.map +1 -0
  41. package/dist/analysis/metrics/passes/size-metrics-pass.d.ts +11 -0
  42. package/dist/analysis/metrics/passes/size-metrics-pass.js +64 -0
  43. package/dist/analysis/metrics/passes/size-metrics-pass.js.map +1 -0
  44. package/dist/analysis/passes/circular-dependency-pass.d.ts +18 -0
  45. package/dist/analysis/passes/circular-dependency-pass.js +39 -0
  46. package/dist/analysis/passes/circular-dependency-pass.js.map +1 -0
  47. package/dist/analysis/passes/constant-propagation-pass.d.ts +22 -0
  48. package/dist/analysis/passes/constant-propagation-pass.js +44 -0
  49. package/dist/analysis/passes/constant-propagation-pass.js.map +1 -0
  50. package/dist/analysis/passes/cross-file-pass.d.ts +27 -0
  51. package/dist/analysis/passes/cross-file-pass.js +102 -0
  52. package/dist/analysis/passes/cross-file-pass.js.map +1 -0
  53. package/dist/analysis/passes/dead-code-pass.d.ts +25 -0
  54. package/dist/analysis/passes/dead-code-pass.js +117 -0
  55. package/dist/analysis/passes/dead-code-pass.js.map +1 -0
  56. package/dist/analysis/passes/deep-inheritance-pass.d.ts +30 -0
  57. package/dist/analysis/passes/deep-inheritance-pass.js +82 -0
  58. package/dist/analysis/passes/deep-inheritance-pass.js.map +1 -0
  59. package/dist/analysis/passes/dependency-fan-out-pass.d.ts +19 -0
  60. package/dist/analysis/passes/dependency-fan-out-pass.js +35 -0
  61. package/dist/analysis/passes/dependency-fan-out-pass.js.map +1 -0
  62. package/dist/analysis/passes/infinite-loop-pass.d.ts +31 -0
  63. package/dist/analysis/passes/infinite-loop-pass.js +126 -0
  64. package/dist/analysis/passes/infinite-loop-pass.js.map +1 -0
  65. package/dist/analysis/passes/interprocedural-pass.d.ts +29 -0
  66. package/dist/analysis/passes/interprocedural-pass.js +169 -0
  67. package/dist/analysis/passes/interprocedural-pass.js.map +1 -0
  68. package/dist/analysis/passes/language-sources-pass.d.ts +76 -0
  69. package/dist/analysis/passes/language-sources-pass.js +491 -0
  70. package/dist/analysis/passes/language-sources-pass.js.map +1 -0
  71. package/dist/analysis/passes/leaked-global-pass.d.ts +34 -0
  72. package/dist/analysis/passes/leaked-global-pass.js +108 -0
  73. package/dist/analysis/passes/leaked-global-pass.js.map +1 -0
  74. package/dist/analysis/passes/missing-await-pass.d.ts +29 -0
  75. package/dist/analysis/passes/missing-await-pass.js +90 -0
  76. package/dist/analysis/passes/missing-await-pass.js.map +1 -0
  77. package/dist/analysis/passes/missing-public-doc-pass.d.ts +35 -0
  78. package/dist/analysis/passes/missing-public-doc-pass.js +148 -0
  79. package/dist/analysis/passes/missing-public-doc-pass.js.map +1 -0
  80. package/dist/analysis/passes/n-plus-one-pass.d.ts +29 -0
  81. package/dist/analysis/passes/n-plus-one-pass.js +100 -0
  82. package/dist/analysis/passes/n-plus-one-pass.js.map +1 -0
  83. package/dist/analysis/passes/null-deref-pass.d.ts +32 -0
  84. package/dist/analysis/passes/null-deref-pass.js +130 -0
  85. package/dist/analysis/passes/null-deref-pass.js.map +1 -0
  86. package/dist/analysis/passes/orphan-module-pass.d.ts +21 -0
  87. package/dist/analysis/passes/orphan-module-pass.js +38 -0
  88. package/dist/analysis/passes/orphan-module-pass.js.map +1 -0
  89. package/dist/analysis/passes/react-inline-jsx-pass.d.ts +36 -0
  90. package/dist/analysis/passes/react-inline-jsx-pass.js +140 -0
  91. package/dist/analysis/passes/react-inline-jsx-pass.js.map +1 -0
  92. package/dist/analysis/passes/redundant-loop-pass.d.ts +30 -0
  93. package/dist/analysis/passes/redundant-loop-pass.js +146 -0
  94. package/dist/analysis/passes/redundant-loop-pass.js.map +1 -0
  95. package/dist/analysis/passes/resource-leak-pass.d.ts +43 -0
  96. package/dist/analysis/passes/resource-leak-pass.js +156 -0
  97. package/dist/analysis/passes/resource-leak-pass.js.map +1 -0
  98. package/dist/analysis/passes/serial-await-pass.d.ts +36 -0
  99. package/dist/analysis/passes/serial-await-pass.js +132 -0
  100. package/dist/analysis/passes/serial-await-pass.js.map +1 -0
  101. package/dist/analysis/passes/sink-filter-pass.d.ts +39 -0
  102. package/dist/analysis/passes/sink-filter-pass.js +231 -0
  103. package/dist/analysis/passes/sink-filter-pass.js.map +1 -0
  104. package/dist/analysis/passes/stale-doc-ref-pass.d.ts +21 -0
  105. package/dist/analysis/passes/stale-doc-ref-pass.js +96 -0
  106. package/dist/analysis/passes/stale-doc-ref-pass.js.map +1 -0
  107. package/dist/analysis/passes/string-concat-loop-pass.d.ts +26 -0
  108. package/dist/analysis/passes/string-concat-loop-pass.js +87 -0
  109. package/dist/analysis/passes/string-concat-loop-pass.js.map +1 -0
  110. package/dist/analysis/passes/sync-io-async-pass.d.ts +28 -0
  111. package/dist/analysis/passes/sync-io-async-pass.js +80 -0
  112. package/dist/analysis/passes/sync-io-async-pass.js.map +1 -0
  113. package/dist/analysis/passes/taint-matcher-pass.d.ts +24 -0
  114. package/dist/analysis/passes/taint-matcher-pass.js +71 -0
  115. package/dist/analysis/passes/taint-matcher-pass.js.map +1 -0
  116. package/dist/analysis/passes/taint-propagation-pass.d.ts +22 -0
  117. package/dist/analysis/passes/taint-propagation-pass.js +266 -0
  118. package/dist/analysis/passes/taint-propagation-pass.js.map +1 -0
  119. package/dist/analysis/passes/todo-in-prod-pass.d.ts +28 -0
  120. package/dist/analysis/passes/todo-in-prod-pass.js +71 -0
  121. package/dist/analysis/passes/todo-in-prod-pass.js.map +1 -0
  122. package/dist/analysis/passes/unbounded-collection-pass.d.ts +32 -0
  123. package/dist/analysis/passes/unbounded-collection-pass.js +128 -0
  124. package/dist/analysis/passes/unbounded-collection-pass.js.map +1 -0
  125. package/dist/analysis/passes/unchecked-return-pass.d.ts +34 -0
  126. package/dist/analysis/passes/unchecked-return-pass.js +106 -0
  127. package/dist/analysis/passes/unchecked-return-pass.js.map +1 -0
  128. package/dist/analysis/passes/unused-variable-pass.d.ts +36 -0
  129. package/dist/analysis/passes/unused-variable-pass.js +150 -0
  130. package/dist/analysis/passes/unused-variable-pass.js.map +1 -0
  131. package/dist/analysis/passes/variable-shadowing-pass.d.ts +41 -0
  132. package/dist/analysis/passes/variable-shadowing-pass.js +211 -0
  133. package/dist/analysis/passes/variable-shadowing-pass.js.map +1 -0
  134. package/dist/analysis/path-finder.d.ts +3 -13
  135. package/dist/analysis/path-finder.js +48 -63
  136. package/dist/analysis/path-finder.js.map +1 -1
  137. package/dist/analysis/taint-matcher.js +8 -1
  138. package/dist/analysis/taint-matcher.js.map +1 -1
  139. package/dist/analysis/taint-propagation.d.ts +5 -1
  140. package/dist/analysis/taint-propagation.js +44 -41
  141. package/dist/analysis/taint-propagation.js.map +1 -1
  142. package/dist/analyzer.d.ts +48 -1
  143. package/dist/analyzer.js +252 -1476
  144. package/dist/analyzer.js.map +1 -1
  145. package/dist/browser/circle-ir.js +3952 -1270
  146. package/dist/core/circle-ir-core.cjs +360 -106
  147. package/dist/core/circle-ir-core.js +360 -106
  148. package/dist/core/extractors/imports.js +18 -0
  149. package/dist/core/extractors/imports.js.map +1 -1
  150. package/dist/graph/analysis-pass.d.ts +68 -0
  151. package/dist/graph/analysis-pass.js +51 -0
  152. package/dist/graph/analysis-pass.js.map +1 -0
  153. package/dist/graph/code-graph.d.ts +92 -0
  154. package/dist/graph/code-graph.js +262 -0
  155. package/dist/graph/code-graph.js.map +1 -0
  156. package/dist/graph/dominator-graph.d.ts +53 -0
  157. package/dist/graph/dominator-graph.js +256 -0
  158. package/dist/graph/dominator-graph.js.map +1 -0
  159. package/dist/graph/import-graph.d.ts +33 -0
  160. package/dist/graph/import-graph.js +170 -0
  161. package/dist/graph/import-graph.js.map +1 -0
  162. package/dist/graph/index.d.ts +5 -0
  163. package/dist/graph/index.js +6 -0
  164. package/dist/graph/index.js.map +1 -0
  165. package/dist/graph/project-graph.d.ts +43 -0
  166. package/dist/graph/project-graph.js +80 -0
  167. package/dist/graph/project-graph.js.map +1 -0
  168. package/dist/graph/scope-graph.d.ts +63 -0
  169. package/dist/graph/scope-graph.js +89 -0
  170. package/dist/graph/scope-graph.js.map +1 -0
  171. package/dist/index.d.ts +3 -2
  172. package/dist/index.js +3 -1
  173. package/dist/index.js.map +1 -1
  174. package/dist/resolution/cross-file.js +52 -19
  175. package/dist/resolution/cross-file.js.map +1 -1
  176. package/dist/types/index.d.ts +151 -0
  177. package/docs/SPEC.md +10 -6
  178. package/package.json +1 -1
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Pass #33: stale-doc-ref
3
+ *
4
+ * Flags doc comment references ({@link ClassName} and @see ClassName) that
5
+ * point to symbols not found in the file's type declarations or imports.
6
+ * Stale doc refs cause confusion and erode documentation trustworthiness.
7
+ *
8
+ * Category: maintainability | Severity: low | Level: note | CWE: none
9
+ */
10
+ // Matches /** ... */ blocks (non-greedy, multiline)
11
+ const DOC_BLOCK_RE = /\/\*\*([\s\S]*?)\*\//g;
12
+ // Matches {@link Foo.Bar#method} or {@link Foo}
13
+ const LINK_RE = /\{@link\s+([\w.#]+)/g;
14
+ // Matches @see Foo.Bar or @see Foo
15
+ const SEE_RE = /@see\s+([\w.#]+)/g;
16
+ /**
17
+ * Normalize a symbol reference: strip method fragment (#method) and
18
+ * take the last dot-separated segment (so "java.util.List" → "List").
19
+ */
20
+ function normalizeRef(raw) {
21
+ const withoutMethod = raw.split('#')[0];
22
+ const parts = withoutMethod.split('.');
23
+ return parts[parts.length - 1] ?? raw;
24
+ }
25
+ /**
26
+ * Return the 1-based line number of the start of a match within `code`.
27
+ */
28
+ function lineOfIndex(code, index) {
29
+ let line = 1;
30
+ for (let i = 0; i < index && i < code.length; i++) {
31
+ if (code[i] === '\n')
32
+ line++;
33
+ }
34
+ return line;
35
+ }
36
+ export class StaleDocRefPass {
37
+ name = 'stale-doc-ref';
38
+ category = 'maintainability';
39
+ run(ctx) {
40
+ const staleRefs = [];
41
+ // Build known-symbol set from types + imports
42
+ const knownSymbols = new Set();
43
+ for (const t of ctx.graph.ir.types) {
44
+ knownSymbols.add(t.name);
45
+ }
46
+ for (const imp of ctx.graph.ir.imports) {
47
+ if (imp.imported_name && imp.imported_name !== '*' && imp.imported_name !== 'default') {
48
+ knownSymbols.add(imp.imported_name);
49
+ }
50
+ if (imp.alias) {
51
+ knownSymbols.add(imp.alias);
52
+ }
53
+ }
54
+ const code = ctx.code;
55
+ DOC_BLOCK_RE.lastIndex = 0;
56
+ let blockMatch;
57
+ while ((blockMatch = DOC_BLOCK_RE.exec(code)) !== null) {
58
+ const blockStart = blockMatch.index;
59
+ const blockText = blockMatch[0];
60
+ // Extract all refs from the block
61
+ const refs = [];
62
+ LINK_RE.lastIndex = 0;
63
+ let m;
64
+ while ((m = LINK_RE.exec(blockText)) !== null) {
65
+ refs.push({ raw: m[1], offsetInBlock: m.index });
66
+ }
67
+ SEE_RE.lastIndex = 0;
68
+ while ((m = SEE_RE.exec(blockText)) !== null) {
69
+ refs.push({ raw: m[1], offsetInBlock: m.index });
70
+ }
71
+ for (const { raw, offsetInBlock } of refs) {
72
+ const normalized = normalizeRef(raw);
73
+ if (!knownSymbols.has(normalized)) {
74
+ const absIdx = blockStart + offsetInBlock;
75
+ const line = lineOfIndex(code, absIdx);
76
+ staleRefs.push({ line, ref: normalized });
77
+ const finding = {
78
+ id: `stale-doc-ref-${ctx.graph.ir.meta.file.replace(/[^a-z0-9]/gi, '-')}-${line}`,
79
+ pass: 'stale-doc-ref',
80
+ category: 'maintainability',
81
+ rule_id: 'stale-doc-ref',
82
+ severity: 'low',
83
+ level: 'note',
84
+ message: `Doc comment references unknown symbol '${normalized}'. Update or remove the stale reference.`,
85
+ file: ctx.graph.ir.meta.file,
86
+ line,
87
+ evidence: { ref: normalized, raw },
88
+ };
89
+ ctx.addFinding(finding);
90
+ }
91
+ }
92
+ }
93
+ return { staleRefs };
94
+ }
95
+ }
96
+ //# sourceMappingURL=stale-doc-ref-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-doc-ref-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/stale-doc-ref-pass.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Pass #50: string-concat-loop (CWE-1046, category: performance)
3
+ *
4
+ * Detects string concatenation using `+=` inside loop bodies, which creates
5
+ * O(n²) string allocations. Each iteration copies the entire accumulated
6
+ * string, making this a common performance anti-pattern.
7
+ *
8
+ * Detection strategy:
9
+ * 1. Identify loop body line ranges via CFG back-edges (graph.loopBodies()).
10
+ * 2. For each line within a loop body, scan for `identifier +=` pattern.
11
+ * 3. Filter out obvious numeric variable names (i, count, sum, etc.) and
12
+ * numeric-looking RHS literals to avoid FP on arithmetic accumulation.
13
+ */
14
+ import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
15
+ export interface StringConcatLoopResult {
16
+ /** `+=` expressions inside loop bodies that are likely string concatenation. */
17
+ concatInLoops: Array<{
18
+ line: number;
19
+ variable: string;
20
+ }>;
21
+ }
22
+ export declare class StringConcatLoopPass implements AnalysisPass<StringConcatLoopResult> {
23
+ readonly name = "string-concat-loop";
24
+ readonly category: "performance";
25
+ run(ctx: PassContext): StringConcatLoopResult;
26
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Pass #50: string-concat-loop (CWE-1046, category: performance)
3
+ *
4
+ * Detects string concatenation using `+=` inside loop bodies, which creates
5
+ * O(n²) string allocations. Each iteration copies the entire accumulated
6
+ * string, making this a common performance anti-pattern.
7
+ *
8
+ * Detection strategy:
9
+ * 1. Identify loop body line ranges via CFG back-edges (graph.loopBodies()).
10
+ * 2. For each line within a loop body, scan for `identifier +=` pattern.
11
+ * 3. Filter out obvious numeric variable names (i, count, sum, etc.) and
12
+ * numeric-looking RHS literals to avoid FP on arithmetic accumulation.
13
+ */
14
+ /** Matches `varName +=` at a token boundary. Group 1 = variable name. */
15
+ const CONCAT_RE = /\b([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\+=/;
16
+ /**
17
+ * Variable names that almost certainly hold numeric values.
18
+ * These can safely be skipped even if they appear in `+=` expressions.
19
+ */
20
+ const NUMERIC_VAR_RE = /^(i|j|k|n|m|x|y|z|count|sum|total|size|index|len|length|num|idx|score|counter|offset|pos|col|row|result|ret|value|acc|bits|byte|bytes|flag|flags|step|delta|diff|dist|min|max|avg|mean|page|line|err|error)$/i;
21
+ /** Variable name suffixes that strongly suggest numeric accumulation. */
22
+ const NUMERIC_SUFFIX_RE = /(Count|Sum|Total|Size|Index|Length|Offset|Position|Score|Counter|Num|Amount|Val|Idx|Len|Max|Min|Avg|Delta|Diff|Step|Flag|Flags|Bits|Byte|Bytes|Calls|Items|Nodes|Edges|Blocks|Lines|Chars|Entries|Records|Rows)$/;
23
+ /**
24
+ * Matches a right-hand side that starts with a digit or decimal point —
25
+ * these are numeric literals so the `+=` is arithmetic, not string concat.
26
+ */
27
+ const NUMERIC_RHS_RE = /^\s*[\d.]/;
28
+ export class StringConcatLoopPass {
29
+ name = 'string-concat-loop';
30
+ category = 'performance';
31
+ run(ctx) {
32
+ const { graph, code } = ctx;
33
+ const file = graph.ir.meta.file;
34
+ const loops = graph.loopBodies();
35
+ if (loops.length === 0)
36
+ return { concatInLoops: [] };
37
+ const codeLines = code.split('\n');
38
+ const concatInLoops = [];
39
+ const reported = new Set(); // one finding per line
40
+ for (const loop of loops) {
41
+ for (let ln = loop.start_line; ln <= loop.end_line; ln++) {
42
+ if (reported.has(ln))
43
+ continue;
44
+ const src = codeLines[ln - 1] ?? '';
45
+ const match = CONCAT_RE.exec(src);
46
+ if (!match)
47
+ continue;
48
+ const varName = match[1];
49
+ // Skip obviously-numeric variable names
50
+ if (NUMERIC_VAR_RE.test(varName))
51
+ continue;
52
+ if (NUMERIC_SUFFIX_RE.test(varName))
53
+ continue;
54
+ // Skip if the RHS after `+=` starts with a digit/decimal (numeric literal)
55
+ const opIdx = src.indexOf('+=');
56
+ const afterOp = opIdx >= 0 ? src.slice(opIdx + 2) : '';
57
+ if (NUMERIC_RHS_RE.test(afterOp))
58
+ continue;
59
+ concatInLoops.push({ line: ln, variable: varName });
60
+ reported.add(ln);
61
+ ctx.addFinding({
62
+ id: `string-concat-loop-${file}-${ln}`,
63
+ pass: this.name,
64
+ category: this.category,
65
+ rule_id: this.name,
66
+ cwe: 'CWE-1046',
67
+ severity: 'low',
68
+ level: 'warning',
69
+ message: `String concatenation with '+=' inside a loop: '${varName}' grows O(n²). ` +
70
+ `Each iteration copies the entire accumulated string.`,
71
+ file,
72
+ line: ln,
73
+ snippet: src.trim(),
74
+ fix: `Accumulate parts in an array and join() after the loop, ` +
75
+ `or use StringBuilder (Java) / StringJoiner`,
76
+ evidence: {
77
+ variable: varName,
78
+ loop_start: loop.start_line,
79
+ loop_end: loop.end_line,
80
+ },
81
+ });
82
+ }
83
+ }
84
+ return { concatInLoops };
85
+ }
86
+ }
87
+ //# sourceMappingURL=string-concat-loop-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string-concat-loop-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/string-concat-loop-pass.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Pass #48: sync-io-async (CWE-1050, category: performance)
3
+ *
4
+ * Detects synchronous (blocking) I/O calls made inside async functions in
5
+ * JavaScript/TypeScript. Blocking calls stall the Node.js event loop,
6
+ * negating the benefits of async/await and starving other concurrent work.
7
+ *
8
+ * Detection strategy:
9
+ * 1. Collect async method line ranges from types[].methods where
10
+ * modifiers include 'async'.
11
+ * 2. For each call site within an async range, check if the method name
12
+ * ends in 'Sync' (Node.js blocking variants) or is in the curated
13
+ * BLOCKING_METHODS set.
14
+ */
15
+ import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
16
+ export interface SyncIoAsyncResult {
17
+ /** Blocking calls found inside async functions. */
18
+ blockingInAsyncFns: Array<{
19
+ line: number;
20
+ method: string;
21
+ enclosingMethod: string;
22
+ }>;
23
+ }
24
+ export declare class SyncIoAsyncPass implements AnalysisPass<SyncIoAsyncResult> {
25
+ readonly name = "sync-io-async";
26
+ readonly category: "performance";
27
+ run(ctx: PassContext): SyncIoAsyncResult;
28
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Pass #48: sync-io-async (CWE-1050, category: performance)
3
+ *
4
+ * Detects synchronous (blocking) I/O calls made inside async functions in
5
+ * JavaScript/TypeScript. Blocking calls stall the Node.js event loop,
6
+ * negating the benefits of async/await and starving other concurrent work.
7
+ *
8
+ * Detection strategy:
9
+ * 1. Collect async method line ranges from types[].methods where
10
+ * modifiers include 'async'.
11
+ * 2. For each call site within an async range, check if the method name
12
+ * ends in 'Sync' (Node.js blocking variants) or is in the curated
13
+ * BLOCKING_METHODS set.
14
+ */
15
+ /**
16
+ * Methods that are always blocking regardless of name convention.
17
+ * Kept intentionally small — only add names with virtually no async usage.
18
+ */
19
+ const BLOCKING_METHODS = new Set([
20
+ 'sleep', // Python time.sleep / any sync sleep utility
21
+ ]);
22
+ /** Any method whose name ends in 'Sync' is a blocking Node.js API variant. */
23
+ const SYNC_SUFFIX_RE = /Sync$/;
24
+ export class SyncIoAsyncPass {
25
+ name = 'sync-io-async';
26
+ category = 'performance';
27
+ run(ctx) {
28
+ const { graph, language } = ctx;
29
+ // Only relevant for JS/TS (and Python for sleep)
30
+ if (language !== 'javascript' && language !== 'typescript' && language !== 'python') {
31
+ return { blockingInAsyncFns: [] };
32
+ }
33
+ const file = graph.ir.meta.file;
34
+ // Collect async method line ranges
35
+ const asyncRanges = [];
36
+ for (const type of graph.ir.types) {
37
+ for (const method of type.methods) {
38
+ if (method.modifiers.includes('async')) {
39
+ asyncRanges.push({
40
+ start: method.start_line,
41
+ end: method.end_line,
42
+ name: method.name,
43
+ });
44
+ }
45
+ }
46
+ }
47
+ if (asyncRanges.length === 0)
48
+ return { blockingInAsyncFns: [] };
49
+ const blockingInAsyncFns = [];
50
+ for (const call of graph.ir.calls) {
51
+ const name = call.method_name;
52
+ if (!SYNC_SUFFIX_RE.test(name) && !BLOCKING_METHODS.has(name))
53
+ continue;
54
+ const line = call.location.line;
55
+ const range = asyncRanges.find(r => line >= r.start && line <= r.end);
56
+ if (!range)
57
+ continue;
58
+ blockingInAsyncFns.push({ line, method: name, enclosingMethod: range.name });
59
+ ctx.addFinding({
60
+ id: `sync-io-async-${file}-${line}`,
61
+ pass: this.name,
62
+ category: this.category,
63
+ rule_id: this.name,
64
+ cwe: 'CWE-1050',
65
+ severity: 'medium',
66
+ level: 'warning',
67
+ message: `Blocking call \`${name}()\` inside async function '${range.name}' stalls the event loop`,
68
+ file,
69
+ line,
70
+ fix: `Replace \`${name}\` with its async equivalent and await the result`,
71
+ evidence: {
72
+ blocking_method: name,
73
+ async_method: range.name,
74
+ },
75
+ });
76
+ }
77
+ return { blockingInAsyncFns };
78
+ }
79
+ }
80
+ //# sourceMappingURL=sync-io-async-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-io-async-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/sync-io-async-pass.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * TaintMatcherPass
3
+ *
4
+ * First pass in the analysis pipeline. Merges language-plugin built-in
5
+ * sources/sinks into the config, runs config-based taint matching, and
6
+ * extracts @sanitizer-annotated method names from type declarations.
7
+ */
8
+ import type { TaintSource, TaintSink, TaintSanitizer } from '../../types/index.js';
9
+ import type { TaintConfig } from '../../types/config.js';
10
+ import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
11
+ export interface TaintMatcherResult {
12
+ sources: TaintSource[];
13
+ sinks: TaintSink[];
14
+ sanitizers: TaintSanitizer[];
15
+ /** Method names annotated with @sanitizer (for ConstantPropagationPass). */
16
+ sanitizerMethods: string[];
17
+ /** Final merged config (with plugin extensions applied). */
18
+ config: TaintConfig;
19
+ }
20
+ export declare class TaintMatcherPass implements AnalysisPass<TaintMatcherResult> {
21
+ readonly name = "taint-matcher";
22
+ readonly category: "security";
23
+ run(ctx: PassContext): TaintMatcherResult;
24
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * TaintMatcherPass
3
+ *
4
+ * First pass in the analysis pipeline. Merges language-plugin built-in
5
+ * sources/sinks into the config, runs config-based taint matching, and
6
+ * extracts @sanitizer-annotated method names from type declarations.
7
+ */
8
+ import { analyzeTaint } from '../taint-matcher.js';
9
+ import { getLanguagePlugin } from '../../languages/index.js';
10
+ export class TaintMatcherPass {
11
+ name = 'taint-matcher';
12
+ category = 'security';
13
+ run(ctx) {
14
+ const { graph, language, config } = ctx;
15
+ const { calls, types } = graph.ir;
16
+ // Merge language-plugin built-in sources/sinks.
17
+ // Plugins (e.g. Bash) define patterns directly on the plugin rather than
18
+ // in YAML config files; splice them in here so they behave identically.
19
+ let mergedConfig = config;
20
+ const plugin = getLanguagePlugin(language);
21
+ if (plugin) {
22
+ const pluginSources = plugin.getBuiltinSources();
23
+ const pluginSinks = plugin.getBuiltinSinks();
24
+ if (pluginSources.length > 0 || pluginSinks.length > 0) {
25
+ mergedConfig = {
26
+ ...config,
27
+ sources: [
28
+ ...config.sources,
29
+ ...pluginSources.map(s => ({
30
+ method: s.method,
31
+ class: s.class,
32
+ annotation: s.annotation,
33
+ type: s.type,
34
+ severity: s.severity,
35
+ return_tainted: s.returnTainted ?? false,
36
+ })),
37
+ ],
38
+ sinks: [
39
+ ...config.sinks,
40
+ ...pluginSinks.map(s => ({
41
+ method: s.method,
42
+ class: s.class,
43
+ type: s.type,
44
+ cwe: s.cwe,
45
+ severity: s.severity,
46
+ arg_positions: s.argPositions,
47
+ })),
48
+ ],
49
+ };
50
+ }
51
+ }
52
+ const taint = analyzeTaint(calls, types, mergedConfig);
53
+ // Extract method names annotated with @sanitizer (Javadoc comments).
54
+ const sanitizerMethods = [];
55
+ for (const type of types) {
56
+ for (const method of type.methods) {
57
+ if (method.annotations.includes('sanitizer')) {
58
+ sanitizerMethods.push(method.name);
59
+ }
60
+ }
61
+ }
62
+ return {
63
+ sources: taint.sources,
64
+ sinks: taint.sinks,
65
+ sanitizers: taint.sanitizers ?? [],
66
+ sanitizerMethods,
67
+ config: mergedConfig,
68
+ };
69
+ }
70
+ }
71
+ //# sourceMappingURL=taint-matcher-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taint-matcher-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/taint-matcher-pass.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAY7D,MAAM,OAAO,gBAAgB;IAClB,IAAI,GAAG,eAAe,CAAC;IACvB,QAAQ,GAAG,UAAmB,CAAC;IAExC,GAAG,CAAC,GAAgB;QAClB,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;QACxC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;QAElC,gDAAgD;QAChD,yEAAyE;QACzE,wEAAwE;QACxE,IAAI,YAAY,GAAG,MAAM,CAAC;QAC1B,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAA4D,CAAC,CAAC;QAC/F,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,aAAa,GAAG,MAAM,CAAC,iBAAiB,EAAE,CAAC;YACjD,MAAM,WAAW,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC;YAC7C,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvD,YAAY,GAAG;oBACb,GAAG,MAAM;oBACT,OAAO,EAAE;wBACP,GAAG,MAAM,CAAC,OAAO;wBACjB,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;4BACzB,MAAM,EAAE,CAAC,CAAC,MAAM;4BAChB,KAAK,EAAE,CAAC,CAAC,KAAK;4BACd,UAAU,EAAE,CAAC,CAAC,UAAU;4BACxB,IAAI,EAAE,CAAC,CAAC,IAAkB;4BAC1B,QAAQ,EAAE,CAAC,CAAC,QAAQ;4BACpB,cAAc,EAAE,CAAC,CAAC,aAAa,IAAI,KAAK;yBACzC,CAAC,CAAC;qBACJ;oBACD,KAAK,EAAE;wBACL,GAAG,MAAM,CAAC,KAAK;wBACf,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;4BACvB,MAAM,EAAE,CAAC,CAAC,MAAM;4BAChB,KAAK,EAAE,CAAC,CAAC,KAAK;4BACd,IAAI,EAAE,CAAC,CAAC,IAAgB;4BACxB,GAAG,EAAE,CAAC,CAAC,GAAG;4BACV,QAAQ,EAAE,CAAC,CAAC,QAAQ;4BACpB,aAAa,EAAE,CAAC,CAAC,YAAY;yBAC9B,CAAC,CAAC;qBACJ;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;QAEvD,qEAAqE;QACrE,MAAM,gBAAgB,GAAa,EAAE,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClC,IAAI,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC7C,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACrC,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,EAAE;YAClC,gBAAgB;YAChB,MAAM,EAAE,YAAY;SACrB,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * TaintPropagationPass
3
+ *
4
+ * Propagates taint through the DFG to find verified source-to-sink flows,
5
+ * then supplements with three additional flow-detection strategies that the
6
+ * DFG-based analysis may miss:
7
+ * - Array element flows (tainted array[idx] → sink)
8
+ * - Collection/iterator flows (list.get(), queue.poll(), etc.)
9
+ * - Direct parameter-to-sink flows (interprocedural parameter used at sink)
10
+ *
11
+ * Depends on: sink-filter, constant-propagation
12
+ */
13
+ import type { TaintFlowInfo } from '../../types/index.js';
14
+ import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
15
+ export interface TaintPropagationPassResult {
16
+ flows: TaintFlowInfo[];
17
+ }
18
+ export declare class TaintPropagationPass implements AnalysisPass<TaintPropagationPassResult> {
19
+ readonly name = "taint-propagation";
20
+ readonly category: "security";
21
+ run(ctx: PassContext): TaintPropagationPassResult;
22
+ }