circle-ir 4.16.1 → 4.18.1
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/dist/analysis/advisories.d.ts +186 -0
- package/dist/analysis/advisories.d.ts.map +1 -0
- package/dist/analysis/advisories.js +391 -0
- package/dist/analysis/advisories.js.map +1 -0
- package/dist/analysis/comment-lines.d.ts +14 -0
- package/dist/analysis/comment-lines.d.ts.map +1 -0
- package/dist/analysis/comment-lines.js +34 -0
- package/dist/analysis/comment-lines.js.map +1 -0
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +59 -5
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/constant-caller-gate.d.ts +2 -2
- package/dist/analysis/constant-caller-gate.d.ts.map +1 -1
- package/dist/analysis/constant-caller-gate.js +205 -29
- package/dist/analysis/constant-caller-gate.js.map +1 -1
- package/dist/analysis/constant-propagation/index.d.ts +2 -0
- package/dist/analysis/constant-propagation/index.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/index.js +10 -0
- package/dist/analysis/constant-propagation/index.js.map +1 -1
- package/dist/analysis/constant-propagation/propagator.d.ts +15 -0
- package/dist/analysis/constant-propagation/propagator.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/propagator.js +224 -20
- package/dist/analysis/constant-propagation/propagator.js.map +1 -1
- package/dist/analysis/constant-propagation/types.d.ts +2 -0
- package/dist/analysis/constant-propagation/types.d.ts.map +1 -1
- package/dist/analysis/constant-propagation.d.ts +1 -1
- package/dist/analysis/constant-propagation.d.ts.map +1 -1
- package/dist/analysis/constant-propagation.js +1 -1
- package/dist/analysis/constant-propagation.js.map +1 -1
- package/dist/analysis/entry-point-detection.d.ts.map +1 -1
- package/dist/analysis/entry-point-detection.js +72 -0
- package/dist/analysis/entry-point-detection.js.map +1 -1
- package/dist/analysis/interprocedural.d.ts.map +1 -1
- package/dist/analysis/interprocedural.js +75 -8
- package/dist/analysis/interprocedural.js.map +1 -1
- package/dist/analysis/maven-declared.d.ts +72 -0
- package/dist/analysis/maven-declared.d.ts.map +1 -0
- package/dist/analysis/maven-declared.js +216 -0
- package/dist/analysis/maven-declared.js.map +1 -0
- package/dist/analysis/maven-version.d.ts +28 -0
- package/dist/analysis/maven-version.d.ts.map +1 -0
- package/dist/analysis/maven-version.js +172 -0
- package/dist/analysis/maven-version.js.map +1 -0
- package/dist/analysis/passes/constant-propagation-pass.d.ts.map +1 -1
- package/dist/analysis/passes/constant-propagation-pass.js +29 -1
- package/dist/analysis/passes/constant-propagation-pass.js.map +1 -1
- package/dist/analysis/passes/cross-file-pass.d.ts.map +1 -1
- package/dist/analysis/passes/cross-file-pass.js +84 -2
- package/dist/analysis/passes/cross-file-pass.js.map +1 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.d.ts +7 -0
- package/dist/analysis/passes/csharp-sanitizer-credit.d.ts.map +1 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.js +44 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.js.map +1 -1
- package/dist/analysis/passes/insecure-cookie-pass.d.ts +2 -0
- package/dist/analysis/passes/insecure-cookie-pass.d.ts.map +1 -1
- package/dist/analysis/passes/insecure-cookie-pass.js +25 -3
- package/dist/analysis/passes/insecure-cookie-pass.js.map +1 -1
- package/dist/analysis/passes/interprocedural-pass.d.ts +11 -14
- package/dist/analysis/passes/interprocedural-pass.d.ts.map +1 -1
- package/dist/analysis/passes/interprocedural-pass.js +562 -0
- package/dist/analysis/passes/interprocedural-pass.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts +18 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +1079 -14
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/param-source-flow-gate-pass.d.ts.map +1 -1
- package/dist/analysis/passes/param-source-flow-gate-pass.js +82 -3
- package/dist/analysis/passes/param-source-flow-gate-pass.js.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.d.ts +44 -2
- package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.js +690 -12
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.d.ts.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.js +298 -19
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- package/dist/analysis/passes/tls-verify-disabled-pass.d.ts +18 -0
- package/dist/analysis/passes/tls-verify-disabled-pass.d.ts.map +1 -1
- package/dist/analysis/passes/tls-verify-disabled-pass.js +161 -0
- package/dist/analysis/passes/tls-verify-disabled-pass.js.map +1 -1
- package/dist/analysis/passes/weak-random-pass.d.ts +6 -0
- package/dist/analysis/passes/weak-random-pass.d.ts.map +1 -1
- package/dist/analysis/passes/weak-random-pass.js +29 -0
- package/dist/analysis/passes/weak-random-pass.js.map +1 -1
- package/dist/analysis/promote-cross-file-flows.d.ts +17 -0
- package/dist/analysis/promote-cross-file-flows.d.ts.map +1 -0
- package/dist/analysis/promote-cross-file-flows.js +32 -0
- package/dist/analysis/promote-cross-file-flows.js.map +1 -0
- package/dist/analysis/rust-literal-guard.d.ts.map +1 -1
- package/dist/analysis/rust-literal-guard.js +100 -1
- package/dist/analysis/rust-literal-guard.js.map +1 -1
- package/dist/analysis/sbom.d.ts +100 -0
- package/dist/analysis/sbom.d.ts.map +1 -1
- package/dist/analysis/sbom.js +514 -3
- package/dist/analysis/sbom.js.map +1 -1
- package/dist/analysis/semver-version.d.ts +30 -0
- package/dist/analysis/semver-version.d.ts.map +1 -0
- package/dist/analysis/semver-version.js +76 -0
- package/dist/analysis/semver-version.js.map +1 -0
- package/dist/analysis/taint-matcher.d.ts +5 -0
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +226 -4
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analyzer.d.ts +57 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +69 -6
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +3360 -314
- package/dist/core/circle-ir-core.cjs +783 -220
- package/dist/core/circle-ir-core.js +783 -220
- package/dist/core/extractors/dfg.js +205 -2
- package/dist/core/extractors/dfg.js.map +1 -1
- package/dist/core/extractors/ts-declaration-detail.d.ts +39 -0
- package/dist/core/extractors/ts-declaration-detail.d.ts.map +1 -0
- package/dist/core/extractors/ts-declaration-detail.js +266 -0
- package/dist/core/extractors/ts-declaration-detail.js.map +1 -0
- package/dist/core/extractors/types.js +53 -9
- package/dist/core/extractors/types.js.map +1 -1
- package/dist/graph/analysis-pass.d.ts +4 -1
- package/dist/graph/analysis-pass.d.ts.map +1 -1
- package/dist/graph/analysis-pass.js +4 -1
- package/dist/graph/analysis-pass.js.map +1 -1
- package/dist/graph-facts/dead-code.d.ts +62 -0
- package/dist/graph-facts/dead-code.d.ts.map +1 -0
- package/dist/graph-facts/dead-code.js +90 -0
- package/dist/graph-facts/dead-code.js.map +1 -0
- package/dist/graph-facts/dependency-calls.d.ts +86 -0
- package/dist/graph-facts/dependency-calls.d.ts.map +1 -0
- package/dist/graph-facts/dependency-calls.js +93 -0
- package/dist/graph-facts/dependency-calls.js.map +1 -0
- package/dist/graph-facts/index.d.ts +10 -0
- package/dist/graph-facts/index.d.ts.map +1 -0
- package/dist/graph-facts/index.js +10 -0
- package/dist/graph-facts/index.js.map +1 -0
- package/dist/graph-facts/maven-packages.d.ts +42 -0
- package/dist/graph-facts/maven-packages.d.ts.map +1 -0
- package/dist/graph-facts/maven-packages.js +133 -0
- package/dist/graph-facts/maven-packages.js.map +1 -0
- package/dist/graph-facts/reach.d.ts +203 -0
- package/dist/graph-facts/reach.d.ts.map +1 -0
- package/dist/graph-facts/reach.js +219 -0
- package/dist/graph-facts/reach.js.map +1 -0
- package/dist/graph-facts/removal-reach.d.ts +73 -0
- package/dist/graph-facts/removal-reach.d.ts.map +1 -0
- package/dist/graph-facts/removal-reach.js +64 -0
- package/dist/graph-facts/removal-reach.js.map +1 -0
- package/dist/index.d.ts +21 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -3
- package/dist/index.js.map +1 -1
- package/dist/navigation/go-graph.d.ts +68 -0
- package/dist/navigation/go-graph.d.ts.map +1 -0
- package/dist/navigation/go-graph.js +753 -0
- package/dist/navigation/go-graph.js.map +1 -0
- package/dist/navigation/index.d.ts +7 -1
- package/dist/navigation/index.d.ts.map +1 -1
- package/dist/navigation/index.js +6 -0
- package/dist/navigation/index.js.map +1 -1
- package/dist/navigation/java-entry-tables.d.ts +43 -0
- package/dist/navigation/java-entry-tables.d.ts.map +1 -0
- package/dist/navigation/java-entry-tables.js +56 -0
- package/dist/navigation/java-entry-tables.js.map +1 -0
- package/dist/navigation/java-graph.d.ts +64 -0
- package/dist/navigation/java-graph.d.ts.map +1 -0
- package/dist/navigation/java-graph.js +466 -0
- package/dist/navigation/java-graph.js.map +1 -0
- package/dist/navigation/navigation-index.d.ts +91 -2
- package/dist/navigation/navigation-index.d.ts.map +1 -1
- package/dist/navigation/navigation-index.js +372 -45
- package/dist/navigation/navigation-index.js.map +1 -1
- package/dist/navigation/python-graph.d.ts +73 -0
- package/dist/navigation/python-graph.d.ts.map +1 -0
- package/dist/navigation/python-graph.js +867 -0
- package/dist/navigation/python-graph.js.map +1 -0
- package/dist/navigation/ts-call-edges.d.ts +193 -0
- package/dist/navigation/ts-call-edges.d.ts.map +1 -0
- package/dist/navigation/ts-call-edges.js +2089 -0
- package/dist/navigation/ts-call-edges.js.map +1 -0
- package/dist/navigation/ts-entry-tables.d.ts +88 -0
- package/dist/navigation/ts-entry-tables.d.ts.map +1 -0
- package/dist/navigation/ts-entry-tables.js +50 -0
- package/dist/navigation/ts-entry-tables.js.map +1 -0
- package/dist/path-set/branches.d.ts +52 -0
- package/dist/path-set/branches.d.ts.map +1 -0
- package/dist/path-set/branches.js +124 -0
- package/dist/path-set/branches.js.map +1 -0
- package/dist/path-set/finding-diff.d.ts +63 -0
- package/dist/path-set/finding-diff.d.ts.map +1 -0
- package/dist/path-set/finding-diff.js +150 -0
- package/dist/path-set/finding-diff.js.map +1 -0
- package/dist/path-set/index.d.ts +18 -0
- package/dist/path-set/index.d.ts.map +1 -0
- package/dist/path-set/index.js +12 -0
- package/dist/path-set/index.js.map +1 -0
- package/dist/path-set/line-diff.d.ts +19 -0
- package/dist/path-set/line-diff.d.ts.map +1 -0
- package/dist/path-set/line-diff.js +98 -0
- package/dist/path-set/line-diff.js.map +1 -0
- package/dist/path-set/path-set.d.ts +88 -0
- package/dist/path-set/path-set.d.ts.map +1 -0
- package/dist/path-set/path-set.js +716 -0
- package/dist/path-set/path-set.js.map +1 -0
- package/dist/path-set/templates.d.ts +42 -0
- package/dist/path-set/templates.d.ts.map +1 -0
- package/dist/path-set/templates.js +119 -0
- package/dist/path-set/templates.js.map +1 -0
- package/dist/path-set/types.d.ts +195 -0
- package/dist/path-set/types.d.ts.map +1 -0
- package/dist/path-set/types.js +8 -0
- package/dist/path-set/types.js.map +1 -0
- package/dist/remedy-class/index.d.ts +12 -0
- package/dist/remedy-class/index.d.ts.map +1 -0
- package/dist/remedy-class/index.js +9 -0
- package/dist/remedy-class/index.js.map +1 -0
- package/dist/remedy-class/match.d.ts +165 -0
- package/dist/remedy-class/match.d.ts.map +1 -0
- package/dist/remedy-class/match.js +705 -0
- package/dist/remedy-class/match.js.map +1 -0
- package/dist/remedy-class/output-context.d.ts +25 -0
- package/dist/remedy-class/output-context.d.ts.map +1 -0
- package/dist/remedy-class/output-context.js +150 -0
- package/dist/remedy-class/output-context.js.map +1 -0
- package/dist/remedy-class/sha256.d.ts +18 -0
- package/dist/remedy-class/sha256.d.ts.map +1 -0
- package/dist/remedy-class/sha256.js +89 -0
- package/dist/remedy-class/sha256.js.map +1 -0
- package/dist/remedy-class/table.d.ts +130 -0
- package/dist/remedy-class/table.d.ts.map +1 -0
- package/dist/remedy-class/table.js +457 -0
- package/dist/remedy-class/table.js.map +1 -0
- package/dist/resolution/cross-file.d.ts +18 -0
- package/dist/resolution/cross-file.d.ts.map +1 -1
- package/dist/resolution/cross-file.js +194 -5
- package/dist/resolution/cross-file.js.map +1 -1
- package/dist/stable-ids/builder.d.ts +20 -0
- package/dist/stable-ids/builder.d.ts.map +1 -0
- package/dist/stable-ids/builder.js +65 -0
- package/dist/stable-ids/builder.js.map +1 -0
- package/dist/stable-ids/diff.d.ts +18 -0
- package/dist/stable-ids/diff.d.ts.map +1 -0
- package/dist/stable-ids/diff.js +37 -0
- package/dist/stable-ids/diff.js.map +1 -0
- package/dist/stable-ids/go.d.ts +28 -0
- package/dist/stable-ids/go.d.ts.map +1 -0
- package/dist/stable-ids/go.js +160 -0
- package/dist/stable-ids/go.js.map +1 -0
- package/dist/stable-ids/hash.d.ts +22 -0
- package/dist/stable-ids/hash.d.ts.map +1 -0
- package/dist/stable-ids/hash.js +167 -0
- package/dist/stable-ids/hash.js.map +1 -0
- package/dist/stable-ids/index.d.ts +30 -0
- package/dist/stable-ids/index.d.ts.map +1 -0
- package/dist/stable-ids/index.js +59 -0
- package/dist/stable-ids/index.js.map +1 -0
- package/dist/stable-ids/java.d.ts +28 -0
- package/dist/stable-ids/java.d.ts.map +1 -0
- package/dist/stable-ids/java.js +224 -0
- package/dist/stable-ids/java.js.map +1 -0
- package/dist/stable-ids/moves.d.ts +87 -0
- package/dist/stable-ids/moves.d.ts.map +1 -0
- package/dist/stable-ids/moves.js +222 -0
- package/dist/stable-ids/moves.js.map +1 -0
- package/dist/stable-ids/nodes.d.ts +32 -0
- package/dist/stable-ids/nodes.d.ts.map +1 -0
- package/dist/stable-ids/nodes.js +92 -0
- package/dist/stable-ids/nodes.js.map +1 -0
- package/dist/stable-ids/normalize.d.ts +35 -0
- package/dist/stable-ids/normalize.d.ts.map +1 -0
- package/dist/stable-ids/normalize.js +174 -0
- package/dist/stable-ids/normalize.js.map +1 -0
- package/dist/stable-ids/parse-errors.d.ts +24 -0
- package/dist/stable-ids/parse-errors.d.ts.map +1 -0
- package/dist/stable-ids/parse-errors.js +46 -0
- package/dist/stable-ids/parse-errors.js.map +1 -0
- package/dist/stable-ids/python.d.ts +27 -0
- package/dist/stable-ids/python.d.ts.map +1 -0
- package/dist/stable-ids/python.js +212 -0
- package/dist/stable-ids/python.js.map +1 -0
- package/dist/stable-ids/ts-parse-errors.d.ts +27 -0
- package/dist/stable-ids/ts-parse-errors.d.ts.map +1 -0
- package/dist/stable-ids/ts-parse-errors.js +75 -0
- package/dist/stable-ids/ts-parse-errors.js.map +1 -0
- package/dist/stable-ids/types.d.ts +106 -0
- package/dist/stable-ids/types.d.ts.map +1 -0
- package/dist/stable-ids/types.js +16 -0
- package/dist/stable-ids/types.js.map +1 -0
- package/dist/stable-ids/typescript.d.ts +30 -0
- package/dist/stable-ids/typescript.d.ts.map +1 -0
- package/dist/stable-ids/typescript.js +362 -0
- package/dist/stable-ids/typescript.js.map +1 -0
- package/dist/types/config.d.ts +9 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.d.ts +53 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -63,6 +63,11 @@ export class ConstantPropagator {
|
|
|
63
63
|
taintedArrayElements = new Map();
|
|
64
64
|
// Track current method name for scoping local variables
|
|
65
65
|
currentMethod = null;
|
|
66
|
+
/** Start lines of nested `{ ... }` blocks — disambiguate same-named locals (#476 Collections10). */
|
|
67
|
+
blockScopeStack = [];
|
|
68
|
+
lineBlockScopes = new Map();
|
|
69
|
+
/** Skip block-scope push on the method/constructor body `{ ... }` (#476). */
|
|
70
|
+
skipMethodBodyBlockScope = false;
|
|
66
71
|
// Track conditional taints: which variables were tainted under which conditions
|
|
67
72
|
// Maps condition expression string → set of variables tainted under that condition
|
|
68
73
|
conditionalTaints = new Map();
|
|
@@ -76,6 +81,8 @@ export class ConstantPropagator {
|
|
|
76
81
|
inSynchronizedBlock = false;
|
|
77
82
|
// Track iterator sources: iterator variable name → collection name it was created from
|
|
78
83
|
iteratorSources = new Map();
|
|
84
|
+
/** Vars assigned from `iter.next()` — do not retro-clear on later iterator rebind (#476). */
|
|
85
|
+
taintedFromIteratorNext = new Set();
|
|
79
86
|
// Track class field names (declared at class level, not local variables)
|
|
80
87
|
classFields = new Set();
|
|
81
88
|
// Track tainted method parameters for inter-procedural analysis
|
|
@@ -133,12 +140,16 @@ export class ConstantPropagator {
|
|
|
133
140
|
this.sanitizedVars.clear();
|
|
134
141
|
this.sanitizedVarUncovered.clear();
|
|
135
142
|
this.currentMethod = null;
|
|
143
|
+
this.blockScopeStack = [];
|
|
144
|
+
this.lineBlockScopes.clear();
|
|
145
|
+
this.skipMethodBodyBlockScope = false;
|
|
136
146
|
this.conditionalTaints.clear();
|
|
137
147
|
this.conditionStack = [];
|
|
138
148
|
this.lineConditions.clear();
|
|
139
149
|
this.synchronizedLines.clear();
|
|
140
150
|
this.inSynchronizedBlock = false;
|
|
141
151
|
this.iteratorSources.clear();
|
|
152
|
+
this.taintedFromIteratorNext.clear();
|
|
142
153
|
this.classFields.clear();
|
|
143
154
|
this.instanceFieldTaint.clear();
|
|
144
155
|
this.currentClassName = null;
|
|
@@ -180,6 +191,9 @@ export class ConstantPropagator {
|
|
|
180
191
|
// Add unscoped versions of scoped names for backward compatibility
|
|
181
192
|
for (const name of this.tainted) {
|
|
182
193
|
if (name.includes(':')) {
|
|
194
|
+
if (/^[^:]+:b\d+:/.test(name)) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
183
197
|
const unscoped = name.substring(name.indexOf(':') + 1);
|
|
184
198
|
resultTainted.add(unscoped);
|
|
185
199
|
}
|
|
@@ -204,6 +218,9 @@ export class ConstantPropagator {
|
|
|
204
218
|
if (this.goScopedNames.has(name))
|
|
205
219
|
continue;
|
|
206
220
|
if (name.includes(':')) {
|
|
221
|
+
if (/^[^:]+:b\d+:/.test(name)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
207
224
|
const unscoped = name.substring(name.indexOf(':') + 1);
|
|
208
225
|
// Only add if not already present (scoped version wins on conflict)
|
|
209
226
|
if (!resultSymbols.has(unscoped)) {
|
|
@@ -214,6 +231,7 @@ export class ConstantPropagator {
|
|
|
214
231
|
return {
|
|
215
232
|
symbols: resultSymbols,
|
|
216
233
|
tainted: resultTainted,
|
|
234
|
+
lineBlockScopes: new Map(this.lineBlockScopes),
|
|
217
235
|
unreachableLines: new Set(this.unreachableLines),
|
|
218
236
|
taintedCollections: new Map(this.taintedCollections),
|
|
219
237
|
taintedArrayElements: new Map(this.taintedArrayElements),
|
|
@@ -840,6 +858,20 @@ export class ConstantPropagator {
|
|
|
840
858
|
// ===========================================================================
|
|
841
859
|
// Taint Refinement
|
|
842
860
|
// ===========================================================================
|
|
861
|
+
/** True when `node` is `iter.next()` (possibly under a cast). */
|
|
862
|
+
isIteratorNextExpression(node) {
|
|
863
|
+
const text = getNodeText(node, this.source);
|
|
864
|
+
if (/\.\s*next\s*\(/.test(text))
|
|
865
|
+
return true;
|
|
866
|
+
let current = node;
|
|
867
|
+
if (current.type === 'cast_expression' || current.type === 'parenthesized_expression') {
|
|
868
|
+
current = current.childForFieldName('operand') ?? current.childForFieldName('expression');
|
|
869
|
+
}
|
|
870
|
+
if (!current || current.type !== 'method_invocation')
|
|
871
|
+
return false;
|
|
872
|
+
const nameNode = current.childForFieldName('name');
|
|
873
|
+
return nameNode ? getNodeText(nameNode, this.source) === 'next' : false;
|
|
874
|
+
}
|
|
843
875
|
refineTaintFromConstants() {
|
|
844
876
|
let changed = true;
|
|
845
877
|
let iterations = 0;
|
|
@@ -849,6 +881,9 @@ export class ConstantPropagator {
|
|
|
849
881
|
iterations++;
|
|
850
882
|
const toRemove = [];
|
|
851
883
|
for (const varName of this.tainted) {
|
|
884
|
+
if (this.taintedFromIteratorNext.has(varName)) {
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
852
887
|
const symbol = this.symbols.get(varName);
|
|
853
888
|
if (symbol && symbol.type !== 'unknown') {
|
|
854
889
|
toRemove.push(varName);
|
|
@@ -909,6 +944,9 @@ export class ConstantPropagator {
|
|
|
909
944
|
if (this.unreachableLines.has(line)) {
|
|
910
945
|
return true; // skip subtree
|
|
911
946
|
}
|
|
947
|
+
if (this.blockScopeStack.length > 0) {
|
|
948
|
+
this.lineBlockScopes.set(line, this.blockScopeStack[this.blockScopeStack.length - 1]);
|
|
949
|
+
}
|
|
912
950
|
// Track which condition this line is under for correlated predicate analysis
|
|
913
951
|
if (this.conditionStack.length > 0 && !this.lineConditions.has(line)) {
|
|
914
952
|
// Use the innermost (most recent) condition
|
|
@@ -948,12 +986,24 @@ export class ConstantPropagator {
|
|
|
948
986
|
case 'assignment_statement':
|
|
949
987
|
this.handleGoAssignment(node);
|
|
950
988
|
return false;
|
|
989
|
+
case 'var_declaration':
|
|
990
|
+
this.handleGoVarDeclaration(node);
|
|
991
|
+
return false;
|
|
951
992
|
case 'update_expression':
|
|
952
993
|
this.handleUpdateExpression(node);
|
|
953
994
|
return false;
|
|
954
995
|
case 'if_statement':
|
|
955
996
|
this.handleIfStatement(node);
|
|
956
997
|
return true;
|
|
998
|
+
case 'block': {
|
|
999
|
+
if (this.skipMethodBodyBlockScope) {
|
|
1000
|
+
this.skipMethodBodyBlockScope = false;
|
|
1001
|
+
}
|
|
1002
|
+
for (const child of node.children) {
|
|
1003
|
+
this.visit(child);
|
|
1004
|
+
}
|
|
1005
|
+
return true;
|
|
1006
|
+
}
|
|
957
1007
|
case 'switch_expression':
|
|
958
1008
|
case 'switch_statement':
|
|
959
1009
|
this.handleSwitch(node);
|
|
@@ -1046,10 +1096,12 @@ export class ConstantPropagator {
|
|
|
1046
1096
|
}
|
|
1047
1097
|
}
|
|
1048
1098
|
}
|
|
1049
|
-
// Visit the method body
|
|
1099
|
+
// Visit the method body (do not treat the outer `{ ... }` as a nested block scope)
|
|
1050
1100
|
const body = node.childForFieldName('body');
|
|
1051
1101
|
if (body) {
|
|
1102
|
+
this.skipMethodBodyBlockScope = true;
|
|
1052
1103
|
this.visit(body);
|
|
1104
|
+
this.skipMethodBodyBlockScope = false;
|
|
1053
1105
|
}
|
|
1054
1106
|
// Restore the previous method context
|
|
1055
1107
|
this.currentMethod = prevMethod;
|
|
@@ -1065,11 +1117,36 @@ export class ConstantPropagator {
|
|
|
1065
1117
|
if (varName.includes('.') || varName.includes(':')) {
|
|
1066
1118
|
return varName;
|
|
1067
1119
|
}
|
|
1068
|
-
|
|
1120
|
+
const blockId = this.blockScopeStack.length > 0
|
|
1121
|
+
? this.blockScopeStack[this.blockScopeStack.length - 1]
|
|
1122
|
+
: undefined;
|
|
1069
1123
|
if (this.currentMethod) {
|
|
1070
|
-
return
|
|
1124
|
+
return blockId !== undefined
|
|
1125
|
+
? `${this.currentMethod}:b${blockId}:${varName}`
|
|
1126
|
+
: `${this.currentMethod}:${varName}`;
|
|
1071
1127
|
}
|
|
1072
|
-
return varName;
|
|
1128
|
+
return blockId !== undefined ? `b${blockId}:${varName}` : varName;
|
|
1129
|
+
}
|
|
1130
|
+
/** Innermost block → method → unscoped, for reads that may bind to an outer declaration. */
|
|
1131
|
+
getScopedNameCandidates(varName) {
|
|
1132
|
+
if (varName.includes('.') || varName.includes(':')) {
|
|
1133
|
+
return [varName];
|
|
1134
|
+
}
|
|
1135
|
+
const out = [];
|
|
1136
|
+
for (let i = this.blockScopeStack.length - 1; i >= 0; i--) {
|
|
1137
|
+
const blockId = this.blockScopeStack[i];
|
|
1138
|
+
if (this.currentMethod) {
|
|
1139
|
+
out.push(`${this.currentMethod}:b${blockId}:${varName}`);
|
|
1140
|
+
}
|
|
1141
|
+
else {
|
|
1142
|
+
out.push(`b${blockId}:${varName}`);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
if (this.currentMethod) {
|
|
1146
|
+
out.push(`${this.currentMethod}:${varName}`);
|
|
1147
|
+
}
|
|
1148
|
+
out.push(varName);
|
|
1149
|
+
return out;
|
|
1073
1150
|
}
|
|
1074
1151
|
/**
|
|
1075
1152
|
* Look up a variable value, checking both scoped and unscoped names.
|
|
@@ -1077,12 +1154,12 @@ export class ConstantPropagator {
|
|
|
1077
1154
|
* either local (scoped) or global (unscoped, like class fields).
|
|
1078
1155
|
*/
|
|
1079
1156
|
lookupSymbol(varName) {
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1157
|
+
if (!varName.includes('.') && !varName.includes(':')) {
|
|
1158
|
+
for (const scopedName of this.getScopedNameCandidates(varName)) {
|
|
1159
|
+
const scopedValue = this.symbols.get(scopedName);
|
|
1160
|
+
if (scopedValue) {
|
|
1161
|
+
return scopedValue;
|
|
1162
|
+
}
|
|
1086
1163
|
}
|
|
1087
1164
|
}
|
|
1088
1165
|
// Fall back to unscoped name (class fields, etc.)
|
|
@@ -1152,9 +1229,26 @@ export class ConstantPropagator {
|
|
|
1152
1229
|
this.trackIteratorsInNode(initNode);
|
|
1153
1230
|
}
|
|
1154
1231
|
}
|
|
1155
|
-
//
|
|
1156
|
-
|
|
1157
|
-
|
|
1232
|
+
// #476: shadowing loop locals (`String str` per while) — scope the body block only.
|
|
1233
|
+
if (node.type === 'while_statement' || node.type === 'do_statement') {
|
|
1234
|
+
const body = node.childForFieldName('body');
|
|
1235
|
+
for (const child of node.children) {
|
|
1236
|
+
if (child !== body)
|
|
1237
|
+
this.visit(child);
|
|
1238
|
+
}
|
|
1239
|
+
if (body?.type === 'block') {
|
|
1240
|
+
this.blockScopeStack.push(getNodeLine(body));
|
|
1241
|
+
this.visit(body);
|
|
1242
|
+
this.blockScopeStack.pop();
|
|
1243
|
+
}
|
|
1244
|
+
else if (body) {
|
|
1245
|
+
this.visit(body);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
else {
|
|
1249
|
+
for (const child of node.children) {
|
|
1250
|
+
this.visit(child);
|
|
1251
|
+
}
|
|
1158
1252
|
}
|
|
1159
1253
|
// After visiting children, ensure loop variables stay unknown (they may have been overwritten)
|
|
1160
1254
|
for (const varName of loopVarNames) {
|
|
@@ -1318,6 +1412,10 @@ export class ConstantPropagator {
|
|
|
1318
1412
|
this.sanitizedVars.delete(scopedName); // No longer sanitized if receiving tainted value
|
|
1319
1413
|
this.sanitizedVarUncovered.delete(scopedName);
|
|
1320
1414
|
this.definitionNodes.set(scopedName, valueNode);
|
|
1415
|
+
if (this.isIteratorNextExpression(valueNode)) {
|
|
1416
|
+
this.taintedFromIteratorNext.add(scopedName);
|
|
1417
|
+
this.taintedFromIteratorNext.add(varName);
|
|
1418
|
+
}
|
|
1321
1419
|
this.symbols.set(scopedName, createUnknown(line));
|
|
1322
1420
|
}
|
|
1323
1421
|
else {
|
|
@@ -1367,6 +1465,71 @@ export class ConstantPropagator {
|
|
|
1367
1465
|
* helper that always returns a constant — clears the variable. Anything else stops being tracked here, which is what Go
|
|
1368
1466
|
* assignments got before this handler existed: the DFG decides.
|
|
1369
1467
|
*/
|
|
1468
|
+
/** `var x = expr` / `var x T = expr` — same clean/taint rules as `:=` (#783). */
|
|
1469
|
+
handleGoVarDeclaration(node) {
|
|
1470
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1471
|
+
const spec = node.child(i);
|
|
1472
|
+
if (!spec || spec.type !== 'var_spec')
|
|
1473
|
+
continue;
|
|
1474
|
+
const nameNode = spec.childForFieldName('name');
|
|
1475
|
+
let valueNode = spec.childForFieldName('value');
|
|
1476
|
+
if (!nameNode || !valueNode)
|
|
1477
|
+
continue;
|
|
1478
|
+
const names = [];
|
|
1479
|
+
if (nameNode.type === 'identifier') {
|
|
1480
|
+
names.push(getNodeText(nameNode, this.source));
|
|
1481
|
+
}
|
|
1482
|
+
else if (nameNode.type === 'identifier_list') {
|
|
1483
|
+
for (const c of nameNode.children) {
|
|
1484
|
+
if (c.type === 'identifier')
|
|
1485
|
+
names.push(getNodeText(c, this.source));
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
if (valueNode.type === 'expression_list') {
|
|
1489
|
+
valueNode =
|
|
1490
|
+
valueNode.children.find((c) => c.type !== ',' && c.type !== 'comment') ?? valueNode;
|
|
1491
|
+
}
|
|
1492
|
+
if (names.length === 0)
|
|
1493
|
+
continue;
|
|
1494
|
+
const line = getNodeLine(node);
|
|
1495
|
+
for (const varName of names) {
|
|
1496
|
+
if (varName === '_')
|
|
1497
|
+
continue;
|
|
1498
|
+
const scopedName = this.getScopedName(varName);
|
|
1499
|
+
this.goScopedNames.add(scopedName);
|
|
1500
|
+
if (this.isTaintedExpression(valueNode)) {
|
|
1501
|
+
this.goUnknownVars.add(scopedName);
|
|
1502
|
+
this.tainted.add(scopedName);
|
|
1503
|
+
this.sanitizedVars.delete(scopedName);
|
|
1504
|
+
this.sanitizedVarUncovered.delete(scopedName);
|
|
1505
|
+
this.definitionNodes.set(scopedName, valueNode);
|
|
1506
|
+
this.symbols.set(scopedName, createUnknown(line));
|
|
1507
|
+
continue;
|
|
1508
|
+
}
|
|
1509
|
+
if (!this.isGoKnownCleanRhs(valueNode) || this.goUnknownVars.has(scopedName)) {
|
|
1510
|
+
this.goUnknownVars.add(scopedName);
|
|
1511
|
+
this.symbols.delete(scopedName);
|
|
1512
|
+
this.sanitizedVars.delete(scopedName);
|
|
1513
|
+
this.sanitizedVarUncovered.delete(scopedName);
|
|
1514
|
+
if (!this.inConditionalBranch) {
|
|
1515
|
+
this.tainted.delete(scopedName);
|
|
1516
|
+
this.definitionNodes.delete(scopedName);
|
|
1517
|
+
}
|
|
1518
|
+
continue;
|
|
1519
|
+
}
|
|
1520
|
+
if (this.inConditionalBranch) {
|
|
1521
|
+
this.goUnknownVars.add(scopedName);
|
|
1522
|
+
this.symbols.delete(scopedName);
|
|
1523
|
+
continue;
|
|
1524
|
+
}
|
|
1525
|
+
this.tainted.delete(scopedName);
|
|
1526
|
+
this.definitionNodes.delete(scopedName);
|
|
1527
|
+
this.symbols.set(scopedName, this.evaluateExpression(valueNode));
|
|
1528
|
+
this.sanitizedVars.delete(scopedName);
|
|
1529
|
+
this.sanitizedVarUncovered.delete(scopedName);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1370
1533
|
handleGoAssignment(node) {
|
|
1371
1534
|
const left = node.childForFieldName('left');
|
|
1372
1535
|
const right = node.childForFieldName('right');
|
|
@@ -1477,6 +1640,12 @@ export class ConstantPropagator {
|
|
|
1477
1640
|
this.sanitizedVars.delete(scopedName); // No longer sanitized if receiving tainted value
|
|
1478
1641
|
this.sanitizedVarUncovered.delete(scopedName);
|
|
1479
1642
|
this.definitionNodes.set(scopedName, right);
|
|
1643
|
+
if (this.isIteratorNextExpression(right)) {
|
|
1644
|
+
this.taintedFromIteratorNext.add(scopedName);
|
|
1645
|
+
if (!varName.includes('.')) {
|
|
1646
|
+
this.taintedFromIteratorNext.add(varName);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1480
1649
|
this.symbols.set(scopedName, createUnknown(line));
|
|
1481
1650
|
// Track constructor field assignments: this.field = taintedParam
|
|
1482
1651
|
if (this.inConstructor && varName.startsWith('this.')) {
|
|
@@ -2319,6 +2488,31 @@ export class ConstantPropagator {
|
|
|
2319
2488
|
cache.set(rootId, false);
|
|
2320
2489
|
return false;
|
|
2321
2490
|
}
|
|
2491
|
+
/** Is an argument of `call` an inline sanitizer call over tainted input (itself possibly nested)? */
|
|
2492
|
+
argIsSanitizedTaint(call) {
|
|
2493
|
+
const argsNode = call.childForFieldName('arguments');
|
|
2494
|
+
if (!argsNode)
|
|
2495
|
+
return false;
|
|
2496
|
+
for (const arg of argsNode.children) {
|
|
2497
|
+
if (arg.type !== 'method_invocation')
|
|
2498
|
+
continue;
|
|
2499
|
+
const name = arg.childForFieldName('name');
|
|
2500
|
+
if (!name || !SANITIZER_METHODS.has(getNodeText(name, this.source)))
|
|
2501
|
+
continue;
|
|
2502
|
+
const inner = arg.childForFieldName('arguments');
|
|
2503
|
+
if (!inner)
|
|
2504
|
+
continue;
|
|
2505
|
+
for (const a of inner.children) {
|
|
2506
|
+
if (a.type === '(' || a.type === ')' || a.type === ',')
|
|
2507
|
+
continue;
|
|
2508
|
+
if (this.isTaintedExpression(a))
|
|
2509
|
+
return true;
|
|
2510
|
+
}
|
|
2511
|
+
if (this.argIsSanitizedTaint(arg))
|
|
2512
|
+
return true;
|
|
2513
|
+
}
|
|
2514
|
+
return false;
|
|
2515
|
+
}
|
|
2322
2516
|
isTaintedExpressionStep(node) {
|
|
2323
2517
|
const text = getNodeText(node, this.source);
|
|
2324
2518
|
if (node.type === 'method_invocation') {
|
|
@@ -2326,6 +2520,12 @@ export class ConstantPropagator {
|
|
|
2326
2520
|
const objectNode = node.childForFieldName('object');
|
|
2327
2521
|
if (nameNode) {
|
|
2328
2522
|
const methodName = getNodeText(nameNode, this.source);
|
|
2523
|
+
// An anti-sanitizer undoing an inline sanitizer is a round trip
|
|
2524
|
+
// (`Base64.getDecoder().decode(Base64.getEncoder().encode(param.getBytes()))`):
|
|
2525
|
+
// the result carries the sanitizer's tainted input (#976).
|
|
2526
|
+
if (ANTI_SANITIZER_METHODS.has(methodName) && this.argIsSanitizedTaint(node)) {
|
|
2527
|
+
return true;
|
|
2528
|
+
}
|
|
2329
2529
|
if (SANITIZER_METHODS.has(methodName) && !this.localNonSanitizing.has(methodName)) {
|
|
2330
2530
|
return false;
|
|
2331
2531
|
}
|
|
@@ -2620,16 +2820,20 @@ export class ConstantPropagator {
|
|
|
2620
2820
|
}
|
|
2621
2821
|
}
|
|
2622
2822
|
if (node.type === 'identifier') {
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2823
|
+
let isTainted = false;
|
|
2824
|
+
let boundName = null;
|
|
2825
|
+
for (const candidate of this.getScopedNameCandidates(text)) {
|
|
2826
|
+
if (this.tainted.has(candidate)) {
|
|
2827
|
+
isTainted = true;
|
|
2828
|
+
boundName = candidate;
|
|
2829
|
+
break;
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
if (isTainted && boundName) {
|
|
2629
2833
|
// Check if this taint is excluded by correlated predicate analysis
|
|
2630
2834
|
// If the variable was tainted under condition C, and we're now under !C,
|
|
2631
2835
|
// the taint doesn't apply in this context
|
|
2632
|
-
if (this.isExcludedByCondition(
|
|
2836
|
+
if (this.isExcludedByCondition(boundName) || this.isExcludedByCondition(text)) {
|
|
2633
2837
|
return false;
|
|
2634
2838
|
}
|
|
2635
2839
|
}
|