circle-ir 3.8.4 → 3.9.7

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 (157) 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/dependency-fan-out-pass.d.ts +19 -0
  57. package/dist/analysis/passes/dependency-fan-out-pass.js +35 -0
  58. package/dist/analysis/passes/dependency-fan-out-pass.js.map +1 -0
  59. package/dist/analysis/passes/interprocedural-pass.d.ts +29 -0
  60. package/dist/analysis/passes/interprocedural-pass.js +169 -0
  61. package/dist/analysis/passes/interprocedural-pass.js.map +1 -0
  62. package/dist/analysis/passes/language-sources-pass.d.ts +76 -0
  63. package/dist/analysis/passes/language-sources-pass.js +491 -0
  64. package/dist/analysis/passes/language-sources-pass.js.map +1 -0
  65. package/dist/analysis/passes/leaked-global-pass.d.ts +34 -0
  66. package/dist/analysis/passes/leaked-global-pass.js +108 -0
  67. package/dist/analysis/passes/leaked-global-pass.js.map +1 -0
  68. package/dist/analysis/passes/missing-await-pass.d.ts +29 -0
  69. package/dist/analysis/passes/missing-await-pass.js +90 -0
  70. package/dist/analysis/passes/missing-await-pass.js.map +1 -0
  71. package/dist/analysis/passes/missing-public-doc-pass.d.ts +35 -0
  72. package/dist/analysis/passes/missing-public-doc-pass.js +148 -0
  73. package/dist/analysis/passes/missing-public-doc-pass.js.map +1 -0
  74. package/dist/analysis/passes/n-plus-one-pass.d.ts +29 -0
  75. package/dist/analysis/passes/n-plus-one-pass.js +100 -0
  76. package/dist/analysis/passes/n-plus-one-pass.js.map +1 -0
  77. package/dist/analysis/passes/null-deref-pass.d.ts +32 -0
  78. package/dist/analysis/passes/null-deref-pass.js +130 -0
  79. package/dist/analysis/passes/null-deref-pass.js.map +1 -0
  80. package/dist/analysis/passes/orphan-module-pass.d.ts +21 -0
  81. package/dist/analysis/passes/orphan-module-pass.js +38 -0
  82. package/dist/analysis/passes/orphan-module-pass.js.map +1 -0
  83. package/dist/analysis/passes/resource-leak-pass.d.ts +43 -0
  84. package/dist/analysis/passes/resource-leak-pass.js +156 -0
  85. package/dist/analysis/passes/resource-leak-pass.js.map +1 -0
  86. package/dist/analysis/passes/sink-filter-pass.d.ts +39 -0
  87. package/dist/analysis/passes/sink-filter-pass.js +231 -0
  88. package/dist/analysis/passes/sink-filter-pass.js.map +1 -0
  89. package/dist/analysis/passes/stale-doc-ref-pass.d.ts +21 -0
  90. package/dist/analysis/passes/stale-doc-ref-pass.js +96 -0
  91. package/dist/analysis/passes/stale-doc-ref-pass.js.map +1 -0
  92. package/dist/analysis/passes/string-concat-loop-pass.d.ts +26 -0
  93. package/dist/analysis/passes/string-concat-loop-pass.js +87 -0
  94. package/dist/analysis/passes/string-concat-loop-pass.js.map +1 -0
  95. package/dist/analysis/passes/sync-io-async-pass.d.ts +28 -0
  96. package/dist/analysis/passes/sync-io-async-pass.js +80 -0
  97. package/dist/analysis/passes/sync-io-async-pass.js.map +1 -0
  98. package/dist/analysis/passes/taint-matcher-pass.d.ts +24 -0
  99. package/dist/analysis/passes/taint-matcher-pass.js +71 -0
  100. package/dist/analysis/passes/taint-matcher-pass.js.map +1 -0
  101. package/dist/analysis/passes/taint-propagation-pass.d.ts +22 -0
  102. package/dist/analysis/passes/taint-propagation-pass.js +266 -0
  103. package/dist/analysis/passes/taint-propagation-pass.js.map +1 -0
  104. package/dist/analysis/passes/todo-in-prod-pass.d.ts +28 -0
  105. package/dist/analysis/passes/todo-in-prod-pass.js +71 -0
  106. package/dist/analysis/passes/todo-in-prod-pass.js.map +1 -0
  107. package/dist/analysis/passes/unchecked-return-pass.d.ts +34 -0
  108. package/dist/analysis/passes/unchecked-return-pass.js +106 -0
  109. package/dist/analysis/passes/unchecked-return-pass.js.map +1 -0
  110. package/dist/analysis/passes/unused-variable-pass.d.ts +36 -0
  111. package/dist/analysis/passes/unused-variable-pass.js +150 -0
  112. package/dist/analysis/passes/unused-variable-pass.js.map +1 -0
  113. package/dist/analysis/passes/variable-shadowing-pass.d.ts +41 -0
  114. package/dist/analysis/passes/variable-shadowing-pass.js +211 -0
  115. package/dist/analysis/passes/variable-shadowing-pass.js.map +1 -0
  116. package/dist/analysis/path-finder.d.ts +3 -13
  117. package/dist/analysis/path-finder.js +48 -63
  118. package/dist/analysis/path-finder.js.map +1 -1
  119. package/dist/analysis/taint-matcher.js +8 -1
  120. package/dist/analysis/taint-matcher.js.map +1 -1
  121. package/dist/analysis/taint-propagation.d.ts +5 -1
  122. package/dist/analysis/taint-propagation.js +44 -41
  123. package/dist/analysis/taint-propagation.js.map +1 -1
  124. package/dist/analyzer.d.ts +42 -1
  125. package/dist/analyzer.js +234 -1476
  126. package/dist/analyzer.js.map +1 -1
  127. package/dist/browser/circle-ir.js +3413 -1271
  128. package/dist/core/circle-ir-core.cjs +360 -106
  129. package/dist/core/circle-ir-core.js +360 -106
  130. package/dist/core/extractors/imports.js +18 -0
  131. package/dist/core/extractors/imports.js.map +1 -1
  132. package/dist/graph/analysis-pass.d.ts +68 -0
  133. package/dist/graph/analysis-pass.js +51 -0
  134. package/dist/graph/analysis-pass.js.map +1 -0
  135. package/dist/graph/code-graph.d.ts +92 -0
  136. package/dist/graph/code-graph.js +262 -0
  137. package/dist/graph/code-graph.js.map +1 -0
  138. package/dist/graph/import-graph.d.ts +33 -0
  139. package/dist/graph/import-graph.js +170 -0
  140. package/dist/graph/import-graph.js.map +1 -0
  141. package/dist/graph/index.d.ts +4 -0
  142. package/dist/graph/index.js +5 -0
  143. package/dist/graph/index.js.map +1 -0
  144. package/dist/graph/project-graph.d.ts +43 -0
  145. package/dist/graph/project-graph.js +80 -0
  146. package/dist/graph/project-graph.js.map +1 -0
  147. package/dist/graph/scope-graph.d.ts +63 -0
  148. package/dist/graph/scope-graph.js +89 -0
  149. package/dist/graph/scope-graph.js.map +1 -0
  150. package/dist/index.d.ts +2 -2
  151. package/dist/index.js +1 -1
  152. package/dist/index.js.map +1 -1
  153. package/dist/resolution/cross-file.js +52 -19
  154. package/dist/resolution/cross-file.js.map +1 -1
  155. package/dist/types/index.d.ts +151 -0
  156. package/docs/SPEC.md +10 -6
  157. package/package.json +1 -1
@@ -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
+ }
@@ -0,0 +1,266 @@
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 { propagateTaint } from '../taint-propagation.js';
14
+ import { isFalsePositive, isCorrelatedPredicateFP } from '../constant-propagation.js';
15
+ export class TaintPropagationPass {
16
+ name = 'taint-propagation';
17
+ category = 'security';
18
+ run(ctx) {
19
+ const { graph } = ctx;
20
+ const { calls, types } = graph.ir;
21
+ const constProp = ctx.getResult('constant-propagation');
22
+ const sinkFilter = ctx.getResult('sink-filter');
23
+ const { sources, sinks, sanitizers } = sinkFilter;
24
+ if (sources.length === 0 || sinks.length === 0) {
25
+ return { flows: [] };
26
+ }
27
+ // DFG-based taint propagation
28
+ const propagationResult = propagateTaint(graph, sources, sinks, sanitizers);
29
+ // Filter flows: eliminate dead-code paths and constant-propagation FPs
30
+ const verifiedFlows = propagationResult.flows.filter(flow => {
31
+ if (constProp.unreachableLines.has(flow.sink.line))
32
+ return false;
33
+ for (const step of flow.path) {
34
+ const fpCheck = isFalsePositive(constProp, step.line, step.variable);
35
+ if (fpCheck.isFalsePositive)
36
+ return false;
37
+ }
38
+ if (isCorrelatedPredicateFP(constProp, flow))
39
+ return false;
40
+ return true;
41
+ });
42
+ // Convert to TaintFlowInfo format
43
+ const flows = verifiedFlows.map(flow => ({
44
+ source_line: flow.source.line,
45
+ sink_line: flow.sink.line,
46
+ source_type: flow.source.type,
47
+ sink_type: flow.sink.type,
48
+ path: flow.path.map(step => ({
49
+ variable: step.variable,
50
+ line: step.line,
51
+ type: step.type,
52
+ })),
53
+ confidence: flow.confidence,
54
+ sanitized: flow.sanitized,
55
+ }));
56
+ // Supplement: array element flows
57
+ const arrayFlows = detectArrayElementFlows(calls, sources, sinks, constProp.taintedArrayElements, constProp.unreachableLines) ?? [];
58
+ for (const f of arrayFlows) {
59
+ if (!flows.some(x => x.source_line === f.source_line && x.sink_line === f.sink_line)) {
60
+ flows.push(f);
61
+ }
62
+ }
63
+ // Supplement: collection/iterator flows — with FP filtering
64
+ const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp.tainted, constProp.unreachableLines) ?? [];
65
+ for (const f of collectionFlows) {
66
+ if (flows.some(x => x.source_line === f.source_line && x.sink_line === f.sink_line))
67
+ continue;
68
+ const flowForCheck = {
69
+ source: { line: f.source_line, type: f.source_type },
70
+ sink: { line: f.sink_line, type: f.sink_type },
71
+ path: f.path.map(p => ({ variable: p.variable, line: p.line })),
72
+ };
73
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
+ if (isCorrelatedPredicateFP(constProp, flowForCheck))
75
+ continue;
76
+ let isFP = false;
77
+ for (const step of f.path) {
78
+ if (isFalsePositive(constProp, step.line, step.variable).isFalsePositive) {
79
+ isFP = true;
80
+ break;
81
+ }
82
+ }
83
+ if (isFP)
84
+ continue;
85
+ flows.push(f);
86
+ }
87
+ // Supplement: direct parameter-to-sink flows
88
+ const paramFlows = detectParameterSinkFlows(types, calls, sources, sinks, constProp.unreachableLines) ?? [];
89
+ for (const f of paramFlows) {
90
+ if (!flows.some(x => x.source_line === f.source_line && x.sink_line === f.sink_line)) {
91
+ flows.push(f);
92
+ }
93
+ }
94
+ return { flows };
95
+ }
96
+ }
97
+ // ---------------------------------------------------------------------------
98
+ // Helpers (moved verbatim from analyzer.ts)
99
+ // ---------------------------------------------------------------------------
100
+ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLines) {
101
+ const flows = [];
102
+ const callsByLine = new Map();
103
+ for (const call of calls) {
104
+ const existing = callsByLine.get(call.location.line) ?? [];
105
+ existing.push(call);
106
+ callsByLine.set(call.location.line, existing);
107
+ }
108
+ for (const sink of sinks) {
109
+ if (unreachableLines.has(sink.line))
110
+ continue;
111
+ const callsAtSink = callsByLine.get(sink.line) ?? [];
112
+ for (const call of callsAtSink) {
113
+ for (const arg of call.arguments) {
114
+ if (arg.variable) {
115
+ const varName = arg.variable;
116
+ const scopedName = call.in_method ? `${call.in_method}:${varName}` : varName;
117
+ if (taintedVars.has(varName) || taintedVars.has(scopedName)) {
118
+ const source = sources[0];
119
+ if (source) {
120
+ flows.push({
121
+ source_line: source.line, sink_line: sink.line,
122
+ source_type: source.type, sink_type: sink.type,
123
+ path: [
124
+ { variable: varName, line: source.line, type: 'source' },
125
+ { variable: varName, line: sink.line, type: 'sink' },
126
+ ],
127
+ confidence: 0.8, sanitized: false,
128
+ });
129
+ }
130
+ }
131
+ }
132
+ if (arg.expression) {
133
+ const expr = arg.expression;
134
+ const collectionMethods = ['getLast', 'getFirst', 'get', 'next', 'poll', 'peek', 'toArray'];
135
+ for (const method of collectionMethods) {
136
+ const match = expr.match(new RegExp(`(\\w+)\\.${method}\\(`));
137
+ if (match) {
138
+ const collectionVar = match[1];
139
+ const scopedCollection = call.in_method ? `${call.in_method}:${collectionVar}` : collectionVar;
140
+ if (taintedVars.has(collectionVar) || taintedVars.has(scopedCollection)) {
141
+ const source = sources[0];
142
+ if (source) {
143
+ flows.push({
144
+ source_line: source.line, sink_line: sink.line,
145
+ source_type: source.type, sink_type: sink.type,
146
+ path: [
147
+ { variable: collectionVar, line: source.line, type: 'source' },
148
+ { variable: collectionVar, line: sink.line, type: 'sink' },
149
+ ],
150
+ confidence: 0.75, sanitized: false,
151
+ });
152
+ }
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+ return flows;
161
+ }
162
+ function detectArrayElementFlows(calls, sources, sinks, taintedArrayElements, unreachableLines) {
163
+ const flows = [];
164
+ const callsByLine = new Map();
165
+ for (const call of calls) {
166
+ const existing = callsByLine.get(call.location.line) ?? [];
167
+ existing.push(call);
168
+ callsByLine.set(call.location.line, existing);
169
+ }
170
+ for (const sink of sinks) {
171
+ if (unreachableLines.has(sink.line))
172
+ continue;
173
+ const callsAtSink = callsByLine.get(sink.line) ?? [];
174
+ for (const call of callsAtSink) {
175
+ for (const arg of call.arguments) {
176
+ const arrayAccessMatch = arg.expression?.match(/^(\w+)\[(\d+|[^[\]]+)\]$/);
177
+ if (arrayAccessMatch) {
178
+ const arrayName = arrayAccessMatch[1];
179
+ const indexStr = arrayAccessMatch[2];
180
+ const taintedIndices = taintedArrayElements.get(arrayName);
181
+ if (taintedIndices) {
182
+ const isTainted = taintedIndices.has(indexStr) || taintedIndices.has('*');
183
+ if (isTainted) {
184
+ const source = sources[0];
185
+ if (source) {
186
+ flows.push({
187
+ source_line: source.line, sink_line: sink.line,
188
+ source_type: source.type, sink_type: sink.type,
189
+ path: [
190
+ { variable: arrayName, line: source.line, type: 'source' },
191
+ { variable: `${arrayName}[${indexStr}]`, line: sink.line, type: 'sink' },
192
+ ],
193
+ confidence: 0.85, sanitized: false,
194
+ });
195
+ }
196
+ }
197
+ }
198
+ }
199
+ }
200
+ }
201
+ }
202
+ return flows;
203
+ }
204
+ function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines) {
205
+ const flows = [];
206
+ const paramSourcesByMethod = new Map();
207
+ for (const source of sources) {
208
+ if (source.type === 'interprocedural_param') {
209
+ const match = source.location.match(/(\S+)\s+(\S+)\s+in\s+(\S+)/);
210
+ if (match) {
211
+ const paramName = match[2];
212
+ const methodName = match[3];
213
+ let methodParams = paramSourcesByMethod.get(methodName);
214
+ if (!methodParams) {
215
+ methodParams = new Map();
216
+ paramSourcesByMethod.set(methodName, methodParams);
217
+ }
218
+ methodParams.set(paramName, source);
219
+ }
220
+ }
221
+ }
222
+ if (paramSourcesByMethod.size === 0)
223
+ return flows;
224
+ const callsByLine = new Map();
225
+ for (const call of calls) {
226
+ const existing = callsByLine.get(call.location.line) ?? [];
227
+ existing.push(call);
228
+ callsByLine.set(call.location.line, existing);
229
+ }
230
+ for (const sink of sinks) {
231
+ if (unreachableLines.has(sink.line))
232
+ continue;
233
+ const callsAtSink = callsByLine.get(sink.line) ?? [];
234
+ for (const call of callsAtSink) {
235
+ const methodName = call.in_method;
236
+ if (!methodName)
237
+ continue;
238
+ const methodParamSources = paramSourcesByMethod.get(methodName);
239
+ if (!methodParamSources)
240
+ continue;
241
+ for (const arg of call.arguments) {
242
+ if (arg.variable) {
243
+ const paramSource = methodParamSources.get(arg.variable);
244
+ if (paramSource) {
245
+ const exists = flows.some(f => f.source_line === paramSource.line && f.sink_line === sink.line);
246
+ if (!exists) {
247
+ flows.push({
248
+ source_line: paramSource.line, sink_line: sink.line,
249
+ source_type: paramSource.type, sink_type: sink.type,
250
+ path: [
251
+ { variable: arg.variable, line: paramSource.line, type: 'source' },
252
+ { variable: arg.variable, line: sink.line, type: 'sink' },
253
+ ],
254
+ confidence: 0.75, sanitized: false,
255
+ });
256
+ }
257
+ }
258
+ }
259
+ }
260
+ }
261
+ }
262
+ // types parameter is accepted for API compatibility; not used in current implementation
263
+ void types;
264
+ return flows;
265
+ }
266
+ //# sourceMappingURL=taint-propagation-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taint-propagation-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/taint-propagation-pass.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Pass #36: todo-in-prod (category: maintainability)
3
+ *
4
+ * Flags TODO, FIXME, HACK, and XXX comments in production code. These markers
5
+ * signal deferred work or known defects that were never resolved. In production
6
+ * code they represent acknowledged technical debt.
7
+ *
8
+ * Test files are excluded entirely: TODO comments in test helpers or test
9
+ * setup code are expected and noise-free.
10
+ *
11
+ * Detection: line-by-line regex scan on the raw source text. A marker must
12
+ * appear in a comment context (after `//`, `#`, `--`, or inside `/* ... *\/`).
13
+ * Markers inside string literals are not flagged.
14
+ */
15
+ import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
16
+ export interface TodoInProdPassResult {
17
+ /** Lines containing TODO/FIXME/HACK/XXX markers in production code. */
18
+ markerLines: Array<{
19
+ line: number;
20
+ marker: string;
21
+ text: string;
22
+ }>;
23
+ }
24
+ export declare class TodoInProdPass implements AnalysisPass<TodoInProdPassResult> {
25
+ readonly name = "todo-in-prod";
26
+ readonly category: "maintainability";
27
+ run(ctx: PassContext): TodoInProdPassResult;
28
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Pass #36: todo-in-prod (category: maintainability)
3
+ *
4
+ * Flags TODO, FIXME, HACK, and XXX comments in production code. These markers
5
+ * signal deferred work or known defects that were never resolved. In production
6
+ * code they represent acknowledged technical debt.
7
+ *
8
+ * Test files are excluded entirely: TODO comments in test helpers or test
9
+ * setup code are expected and noise-free.
10
+ *
11
+ * Detection: line-by-line regex scan on the raw source text. A marker must
12
+ * appear in a comment context (after `//`, `#`, `--`, or inside `/* ... *\/`).
13
+ * Markers inside string literals are not flagged.
14
+ */
15
+ /** Files matching these path patterns are treated as test/spec files. */
16
+ const TEST_PATH_RE = /[/._](test|tests|spec|specs|__tests?__|__mocks?__)[/._]/i;
17
+ /**
18
+ * Matches comment markers on a line.
19
+ *
20
+ * Groups:
21
+ * 1 — comment prefix (`//`, `#`, `--`, `*`)
22
+ * 2 — marker keyword (TODO, FIXME, HACK, XXX)
23
+ */
24
+ const MARKER_RE = /(?:\/\/|#|--|^\s*\*)\s*(TODO|FIXME|HACK|XXX)\b/i;
25
+ /**
26
+ * Severity mapping by marker keyword.
27
+ * - FIXME / HACK → medium (known defect / deliberate workaround)
28
+ * - TODO / XXX → low (deferred work / note)
29
+ */
30
+ function markerSeverity(marker) {
31
+ const upper = marker.toUpperCase();
32
+ return upper === 'FIXME' || upper === 'HACK' ? 'medium' : 'low';
33
+ }
34
+ export class TodoInProdPass {
35
+ name = 'todo-in-prod';
36
+ category = 'maintainability';
37
+ run(ctx) {
38
+ const { graph, code } = ctx;
39
+ const file = graph.ir.meta.file;
40
+ // Exclude test/spec files.
41
+ if (TEST_PATH_RE.test(file)) {
42
+ return { markerLines: [] };
43
+ }
44
+ const lines = code.split('\n');
45
+ const markerLines = [];
46
+ for (let i = 0; i < lines.length; i++) {
47
+ const lineText = lines[i];
48
+ const match = MARKER_RE.exec(lineText);
49
+ if (!match)
50
+ continue;
51
+ const marker = match[1].toUpperCase();
52
+ const lineNum = i + 1; // 1-indexed
53
+ markerLines.push({ line: lineNum, marker, text: lineText.trim() });
54
+ ctx.addFinding({
55
+ id: `todo-in-prod-${file}-${lineNum}`,
56
+ pass: this.name,
57
+ category: this.category,
58
+ rule_id: this.name,
59
+ severity: markerSeverity(marker),
60
+ level: 'note',
61
+ message: `${marker} in production code at line ${lineNum}: ${lineText.trim()}`,
62
+ file,
63
+ line: lineNum,
64
+ snippet: lineText.trim(),
65
+ evidence: { marker },
66
+ });
67
+ }
68
+ return { markerLines };
69
+ }
70
+ }
71
+ //# sourceMappingURL=todo-in-prod-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"todo-in-prod-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/todo-in-prod-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,yEAAyE;AACzE,MAAM,YAAY,GAAG,0DAA0D,CAAC;AAEhF;;;;;;GAMG;AACH,MAAM,SAAS,GAAG,iDAAiD,CAAC;AAEpE;;;;GAIG;AACH,SAAS,cAAc,CAAC,MAAc;IACpC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACnC,OAAO,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;AAClE,CAAC;AAOD,MAAM,OAAO,cAAc;IAChB,IAAI,GAAG,cAAc,CAAC;IACtB,QAAQ,GAAG,iBAA0B,CAAC;IAE/C,GAAG,CAAC,GAAgB;QAClB,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC;QAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;QAEhC,2BAA2B;QAC3B,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;QAC7B,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,WAAW,GAA0D,EAAE,CAAC;QAE9E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvC,IAAI,CAAC,KAAK;gBAAE,SAAS;YAErB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YACtC,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY;YAEnC,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAEnE,GAAG,CAAC,UAAU,CAAC;gBACb,EAAE,EAAE,gBAAgB,IAAI,IAAI,OAAO,EAAE;gBACrC,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,OAAO,EAAE,IAAI,CAAC,IAAI;gBAClB,QAAQ,EAAE,cAAc,CAAC,MAAM,CAAC;gBAChC,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,GAAG,MAAM,+BAA+B,OAAO,KAAK,QAAQ,CAAC,IAAI,EAAE,EAAE;gBAC9E,IAAI;gBACJ,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,QAAQ,CAAC,IAAI,EAAE;gBACxB,QAAQ,EAAE,EAAE,MAAM,EAAE;aACrB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,EAAE,WAAW,EAAE,CAAC;IACzB,CAAC;CACF"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Pass #28: unchecked-return (CWE-252, category: reliability)
3
+ *
4
+ * Detects calls to methods that signal success/failure via their return value
5
+ * when that return value is silently discarded. Missing the check means silent
6
+ * failure propagation that can corrupt application state.
7
+ *
8
+ * Detection strategy:
9
+ * Two-tier curated list:
10
+ * HIGH: Always flag — the method's name is unambiguous (e.g. File.delete,
11
+ * Matcher.find, Lock.tryLock) and discarding is almost always a bug.
12
+ * MEDIUM: Flag only when the receiver name suggests a File object —
13
+ * guards against common false positives on generic names.
14
+ *
15
+ * For each candidate call:
16
+ * 1. If there is a DFG def at the call's line, the result was captured.
17
+ * 2. If the source line matches a conditional/assertion pattern, the
18
+ * return value is already being used.
19
+ * If neither applies → emit finding.
20
+ */
21
+ import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
22
+ export interface UncheckedReturnResult {
23
+ /** Calls where the return value is silently discarded. */
24
+ uncheckedCalls: Array<{
25
+ line: number;
26
+ method: string;
27
+ receiver: string | null;
28
+ }>;
29
+ }
30
+ export declare class UncheckedReturnPass implements AnalysisPass<UncheckedReturnResult> {
31
+ readonly name = "unchecked-return";
32
+ readonly category: "reliability";
33
+ run(ctx: PassContext): UncheckedReturnResult;
34
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Pass #28: unchecked-return (CWE-252, category: reliability)
3
+ *
4
+ * Detects calls to methods that signal success/failure via their return value
5
+ * when that return value is silently discarded. Missing the check means silent
6
+ * failure propagation that can corrupt application state.
7
+ *
8
+ * Detection strategy:
9
+ * Two-tier curated list:
10
+ * HIGH: Always flag — the method's name is unambiguous (e.g. File.delete,
11
+ * Matcher.find, Lock.tryLock) and discarding is almost always a bug.
12
+ * MEDIUM: Flag only when the receiver name suggests a File object —
13
+ * guards against common false positives on generic names.
14
+ *
15
+ * For each candidate call:
16
+ * 1. If there is a DFG def at the call's line, the result was captured.
17
+ * 2. If the source line matches a conditional/assertion pattern, the
18
+ * return value is already being used.
19
+ * If neither applies → emit finding.
20
+ */
21
+ /**
22
+ * Methods where discarding the return value is almost always a bug.
23
+ * Flagged regardless of receiver.
24
+ */
25
+ const MUST_CHECK_HIGH = new Set([
26
+ // java.io.File — boolean status
27
+ 'createNewFile', 'mkdir', 'mkdirs',
28
+ // java.util.concurrent
29
+ 'tryLock', 'tryAcquire', 'compareAndSet', 'compareAndExchange',
30
+ ]);
31
+ /**
32
+ * Java-only methods where discarding the return value is a bug.
33
+ * `delete` (Set.delete / Map.delete) and `find` (Array.find) have common
34
+ * non-Java semantics where ignoring the return value is perfectly normal.
35
+ */
36
+ const MUST_CHECK_HIGH_JAVA_ONLY = new Set([
37
+ 'delete', // java.io.File.delete()
38
+ 'find', // java.util.regex.Matcher.find()
39
+ ]);
40
+ /**
41
+ * Methods flagged only when the receiver name suggests a File instance.
42
+ */
43
+ const MUST_CHECK_MEDIUM = new Set([
44
+ 'renameTo', 'setExecutable', 'setReadable', 'setWritable', 'setLastModified',
45
+ ]);
46
+ /** Receiver names that strongly suggest java.io.File. */
47
+ const FILE_RECEIVER_RE = /^(file|f|src|dest|target|source|dir|directory|path|tmp|temp)\b/i;
48
+ /**
49
+ * Line patterns that indicate the return value IS being used in a conditional,
50
+ * assertion, or ternary — do not flag these.
51
+ */
52
+ const CHECKED_LINE_RE = /\bif\s*\(|\bwhile\s*\(|\bassert\b|\?[^:]|\|\||\&\&/;
53
+ export class UncheckedReturnPass {
54
+ name = 'unchecked-return';
55
+ category = 'reliability';
56
+ run(ctx) {
57
+ const { graph, code, language } = ctx;
58
+ const file = graph.ir.meta.file;
59
+ const codeLines = code.split('\n');
60
+ // Set of lines that have a DFG definition (return value captured in variable)
61
+ const linesWithDefs = new Set(graph.ir.dfg.defs.map(d => d.line));
62
+ const uncheckedCalls = [];
63
+ for (const call of graph.ir.calls) {
64
+ const { method_name: name, receiver, location: { line } } = call;
65
+ let shouldCheck = false;
66
+ if (MUST_CHECK_HIGH.has(name)) {
67
+ shouldCheck = true;
68
+ }
69
+ else if (language === 'java' && MUST_CHECK_HIGH_JAVA_ONLY.has(name)) {
70
+ shouldCheck = true;
71
+ }
72
+ else if (MUST_CHECK_MEDIUM.has(name)) {
73
+ shouldCheck = receiver != null && FILE_RECEIVER_RE.test(receiver);
74
+ }
75
+ if (!shouldCheck)
76
+ continue;
77
+ // Result captured → not an unchecked return
78
+ if (linesWithDefs.has(line))
79
+ continue;
80
+ // Conditional / assertion context → return value being used
81
+ const lineText = codeLines[line - 1] ?? '';
82
+ if (CHECKED_LINE_RE.test(lineText))
83
+ continue;
84
+ uncheckedCalls.push({ line, method: name, receiver: receiver ?? null });
85
+ const qualifier = receiver ? `${receiver}.` : '';
86
+ ctx.addFinding({
87
+ id: `unchecked-return-${file}-${line}`,
88
+ pass: this.name,
89
+ category: this.category,
90
+ rule_id: this.name,
91
+ cwe: 'CWE-252',
92
+ severity: 'medium',
93
+ level: 'warning',
94
+ message: `Return value of \`${qualifier}${name}()\` is silently discarded — ` +
95
+ `failures will go undetected`,
96
+ file,
97
+ line,
98
+ snippet: lineText.trim(),
99
+ fix: `Check the return value: \`if (!${qualifier}${name}()) { throw new IOException(...); }\``,
100
+ evidence: { receiver: receiver ?? undefined },
101
+ });
102
+ }
103
+ return { uncheckedCalls };
104
+ }
105
+ }
106
+ //# sourceMappingURL=unchecked-return-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unchecked-return-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/unchecked-return-pass.ts"],"names":[],"mappings":"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"}