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.
- package/README.md +82 -5
- package/dist/analysis/dfg-verifier.d.ts +3 -14
- package/dist/analysis/dfg-verifier.js +43 -74
- package/dist/analysis/dfg-verifier.js.map +1 -1
- package/dist/analysis/interprocedural.d.ts +5 -1
- package/dist/analysis/interprocedural.js +62 -60
- package/dist/analysis/interprocedural.js.map +1 -1
- package/dist/analysis/metrics/index.d.ts +2 -0
- package/dist/analysis/metrics/index.js +2 -0
- package/dist/analysis/metrics/index.js.map +1 -0
- package/dist/analysis/metrics/metric-pass.d.ts +27 -0
- package/dist/analysis/metrics/metric-pass.js +2 -0
- package/dist/analysis/metrics/metric-pass.js.map +1 -0
- package/dist/analysis/metrics/metric-runner.d.ts +21 -0
- package/dist/analysis/metrics/metric-runner.js +47 -0
- package/dist/analysis/metrics/metric-runner.js.map +1 -0
- package/dist/analysis/metrics/passes/cohesion-metrics-pass.d.ts +21 -0
- package/dist/analysis/metrics/passes/cohesion-metrics-pass.js +100 -0
- package/dist/analysis/metrics/passes/cohesion-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/complexity-metrics-pass.d.ts +15 -0
- package/dist/analysis/metrics/passes/complexity-metrics-pass.js +76 -0
- package/dist/analysis/metrics/passes/complexity-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/composite-metrics-pass.d.ts +17 -0
- package/dist/analysis/metrics/passes/composite-metrics-pass.js +77 -0
- package/dist/analysis/metrics/passes/composite-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/coupling-metrics-pass.d.ts +19 -0
- package/dist/analysis/metrics/passes/coupling-metrics-pass.js +94 -0
- package/dist/analysis/metrics/passes/coupling-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/data-flow-metrics-pass.d.ts +14 -0
- package/dist/analysis/metrics/passes/data-flow-metrics-pass.js +25 -0
- package/dist/analysis/metrics/passes/data-flow-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/documentation-metrics-pass.d.ts +15 -0
- package/dist/analysis/metrics/passes/documentation-metrics-pass.js +64 -0
- package/dist/analysis/metrics/passes/documentation-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/halstead-metrics-pass.d.ts +16 -0
- package/dist/analysis/metrics/passes/halstead-metrics-pass.js +95 -0
- package/dist/analysis/metrics/passes/halstead-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/inheritance-metrics-pass.d.ts +18 -0
- package/dist/analysis/metrics/passes/inheritance-metrics-pass.js +73 -0
- package/dist/analysis/metrics/passes/inheritance-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/size-metrics-pass.d.ts +11 -0
- package/dist/analysis/metrics/passes/size-metrics-pass.js +64 -0
- package/dist/analysis/metrics/passes/size-metrics-pass.js.map +1 -0
- package/dist/analysis/passes/circular-dependency-pass.d.ts +18 -0
- package/dist/analysis/passes/circular-dependency-pass.js +39 -0
- package/dist/analysis/passes/circular-dependency-pass.js.map +1 -0
- package/dist/analysis/passes/constant-propagation-pass.d.ts +22 -0
- package/dist/analysis/passes/constant-propagation-pass.js +44 -0
- package/dist/analysis/passes/constant-propagation-pass.js.map +1 -0
- package/dist/analysis/passes/cross-file-pass.d.ts +27 -0
- package/dist/analysis/passes/cross-file-pass.js +102 -0
- package/dist/analysis/passes/cross-file-pass.js.map +1 -0
- package/dist/analysis/passes/dead-code-pass.d.ts +25 -0
- package/dist/analysis/passes/dead-code-pass.js +117 -0
- package/dist/analysis/passes/dead-code-pass.js.map +1 -0
- package/dist/analysis/passes/deep-inheritance-pass.d.ts +30 -0
- package/dist/analysis/passes/deep-inheritance-pass.js +82 -0
- package/dist/analysis/passes/deep-inheritance-pass.js.map +1 -0
- package/dist/analysis/passes/dependency-fan-out-pass.d.ts +19 -0
- package/dist/analysis/passes/dependency-fan-out-pass.js +35 -0
- package/dist/analysis/passes/dependency-fan-out-pass.js.map +1 -0
- package/dist/analysis/passes/infinite-loop-pass.d.ts +31 -0
- package/dist/analysis/passes/infinite-loop-pass.js +126 -0
- package/dist/analysis/passes/infinite-loop-pass.js.map +1 -0
- package/dist/analysis/passes/interprocedural-pass.d.ts +29 -0
- package/dist/analysis/passes/interprocedural-pass.js +169 -0
- package/dist/analysis/passes/interprocedural-pass.js.map +1 -0
- package/dist/analysis/passes/language-sources-pass.d.ts +76 -0
- package/dist/analysis/passes/language-sources-pass.js +491 -0
- package/dist/analysis/passes/language-sources-pass.js.map +1 -0
- package/dist/analysis/passes/leaked-global-pass.d.ts +34 -0
- package/dist/analysis/passes/leaked-global-pass.js +108 -0
- package/dist/analysis/passes/leaked-global-pass.js.map +1 -0
- package/dist/analysis/passes/missing-await-pass.d.ts +29 -0
- package/dist/analysis/passes/missing-await-pass.js +90 -0
- package/dist/analysis/passes/missing-await-pass.js.map +1 -0
- package/dist/analysis/passes/missing-public-doc-pass.d.ts +35 -0
- package/dist/analysis/passes/missing-public-doc-pass.js +148 -0
- package/dist/analysis/passes/missing-public-doc-pass.js.map +1 -0
- package/dist/analysis/passes/n-plus-one-pass.d.ts +29 -0
- package/dist/analysis/passes/n-plus-one-pass.js +100 -0
- package/dist/analysis/passes/n-plus-one-pass.js.map +1 -0
- package/dist/analysis/passes/null-deref-pass.d.ts +32 -0
- package/dist/analysis/passes/null-deref-pass.js +130 -0
- package/dist/analysis/passes/null-deref-pass.js.map +1 -0
- package/dist/analysis/passes/orphan-module-pass.d.ts +21 -0
- package/dist/analysis/passes/orphan-module-pass.js +38 -0
- package/dist/analysis/passes/orphan-module-pass.js.map +1 -0
- package/dist/analysis/passes/react-inline-jsx-pass.d.ts +36 -0
- package/dist/analysis/passes/react-inline-jsx-pass.js +140 -0
- package/dist/analysis/passes/react-inline-jsx-pass.js.map +1 -0
- package/dist/analysis/passes/redundant-loop-pass.d.ts +30 -0
- package/dist/analysis/passes/redundant-loop-pass.js +146 -0
- package/dist/analysis/passes/redundant-loop-pass.js.map +1 -0
- package/dist/analysis/passes/resource-leak-pass.d.ts +43 -0
- package/dist/analysis/passes/resource-leak-pass.js +156 -0
- package/dist/analysis/passes/resource-leak-pass.js.map +1 -0
- package/dist/analysis/passes/serial-await-pass.d.ts +36 -0
- package/dist/analysis/passes/serial-await-pass.js +132 -0
- package/dist/analysis/passes/serial-await-pass.js.map +1 -0
- package/dist/analysis/passes/sink-filter-pass.d.ts +39 -0
- package/dist/analysis/passes/sink-filter-pass.js +231 -0
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -0
- package/dist/analysis/passes/stale-doc-ref-pass.d.ts +21 -0
- package/dist/analysis/passes/stale-doc-ref-pass.js +96 -0
- package/dist/analysis/passes/stale-doc-ref-pass.js.map +1 -0
- package/dist/analysis/passes/string-concat-loop-pass.d.ts +26 -0
- package/dist/analysis/passes/string-concat-loop-pass.js +87 -0
- package/dist/analysis/passes/string-concat-loop-pass.js.map +1 -0
- package/dist/analysis/passes/sync-io-async-pass.d.ts +28 -0
- package/dist/analysis/passes/sync-io-async-pass.js +80 -0
- package/dist/analysis/passes/sync-io-async-pass.js.map +1 -0
- package/dist/analysis/passes/taint-matcher-pass.d.ts +24 -0
- package/dist/analysis/passes/taint-matcher-pass.js +71 -0
- package/dist/analysis/passes/taint-matcher-pass.js.map +1 -0
- package/dist/analysis/passes/taint-propagation-pass.d.ts +22 -0
- package/dist/analysis/passes/taint-propagation-pass.js +266 -0
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -0
- package/dist/analysis/passes/todo-in-prod-pass.d.ts +28 -0
- package/dist/analysis/passes/todo-in-prod-pass.js +71 -0
- package/dist/analysis/passes/todo-in-prod-pass.js.map +1 -0
- package/dist/analysis/passes/unbounded-collection-pass.d.ts +32 -0
- package/dist/analysis/passes/unbounded-collection-pass.js +128 -0
- package/dist/analysis/passes/unbounded-collection-pass.js.map +1 -0
- package/dist/analysis/passes/unchecked-return-pass.d.ts +34 -0
- package/dist/analysis/passes/unchecked-return-pass.js +106 -0
- package/dist/analysis/passes/unchecked-return-pass.js.map +1 -0
- package/dist/analysis/passes/unused-variable-pass.d.ts +36 -0
- package/dist/analysis/passes/unused-variable-pass.js +150 -0
- package/dist/analysis/passes/unused-variable-pass.js.map +1 -0
- package/dist/analysis/passes/variable-shadowing-pass.d.ts +41 -0
- package/dist/analysis/passes/variable-shadowing-pass.js +211 -0
- package/dist/analysis/passes/variable-shadowing-pass.js.map +1 -0
- package/dist/analysis/path-finder.d.ts +3 -13
- package/dist/analysis/path-finder.js +48 -63
- package/dist/analysis/path-finder.js.map +1 -1
- package/dist/analysis/taint-matcher.js +8 -1
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analysis/taint-propagation.d.ts +5 -1
- package/dist/analysis/taint-propagation.js +44 -41
- package/dist/analysis/taint-propagation.js.map +1 -1
- package/dist/analyzer.d.ts +48 -1
- package/dist/analyzer.js +252 -1476
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +3952 -1270
- package/dist/core/circle-ir-core.cjs +360 -106
- package/dist/core/circle-ir-core.js +360 -106
- package/dist/core/extractors/imports.js +18 -0
- package/dist/core/extractors/imports.js.map +1 -1
- package/dist/graph/analysis-pass.d.ts +68 -0
- package/dist/graph/analysis-pass.js +51 -0
- package/dist/graph/analysis-pass.js.map +1 -0
- package/dist/graph/code-graph.d.ts +92 -0
- package/dist/graph/code-graph.js +262 -0
- package/dist/graph/code-graph.js.map +1 -0
- package/dist/graph/dominator-graph.d.ts +53 -0
- package/dist/graph/dominator-graph.js +256 -0
- package/dist/graph/dominator-graph.js.map +1 -0
- package/dist/graph/import-graph.d.ts +33 -0
- package/dist/graph/import-graph.js +170 -0
- package/dist/graph/import-graph.js.map +1 -0
- package/dist/graph/index.d.ts +5 -0
- package/dist/graph/index.js +6 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/project-graph.d.ts +43 -0
- package/dist/graph/project-graph.js +80 -0
- package/dist/graph/project-graph.js.map +1 -0
- package/dist/graph/scope-graph.d.ts +63 -0
- package/dist/graph/scope-graph.js +89 -0
- package/dist/graph/scope-graph.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/resolution/cross-file.js +52 -19
- package/dist/resolution/cross-file.js.map +1 -1
- package/dist/types/index.d.ts +151 -0
- package/docs/SPEC.md +10 -6
- package/package.json +1 -1
|
@@ -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":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAMtF,MAAM,OAAO,oBAAoB;IACtB,IAAI,GAAG,mBAAmB,CAAC;IAC3B,QAAQ,GAAG,UAAmB,CAAC;IAExC,GAAG,CAAC,GAAgB;QAClB,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;QACtB,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;QAElC,MAAM,SAAS,GAAK,GAAG,CAAC,SAAS,CAA2B,sBAAsB,CAAC,CAAC;QACpF,MAAM,UAAU,GAAI,GAAG,CAAC,SAAS,CAAmB,aAAa,CAAC,CAAC;QACnE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,UAAU,CAAC;QAElD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/C,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QACvB,CAAC;QAED,8BAA8B;QAC9B,MAAM,iBAAiB,GAAG,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAE5E,uEAAuE;QACvE,MAAM,aAAa,GAAG,iBAAiB,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;YAC1D,IAAI,SAAS,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,KAAK,CAAC;YAEjE,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC7B,MAAM,OAAO,GAAG,eAAe,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACrE,IAAI,OAAO,CAAC,eAAe;oBAAE,OAAO,KAAK,CAAC;YAC5C,CAAC;YAED,IAAI,uBAAuB,CAAC,SAAS,EAAE,IAAI,CAAC;gBAAE,OAAO,KAAK,CAAC;YAE3D,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,kCAAkC;QAClC,MAAM,KAAK,GAAoB,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACxD,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;YAC7B,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI;YACzB,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;YAC7B,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;aAChB,CAAC,CAAC;YACH,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC,CAAC,CAAC;QAEJ,kCAAkC;QAClC,MAAM,UAAU,GAAG,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,oBAAoB,EAAE,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;QACpI,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;gBACrF,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChB,CAAC;QACH,CAAC;QAED,4DAA4D;QAC5D,MAAM,eAAe,GAAG,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;QAC1H,KAAK,MAAM,CAAC,IAAI,eAAe,EAAE,CAAC;YAChC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,SAAS,CAAC;gBAAE,SAAS;YAE9F,MAAM,YAAY,GAAG;gBACnB,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE;gBACpD,IAAI,EAAI,EAAE,IAAI,EAAE,CAAC,CAAC,SAAS,EAAI,IAAI,EAAE,CAAC,CAAC,SAAS,EAAI;gBACpD,IAAI,EAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;aAClE,CAAC;YACF,8DAA8D;YAC9D,IAAI,uBAAuB,CAAC,SAAS,EAAE,YAAmB,CAAC;gBAAE,SAAS;YAEtE,IAAI,IAAI,GAAG,KAAK,CAAC;YACjB,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC1B,IAAI,eAAe,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,eAAe,EAAE,CAAC;oBAAC,IAAI,GAAG,IAAI,CAAC;oBAAC,MAAM;gBAAC,CAAC;YACnG,CAAC;YACD,IAAI,IAAI;gBAAE,SAAS;YAEnB,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QAED,6CAA6C;QAC7C,MAAM,UAAU,GAAG,wBAAwB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;QAC5G,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;gBACrF,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChB,CAAC;QACH,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;CACF;AAED,8EAA8E;AAC9E,4CAA4C;AAC5C,8EAA8E;AAE9E,SAAS,qBAAqB,CAC5B,KAAwB,EACxB,OAAqC,EACrC,KAAiC,EACjC,WAAwB,EACxB,gBAA6B;IAE7B,MAAM,KAAK,GAA+B,EAAE,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,GAAG,EAAwB,CAAC;IACpD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC3D,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAC9C,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAErD,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjC,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;oBACjB,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC;oBAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;oBAC7E,IAAI,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC5D,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;wBAC1B,IAAI,MAAM,EAAE,CAAC;4BACX,KAAK,CAAC,IAAI,CAAC;gCACT,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI;gCAC9C,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI;gCAC9C,IAAI,EAAE;oCACJ,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,QAAiB,EAAE;oCACjE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAI,IAAI,EAAE,MAAiB,EAAE;iCAClE;gCACD,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK;6BAClC,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;gBAED,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;oBACnB,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC;oBAC5B,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;oBAC5F,KAAK,MAAM,MAAM,IAAI,iBAAiB,EAAE,CAAC;wBACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,YAAY,MAAM,KAAK,CAAC,CAAC,CAAC;wBAC9D,IAAI,KAAK,EAAE,CAAC;4BACV,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;4BAC/B,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,aAAa,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;4BAC/F,IAAI,WAAW,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC;gCACxE,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;gCAC1B,IAAI,MAAM,EAAE,CAAC;oCACX,KAAK,CAAC,IAAI,CAAC;wCACT,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI;wCAC9C,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI;wCAC9C,IAAI,EAAE;4CACJ,EAAE,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,QAAiB,EAAE;4CACvE,EAAE,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAI,IAAI,EAAE,MAAiB,EAAE;yCACxE;wCACD,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK;qCACnC,CAAC,CAAC;gCACL,CAAC;4BACH,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,uBAAuB,CAC9B,KAAwB,EACxB,OAAqC,EACrC,KAAiC,EACjC,oBAA8C,EAC9C,gBAA6B;IAE7B,MAAM,KAAK,GAA+B,EAAE,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,GAAG,EAAwB,CAAC;IACpD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC3D,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAC9C,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAErD,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjC,MAAM,gBAAgB,GAAG,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,0BAA0B,CAAC,CAAC;gBAC3E,IAAI,gBAAgB,EAAE,CAAC;oBACrB,MAAM,SAAS,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;oBACtC,MAAM,QAAQ,GAAI,gBAAgB,CAAC,CAAC,CAAC,CAAC;oBACtC,MAAM,cAAc,GAAG,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;oBAC3D,IAAI,cAAc,EAAE,CAAC;wBACnB,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;wBAC1E,IAAI,SAAS,EAAE,CAAC;4BACd,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;4BAC1B,IAAI,MAAM,EAAE,CAAC;gCACX,KAAK,CAAC,IAAI,CAAC;oCACT,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI;oCAC9C,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI;oCAC9C,IAAI,EAAE;wCACJ,EAAE,QAAQ,EAAE,SAAS,EAAqB,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,QAAiB,EAAE;wCACtF,EAAE,QAAQ,EAAE,GAAG,SAAS,IAAI,QAAQ,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAI,IAAI,EAAE,MAAiB,EAAE;qCACtF;oCACD,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK;iCACnC,CAAC,CAAC;4BACL,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,wBAAwB,CAC/B,KAAwB,EACxB,KAAwB,EACxB,OAAqC,EACrC,KAAiC,EACjC,gBAA6B;IAE7B,MAAM,KAAK,GAA+B,EAAE,CAAC;IAE7C,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAwD,CAAC;IAC7F,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,IAAI,KAAK,uBAAuB,EAAE,CAAC;YAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;YAClE,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,SAAS,GAAI,KAAK,CAAC,CAAC,CAAC,CAAC;gBAC5B,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBAC5B,IAAI,YAAY,GAAG,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBACxD,IAAI,CAAC,YAAY,EAAE,CAAC;oBAAC,YAAY,GAAG,IAAI,GAAG,EAAE,CAAC;oBAAC,oBAAoB,CAAC,GAAG,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;gBAAC,CAAC;gBACpG,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,oBAAoB,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAElD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAwB,CAAC;IACpD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC3D,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAC9C,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAErD,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;YAClC,IAAI,CAAC,UAAU;gBAAE,SAAS;YAC1B,MAAM,kBAAkB,GAAG,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAChE,IAAI,CAAC,kBAAkB;gBAAE,SAAS;YAElC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjC,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;oBACjB,MAAM,WAAW,GAAG,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;oBACzD,IAAI,WAAW,EAAE,CAAC;wBAChB,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,WAAW,CAAC,IAAI,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC;wBAChG,IAAI,CAAC,MAAM,EAAE,CAAC;4BACZ,KAAK,CAAC,IAAI,CAAC;gCACT,WAAW,EAAE,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI;gCACnD,WAAW,EAAE,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI;gCACnD,IAAI,EAAE;oCACJ,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,QAAiB,EAAE;oCAC3E,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAS,IAAI,EAAE,MAAiB,EAAE;iCAC5E;gCACD,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK;6BACnC,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,wFAAwF;IACxF,KAAK,KAAK,CAAC;IACX,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -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,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pass #31: unbounded-collection (CWE-770, category: performance)
|
|
3
|
+
*
|
|
4
|
+
* Detects collections that grow unboundedly inside a loop with no
|
|
5
|
+
* corresponding size limit check or clear/remove operation.
|
|
6
|
+
*
|
|
7
|
+
* Detection strategy:
|
|
8
|
+
* 1. For each loop body (via `graph.loopBodies()`), find all calls in the
|
|
9
|
+
* range whose method_name is a known "grow" operation.
|
|
10
|
+
* 2. For each grow call, extract the receiver as the collection variable.
|
|
11
|
+
* 3. Check if the loop body also contains any shrink operation (`clear`,
|
|
12
|
+
* `remove`, `delete`, `shift`, `pop`, `removeFirst`, `poll`) on the
|
|
13
|
+
* same receiver, OR a size-limit guard in the source text
|
|
14
|
+
* (`size() <`, `length <`, `size() <=`, etc.).
|
|
15
|
+
* 4. If grow-only with no limit found: emit a finding.
|
|
16
|
+
*
|
|
17
|
+
* Languages: Java, JavaScript/TypeScript, Python, Rust. Bash — skipped.
|
|
18
|
+
*/
|
|
19
|
+
import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
|
|
20
|
+
export interface UnboundedCollectionResult {
|
|
21
|
+
unboundedCollections: Array<{
|
|
22
|
+
receiver: string;
|
|
23
|
+
line: number;
|
|
24
|
+
loopStart: number;
|
|
25
|
+
loopEnd: number;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
export declare class UnboundedCollectionPass implements AnalysisPass<UnboundedCollectionResult> {
|
|
29
|
+
readonly name = "unbounded-collection";
|
|
30
|
+
readonly category: "performance";
|
|
31
|
+
run(ctx: PassContext): UnboundedCollectionResult;
|
|
32
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pass #31: unbounded-collection (CWE-770, category: performance)
|
|
3
|
+
*
|
|
4
|
+
* Detects collections that grow unboundedly inside a loop with no
|
|
5
|
+
* corresponding size limit check or clear/remove operation.
|
|
6
|
+
*
|
|
7
|
+
* Detection strategy:
|
|
8
|
+
* 1. For each loop body (via `graph.loopBodies()`), find all calls in the
|
|
9
|
+
* range whose method_name is a known "grow" operation.
|
|
10
|
+
* 2. For each grow call, extract the receiver as the collection variable.
|
|
11
|
+
* 3. Check if the loop body also contains any shrink operation (`clear`,
|
|
12
|
+
* `remove`, `delete`, `shift`, `pop`, `removeFirst`, `poll`) on the
|
|
13
|
+
* same receiver, OR a size-limit guard in the source text
|
|
14
|
+
* (`size() <`, `length <`, `size() <=`, etc.).
|
|
15
|
+
* 4. If grow-only with no limit found: emit a finding.
|
|
16
|
+
*
|
|
17
|
+
* Languages: Java, JavaScript/TypeScript, Python, Rust. Bash — skipped.
|
|
18
|
+
*/
|
|
19
|
+
/** Method names that grow a collection, keyed by language group. */
|
|
20
|
+
const GROW_METHODS = {
|
|
21
|
+
java: new Set(['add', 'put', 'offer', 'push', 'addAll', 'addFirst', 'addLast', 'enqueue', 'insert']),
|
|
22
|
+
javascript: new Set(['push', 'set', 'add', 'unshift', 'append', 'prepend']),
|
|
23
|
+
typescript: new Set(['push', 'set', 'add', 'unshift', 'append', 'prepend']),
|
|
24
|
+
python: new Set(['append', 'extend', 'update', 'add', 'insert']),
|
|
25
|
+
rust: new Set(['push', 'insert', 'push_back', 'push_front']),
|
|
26
|
+
};
|
|
27
|
+
/** Method names that shrink a collection. Language-agnostic. */
|
|
28
|
+
const SHRINK_METHODS = new Set([
|
|
29
|
+
'clear', 'remove', 'delete', 'shift', 'pop', 'removeFirst', 'removeLast',
|
|
30
|
+
'poll', 'pollFirst', 'pollLast', 'dequeue', 'discard', 'drain',
|
|
31
|
+
]);
|
|
32
|
+
/** Regex: size limit guard pattern in source text. */
|
|
33
|
+
const SIZE_LIMIT_RE = /\b(?:size|length|count|len)\s*\(\)?\s*[<>]=?\s*\d|\b(?:MAX|LIMIT|CAPACITY|MAX_SIZE)\b/i;
|
|
34
|
+
export class UnboundedCollectionPass {
|
|
35
|
+
name = 'unbounded-collection';
|
|
36
|
+
category = 'performance';
|
|
37
|
+
run(ctx) {
|
|
38
|
+
const { graph, code, language } = ctx;
|
|
39
|
+
if (language === 'bash') {
|
|
40
|
+
return { unboundedCollections: [] };
|
|
41
|
+
}
|
|
42
|
+
const growMethods = GROW_METHODS[language] ?? GROW_METHODS['javascript'];
|
|
43
|
+
const file = graph.ir.meta.file;
|
|
44
|
+
const codeLines = code.split('\n');
|
|
45
|
+
const loops = graph.loopBodies();
|
|
46
|
+
if (loops.length === 0)
|
|
47
|
+
return { unboundedCollections: [] };
|
|
48
|
+
const unboundedCollections = [];
|
|
49
|
+
const reported = new Set();
|
|
50
|
+
for (const loop of loops) {
|
|
51
|
+
const { start_line, end_line } = loop;
|
|
52
|
+
// Collect source text for the loop body (for heuristic checks)
|
|
53
|
+
const loopSource = codeLines.slice(start_line - 1, end_line).join('\n');
|
|
54
|
+
// Find grow calls in the loop body
|
|
55
|
+
const growCalls = [];
|
|
56
|
+
for (const call of graph.ir.calls) {
|
|
57
|
+
const ln = call.location.line;
|
|
58
|
+
if (ln < start_line || ln > end_line)
|
|
59
|
+
continue;
|
|
60
|
+
if (!growMethods.has(call.method_name))
|
|
61
|
+
continue;
|
|
62
|
+
if (!call.receiver)
|
|
63
|
+
continue;
|
|
64
|
+
// Skip 'this' receiver — can't reliably bound
|
|
65
|
+
if (call.receiver === 'this' || call.receiver === 'self')
|
|
66
|
+
continue;
|
|
67
|
+
growCalls.push({ receiver: call.receiver, line: ln });
|
|
68
|
+
}
|
|
69
|
+
if (growCalls.length === 0)
|
|
70
|
+
continue;
|
|
71
|
+
// Group by receiver
|
|
72
|
+
const receiverLines = new Map();
|
|
73
|
+
for (const { receiver, line } of growCalls) {
|
|
74
|
+
if (!receiverLines.has(receiver)) {
|
|
75
|
+
receiverLines.set(receiver, line);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (const [receiver, firstGrowLine] of receiverLines.entries()) {
|
|
79
|
+
// Check for shrink operations on the same receiver in the loop body
|
|
80
|
+
let hasShrink = false;
|
|
81
|
+
for (const call of graph.ir.calls) {
|
|
82
|
+
const ln = call.location.line;
|
|
83
|
+
if (ln < start_line || ln > end_line)
|
|
84
|
+
continue;
|
|
85
|
+
if (call.receiver !== receiver)
|
|
86
|
+
continue;
|
|
87
|
+
if (SHRINK_METHODS.has(call.method_name)) {
|
|
88
|
+
hasShrink = true;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (hasShrink)
|
|
93
|
+
continue;
|
|
94
|
+
// Check for size-limit guard in loop source
|
|
95
|
+
if (SIZE_LIMIT_RE.test(loopSource))
|
|
96
|
+
continue;
|
|
97
|
+
const key = `${receiver}-${start_line}`;
|
|
98
|
+
if (reported.has(key))
|
|
99
|
+
continue;
|
|
100
|
+
reported.add(key);
|
|
101
|
+
unboundedCollections.push({
|
|
102
|
+
receiver,
|
|
103
|
+
line: firstGrowLine,
|
|
104
|
+
loopStart: start_line,
|
|
105
|
+
loopEnd: end_line,
|
|
106
|
+
});
|
|
107
|
+
ctx.addFinding({
|
|
108
|
+
id: `unbounded-collection-${file}-${firstGrowLine}`,
|
|
109
|
+
pass: this.name,
|
|
110
|
+
category: this.category,
|
|
111
|
+
rule_id: this.name,
|
|
112
|
+
cwe: 'CWE-770',
|
|
113
|
+
severity: 'medium',
|
|
114
|
+
level: 'warning',
|
|
115
|
+
message: `Unbounded collection: \`${receiver}\` grows inside a loop (lines ${start_line}–${end_line}) ` +
|
|
116
|
+
`with no size limit or clear`,
|
|
117
|
+
file,
|
|
118
|
+
line: firstGrowLine,
|
|
119
|
+
fix: `Add a size limit check (e.g., \`if (${receiver}.size() >= MAX) break;\`) ` +
|
|
120
|
+
`or periodically clear/drain \`${receiver}\`.`,
|
|
121
|
+
evidence: { receiver, loop_start: start_line, loop_end: end_line },
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { unboundedCollections };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=unbounded-collection-pass.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unbounded-collection-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/unbounded-collection-pass.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
+
}
|