circle-ir 4.16.1 → 4.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +11 -0
- package/dist/analysis/constant-propagation/propagator.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/propagator.js +125 -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/passes/cross-file-pass.d.ts.map +1 -1
- package/dist/analysis/passes/cross-file-pass.js +3 -1
- package/dist/analysis/passes/cross-file-pass.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +22 -0
- 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 +17 -1
- package/dist/analysis/passes/param-source-flow-gate-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 +167 -11
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- 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/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +1 -0
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/browser/circle-ir.js +427 -26
- package/dist/core/circle-ir-core.cjs +149 -17
- package/dist/core/circle-ir-core.js +149 -17
- package/dist/core/extractors/types.js +53 -9
- package/dist/core/extractors/types.js.map +1 -1
- package/dist/navigation/navigation-index.d.ts +40 -1
- package/dist/navigation/navigation-index.d.ts.map +1 -1
- package/dist/navigation/navigation-index.js +300 -45
- package/dist/navigation/navigation-index.js.map +1 -1
- package/dist/resolution/cross-file.d.ts +9 -0
- package/dist/resolution/cross-file.d.ts.map +1 -1
- package/dist/resolution/cross-file.js +134 -0
- package/dist/resolution/cross-file.js.map +1 -1
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -4709,14 +4709,43 @@ function extractJavaTypes(tree, cache, options) {
|
|
|
4709
4709
|
);
|
|
4710
4710
|
const path = node ? enclosingTypePath(node) : [];
|
|
4711
4711
|
if (path.length > 0) t.enclosing_type = path.join(".");
|
|
4712
|
-
const
|
|
4713
|
-
if (
|
|
4714
|
-
|
|
4715
|
-
|
|
4712
|
+
const tp = typeParameters(node);
|
|
4713
|
+
if (tp.names.length > 0) t.type_parameters = tp.names;
|
|
4714
|
+
if (Object.keys(tp.bounds).length > 0) t.type_parameter_bounds = tp.bounds;
|
|
4715
|
+
}
|
|
4716
|
+
const allMethods = /* @__PURE__ */ new Map();
|
|
4717
|
+
for (const t of types) {
|
|
4718
|
+
for (const m of t.methods) allMethods.set(`${m.start_line}:${m.name}`, m);
|
|
4719
|
+
}
|
|
4720
|
+
for (const kind of ["method_declaration", "constructor_declaration"]) {
|
|
4721
|
+
for (const node of getNodesFromCache(tree.rootNode, kind, cache)) {
|
|
4722
|
+
const name2 = getIdentifier(node, "name");
|
|
4723
|
+
const info2 = name2 ? allMethods.get(`${node.startPosition.row + 1}:${name2}`) : void 0;
|
|
4724
|
+
if (!info2) continue;
|
|
4725
|
+
const tp = typeParameters(node);
|
|
4726
|
+
if (tp.names.length > 0) info2.type_parameters = tp.names;
|
|
4727
|
+
if (Object.keys(tp.bounds).length > 0) info2.type_parameter_bounds = tp.bounds;
|
|
4716
4728
|
}
|
|
4717
4729
|
}
|
|
4718
4730
|
return types;
|
|
4719
4731
|
}
|
|
4732
|
+
function typeParameters(node) {
|
|
4733
|
+
const names = [];
|
|
4734
|
+
const bounds = {};
|
|
4735
|
+
const list = node ? node.childForFieldName("type_parameters") ?? node.namedChildren.find((c) => c?.type === "type_parameters") ?? null : null;
|
|
4736
|
+
if (!list) return { names, bounds };
|
|
4737
|
+
for (const c of list.namedChildren) {
|
|
4738
|
+
if (c?.type !== "type_parameter") continue;
|
|
4739
|
+
const id = c.namedChildren.find((x) => x?.type === "type_identifier" || x?.type === "identifier");
|
|
4740
|
+
const name2 = id ? getNodeText(id) : "";
|
|
4741
|
+
if (!name2) continue;
|
|
4742
|
+
names.push(name2);
|
|
4743
|
+
const bound = c.namedChildren.find((x) => x?.type === "type_bound");
|
|
4744
|
+
const first = bound?.namedChildren.find((x) => x !== null && x.type !== "annotation" && x.type !== "marker_annotation");
|
|
4745
|
+
if (first) bounds[name2] = getNodeText(first);
|
|
4746
|
+
}
|
|
4747
|
+
return { names, bounds };
|
|
4748
|
+
}
|
|
4720
4749
|
function recordComponents(node) {
|
|
4721
4750
|
const out2 = [];
|
|
4722
4751
|
const params = node.childForFieldName("parameters");
|
|
@@ -16490,6 +16519,9 @@ function isInterproceduralTaintableType(typeName, language) {
|
|
|
16490
16519
|
"IEnumerable",
|
|
16491
16520
|
"ICollection",
|
|
16492
16521
|
"IList",
|
|
16522
|
+
"List",
|
|
16523
|
+
"Hashtable",
|
|
16524
|
+
"ArrayList",
|
|
16493
16525
|
"Dictionary",
|
|
16494
16526
|
"IDictionary",
|
|
16495
16527
|
"IReadOnlyList",
|
|
@@ -19793,6 +19825,11 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19793
19825
|
taintedArrayElements = /* @__PURE__ */ new Map();
|
|
19794
19826
|
// Track current method name for scoping local variables
|
|
19795
19827
|
currentMethod = null;
|
|
19828
|
+
/** Start lines of nested `{ ... }` blocks — disambiguate same-named locals (#476 Collections10). */
|
|
19829
|
+
blockScopeStack = [];
|
|
19830
|
+
lineBlockScopes = /* @__PURE__ */ new Map();
|
|
19831
|
+
/** Skip block-scope push on the method/constructor body `{ ... }` (#476). */
|
|
19832
|
+
skipMethodBodyBlockScope = false;
|
|
19796
19833
|
// Track conditional taints: which variables were tainted under which conditions
|
|
19797
19834
|
// Maps condition expression string → set of variables tainted under that condition
|
|
19798
19835
|
conditionalTaints = /* @__PURE__ */ new Map();
|
|
@@ -19806,6 +19843,8 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19806
19843
|
inSynchronizedBlock = false;
|
|
19807
19844
|
// Track iterator sources: iterator variable name → collection name it was created from
|
|
19808
19845
|
iteratorSources = /* @__PURE__ */ new Map();
|
|
19846
|
+
/** Vars assigned from `iter.next()` — do not retro-clear on later iterator rebind (#476). */
|
|
19847
|
+
taintedFromIteratorNext = /* @__PURE__ */ new Set();
|
|
19809
19848
|
// Track class field names (declared at class level, not local variables)
|
|
19810
19849
|
classFields = /* @__PURE__ */ new Set();
|
|
19811
19850
|
// Track tainted method parameters for inter-procedural analysis
|
|
@@ -19863,12 +19902,16 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19863
19902
|
this.sanitizedVars.clear();
|
|
19864
19903
|
this.sanitizedVarUncovered.clear();
|
|
19865
19904
|
this.currentMethod = null;
|
|
19905
|
+
this.blockScopeStack = [];
|
|
19906
|
+
this.lineBlockScopes.clear();
|
|
19907
|
+
this.skipMethodBodyBlockScope = false;
|
|
19866
19908
|
this.conditionalTaints.clear();
|
|
19867
19909
|
this.conditionStack = [];
|
|
19868
19910
|
this.lineConditions.clear();
|
|
19869
19911
|
this.synchronizedLines.clear();
|
|
19870
19912
|
this.inSynchronizedBlock = false;
|
|
19871
19913
|
this.iteratorSources.clear();
|
|
19914
|
+
this.taintedFromIteratorNext.clear();
|
|
19872
19915
|
this.classFields.clear();
|
|
19873
19916
|
this.instanceFieldTaint.clear();
|
|
19874
19917
|
this.currentClassName = null;
|
|
@@ -19893,6 +19936,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19893
19936
|
const resultSymbols = new Map(this.symbols);
|
|
19894
19937
|
for (const name2 of this.tainted) {
|
|
19895
19938
|
if (name2.includes(":")) {
|
|
19939
|
+
if (/^[^:]+:b\d+:/.test(name2)) {
|
|
19940
|
+
continue;
|
|
19941
|
+
}
|
|
19896
19942
|
const unscoped = name2.substring(name2.indexOf(":") + 1);
|
|
19897
19943
|
resultTainted.add(unscoped);
|
|
19898
19944
|
}
|
|
@@ -19907,6 +19953,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19907
19953
|
for (const [name2, value] of this.symbols) {
|
|
19908
19954
|
if (this.goScopedNames.has(name2)) continue;
|
|
19909
19955
|
if (name2.includes(":")) {
|
|
19956
|
+
if (/^[^:]+:b\d+:/.test(name2)) {
|
|
19957
|
+
continue;
|
|
19958
|
+
}
|
|
19910
19959
|
const unscoped = name2.substring(name2.indexOf(":") + 1);
|
|
19911
19960
|
if (!resultSymbols.has(unscoped)) {
|
|
19912
19961
|
resultSymbols.set(unscoped, value);
|
|
@@ -19916,6 +19965,7 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19916
19965
|
return {
|
|
19917
19966
|
symbols: resultSymbols,
|
|
19918
19967
|
tainted: resultTainted,
|
|
19968
|
+
lineBlockScopes: new Map(this.lineBlockScopes),
|
|
19919
19969
|
unreachableLines: new Set(this.unreachableLines),
|
|
19920
19970
|
taintedCollections: new Map(this.taintedCollections),
|
|
19921
19971
|
taintedArrayElements: new Map(this.taintedArrayElements),
|
|
@@ -20459,6 +20509,18 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20459
20509
|
// ===========================================================================
|
|
20460
20510
|
// Taint Refinement
|
|
20461
20511
|
// ===========================================================================
|
|
20512
|
+
/** True when `node` is `iter.next()` (possibly under a cast). */
|
|
20513
|
+
isIteratorNextExpression(node) {
|
|
20514
|
+
const text = getNodeText2(node, this.source);
|
|
20515
|
+
if (/\.\s*next\s*\(/.test(text)) return true;
|
|
20516
|
+
let current = node;
|
|
20517
|
+
if (current.type === "cast_expression" || current.type === "parenthesized_expression") {
|
|
20518
|
+
current = current.childForFieldName("operand") ?? current.childForFieldName("expression");
|
|
20519
|
+
}
|
|
20520
|
+
if (!current || current.type !== "method_invocation") return false;
|
|
20521
|
+
const nameNode = current.childForFieldName("name");
|
|
20522
|
+
return nameNode ? getNodeText2(nameNode, this.source) === "next" : false;
|
|
20523
|
+
}
|
|
20462
20524
|
refineTaintFromConstants() {
|
|
20463
20525
|
let changed = true;
|
|
20464
20526
|
let iterations = 0;
|
|
@@ -20468,6 +20530,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20468
20530
|
iterations++;
|
|
20469
20531
|
const toRemove = [];
|
|
20470
20532
|
for (const varName of this.tainted) {
|
|
20533
|
+
if (this.taintedFromIteratorNext.has(varName)) {
|
|
20534
|
+
continue;
|
|
20535
|
+
}
|
|
20471
20536
|
const symbol = this.symbols.get(varName);
|
|
20472
20537
|
if (symbol && symbol.type !== "unknown") {
|
|
20473
20538
|
toRemove.push(varName);
|
|
@@ -20516,6 +20581,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20516
20581
|
if (this.unreachableLines.has(line)) {
|
|
20517
20582
|
return true;
|
|
20518
20583
|
}
|
|
20584
|
+
if (this.blockScopeStack.length > 0) {
|
|
20585
|
+
this.lineBlockScopes.set(line, this.blockScopeStack[this.blockScopeStack.length - 1]);
|
|
20586
|
+
}
|
|
20519
20587
|
if (this.conditionStack.length > 0 && !this.lineConditions.has(line)) {
|
|
20520
20588
|
this.lineConditions.set(line, this.conditionStack[this.conditionStack.length - 1]);
|
|
20521
20589
|
}
|
|
@@ -20547,6 +20615,15 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20547
20615
|
case "if_statement":
|
|
20548
20616
|
this.handleIfStatement(node);
|
|
20549
20617
|
return true;
|
|
20618
|
+
case "block": {
|
|
20619
|
+
if (this.skipMethodBodyBlockScope) {
|
|
20620
|
+
this.skipMethodBodyBlockScope = false;
|
|
20621
|
+
}
|
|
20622
|
+
for (const child of node.children) {
|
|
20623
|
+
this.visit(child);
|
|
20624
|
+
}
|
|
20625
|
+
return true;
|
|
20626
|
+
}
|
|
20550
20627
|
case "switch_expression":
|
|
20551
20628
|
case "switch_statement":
|
|
20552
20629
|
this.handleSwitch(node);
|
|
@@ -20632,7 +20709,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20632
20709
|
}
|
|
20633
20710
|
const body2 = node.childForFieldName("body");
|
|
20634
20711
|
if (body2) {
|
|
20712
|
+
this.skipMethodBodyBlockScope = true;
|
|
20635
20713
|
this.visit(body2);
|
|
20714
|
+
this.skipMethodBodyBlockScope = false;
|
|
20636
20715
|
}
|
|
20637
20716
|
this.currentMethod = prevMethod;
|
|
20638
20717
|
this.inConstructor = prevInConstructor;
|
|
@@ -20646,10 +20725,31 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20646
20725
|
if (varName.includes(".") || varName.includes(":")) {
|
|
20647
20726
|
return varName;
|
|
20648
20727
|
}
|
|
20728
|
+
const blockId = this.blockScopeStack.length > 0 ? this.blockScopeStack[this.blockScopeStack.length - 1] : void 0;
|
|
20649
20729
|
if (this.currentMethod) {
|
|
20650
|
-
return `${this.currentMethod}:${varName}`;
|
|
20730
|
+
return blockId !== void 0 ? `${this.currentMethod}:b${blockId}:${varName}` : `${this.currentMethod}:${varName}`;
|
|
20651
20731
|
}
|
|
20652
|
-
return varName;
|
|
20732
|
+
return blockId !== void 0 ? `b${blockId}:${varName}` : varName;
|
|
20733
|
+
}
|
|
20734
|
+
/** Innermost block → method → unscoped, for reads that may bind to an outer declaration. */
|
|
20735
|
+
getScopedNameCandidates(varName) {
|
|
20736
|
+
if (varName.includes(".") || varName.includes(":")) {
|
|
20737
|
+
return [varName];
|
|
20738
|
+
}
|
|
20739
|
+
const out2 = [];
|
|
20740
|
+
for (let i2 = this.blockScopeStack.length - 1; i2 >= 0; i2--) {
|
|
20741
|
+
const blockId = this.blockScopeStack[i2];
|
|
20742
|
+
if (this.currentMethod) {
|
|
20743
|
+
out2.push(`${this.currentMethod}:b${blockId}:${varName}`);
|
|
20744
|
+
} else {
|
|
20745
|
+
out2.push(`b${blockId}:${varName}`);
|
|
20746
|
+
}
|
|
20747
|
+
}
|
|
20748
|
+
if (this.currentMethod) {
|
|
20749
|
+
out2.push(`${this.currentMethod}:${varName}`);
|
|
20750
|
+
}
|
|
20751
|
+
out2.push(varName);
|
|
20752
|
+
return out2;
|
|
20653
20753
|
}
|
|
20654
20754
|
/**
|
|
20655
20755
|
* Look up a variable value, checking both scoped and unscoped names.
|
|
@@ -20657,11 +20757,12 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20657
20757
|
* either local (scoped) or global (unscoped, like class fields).
|
|
20658
20758
|
*/
|
|
20659
20759
|
lookupSymbol(varName) {
|
|
20660
|
-
if (
|
|
20661
|
-
const scopedName
|
|
20662
|
-
|
|
20663
|
-
|
|
20664
|
-
|
|
20760
|
+
if (!varName.includes(".") && !varName.includes(":")) {
|
|
20761
|
+
for (const scopedName of this.getScopedNameCandidates(varName)) {
|
|
20762
|
+
const scopedValue = this.symbols.get(scopedName);
|
|
20763
|
+
if (scopedValue) {
|
|
20764
|
+
return scopedValue;
|
|
20765
|
+
}
|
|
20665
20766
|
}
|
|
20666
20767
|
}
|
|
20667
20768
|
return this.symbols.get(varName);
|
|
@@ -20713,8 +20814,22 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20713
20814
|
this.trackIteratorsInNode(initNode);
|
|
20714
20815
|
}
|
|
20715
20816
|
}
|
|
20716
|
-
|
|
20717
|
-
|
|
20817
|
+
if (node.type === "while_statement" || node.type === "do_statement") {
|
|
20818
|
+
const body2 = node.childForFieldName("body");
|
|
20819
|
+
for (const child of node.children) {
|
|
20820
|
+
if (child !== body2) this.visit(child);
|
|
20821
|
+
}
|
|
20822
|
+
if (body2?.type === "block") {
|
|
20823
|
+
this.blockScopeStack.push(getNodeLine(body2));
|
|
20824
|
+
this.visit(body2);
|
|
20825
|
+
this.blockScopeStack.pop();
|
|
20826
|
+
} else if (body2) {
|
|
20827
|
+
this.visit(body2);
|
|
20828
|
+
}
|
|
20829
|
+
} else {
|
|
20830
|
+
for (const child of node.children) {
|
|
20831
|
+
this.visit(child);
|
|
20832
|
+
}
|
|
20718
20833
|
}
|
|
20719
20834
|
for (const varName of loopVarNames) {
|
|
20720
20835
|
this.symbols.set(varName, createUnknown(getNodeLine(node)));
|
|
@@ -20856,6 +20971,10 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20856
20971
|
this.sanitizedVars.delete(scopedName);
|
|
20857
20972
|
this.sanitizedVarUncovered.delete(scopedName);
|
|
20858
20973
|
this.definitionNodes.set(scopedName, valueNode);
|
|
20974
|
+
if (this.isIteratorNextExpression(valueNode)) {
|
|
20975
|
+
this.taintedFromIteratorNext.add(scopedName);
|
|
20976
|
+
this.taintedFromIteratorNext.add(varName);
|
|
20977
|
+
}
|
|
20859
20978
|
this.symbols.set(scopedName, createUnknown(line));
|
|
20860
20979
|
} else {
|
|
20861
20980
|
if (this.inConditionalBranch) {
|
|
@@ -20983,6 +21102,12 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20983
21102
|
this.sanitizedVars.delete(scopedName);
|
|
20984
21103
|
this.sanitizedVarUncovered.delete(scopedName);
|
|
20985
21104
|
this.definitionNodes.set(scopedName, right);
|
|
21105
|
+
if (this.isIteratorNextExpression(right)) {
|
|
21106
|
+
this.taintedFromIteratorNext.add(scopedName);
|
|
21107
|
+
if (!varName.includes(".")) {
|
|
21108
|
+
this.taintedFromIteratorNext.add(varName);
|
|
21109
|
+
}
|
|
21110
|
+
}
|
|
20986
21111
|
this.symbols.set(scopedName, createUnknown(line));
|
|
20987
21112
|
if (this.inConstructor && varName.startsWith("this.")) {
|
|
20988
21113
|
const fieldName = varName.substring(5);
|
|
@@ -21973,10 +22098,17 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
21973
22098
|
}
|
|
21974
22099
|
}
|
|
21975
22100
|
if (node.type === "identifier") {
|
|
21976
|
-
|
|
21977
|
-
|
|
21978
|
-
|
|
21979
|
-
if (this.
|
|
22101
|
+
let isTainted = false;
|
|
22102
|
+
let boundName = null;
|
|
22103
|
+
for (const candidate of this.getScopedNameCandidates(text)) {
|
|
22104
|
+
if (this.tainted.has(candidate)) {
|
|
22105
|
+
isTainted = true;
|
|
22106
|
+
boundName = candidate;
|
|
22107
|
+
break;
|
|
22108
|
+
}
|
|
22109
|
+
}
|
|
22110
|
+
if (isTainted && boundName) {
|
|
22111
|
+
if (this.isExcludedByCondition(boundName) || this.isExcludedByCondition(text)) {
|
|
21980
22112
|
return false;
|
|
21981
22113
|
}
|
|
21982
22114
|
}
|
|
@@ -4643,14 +4643,43 @@ function extractJavaTypes(tree, cache, options) {
|
|
|
4643
4643
|
);
|
|
4644
4644
|
const path = node ? enclosingTypePath(node) : [];
|
|
4645
4645
|
if (path.length > 0) t.enclosing_type = path.join(".");
|
|
4646
|
-
const
|
|
4647
|
-
if (
|
|
4648
|
-
|
|
4649
|
-
|
|
4646
|
+
const tp = typeParameters(node);
|
|
4647
|
+
if (tp.names.length > 0) t.type_parameters = tp.names;
|
|
4648
|
+
if (Object.keys(tp.bounds).length > 0) t.type_parameter_bounds = tp.bounds;
|
|
4649
|
+
}
|
|
4650
|
+
const allMethods = /* @__PURE__ */ new Map();
|
|
4651
|
+
for (const t of types) {
|
|
4652
|
+
for (const m of t.methods) allMethods.set(`${m.start_line}:${m.name}`, m);
|
|
4653
|
+
}
|
|
4654
|
+
for (const kind of ["method_declaration", "constructor_declaration"]) {
|
|
4655
|
+
for (const node of getNodesFromCache(tree.rootNode, kind, cache)) {
|
|
4656
|
+
const name2 = getIdentifier(node, "name");
|
|
4657
|
+
const info2 = name2 ? allMethods.get(`${node.startPosition.row + 1}:${name2}`) : void 0;
|
|
4658
|
+
if (!info2) continue;
|
|
4659
|
+
const tp = typeParameters(node);
|
|
4660
|
+
if (tp.names.length > 0) info2.type_parameters = tp.names;
|
|
4661
|
+
if (Object.keys(tp.bounds).length > 0) info2.type_parameter_bounds = tp.bounds;
|
|
4650
4662
|
}
|
|
4651
4663
|
}
|
|
4652
4664
|
return types;
|
|
4653
4665
|
}
|
|
4666
|
+
function typeParameters(node) {
|
|
4667
|
+
const names = [];
|
|
4668
|
+
const bounds = {};
|
|
4669
|
+
const list = node ? node.childForFieldName("type_parameters") ?? node.namedChildren.find((c) => c?.type === "type_parameters") ?? null : null;
|
|
4670
|
+
if (!list) return { names, bounds };
|
|
4671
|
+
for (const c of list.namedChildren) {
|
|
4672
|
+
if (c?.type !== "type_parameter") continue;
|
|
4673
|
+
const id = c.namedChildren.find((x) => x?.type === "type_identifier" || x?.type === "identifier");
|
|
4674
|
+
const name2 = id ? getNodeText(id) : "";
|
|
4675
|
+
if (!name2) continue;
|
|
4676
|
+
names.push(name2);
|
|
4677
|
+
const bound = c.namedChildren.find((x) => x?.type === "type_bound");
|
|
4678
|
+
const first = bound?.namedChildren.find((x) => x !== null && x.type !== "annotation" && x.type !== "marker_annotation");
|
|
4679
|
+
if (first) bounds[name2] = getNodeText(first);
|
|
4680
|
+
}
|
|
4681
|
+
return { names, bounds };
|
|
4682
|
+
}
|
|
4654
4683
|
function recordComponents(node) {
|
|
4655
4684
|
const out2 = [];
|
|
4656
4685
|
const params = node.childForFieldName("parameters");
|
|
@@ -16424,6 +16453,9 @@ function isInterproceduralTaintableType(typeName, language) {
|
|
|
16424
16453
|
"IEnumerable",
|
|
16425
16454
|
"ICollection",
|
|
16426
16455
|
"IList",
|
|
16456
|
+
"List",
|
|
16457
|
+
"Hashtable",
|
|
16458
|
+
"ArrayList",
|
|
16427
16459
|
"Dictionary",
|
|
16428
16460
|
"IDictionary",
|
|
16429
16461
|
"IReadOnlyList",
|
|
@@ -19727,6 +19759,11 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19727
19759
|
taintedArrayElements = /* @__PURE__ */ new Map();
|
|
19728
19760
|
// Track current method name for scoping local variables
|
|
19729
19761
|
currentMethod = null;
|
|
19762
|
+
/** Start lines of nested `{ ... }` blocks — disambiguate same-named locals (#476 Collections10). */
|
|
19763
|
+
blockScopeStack = [];
|
|
19764
|
+
lineBlockScopes = /* @__PURE__ */ new Map();
|
|
19765
|
+
/** Skip block-scope push on the method/constructor body `{ ... }` (#476). */
|
|
19766
|
+
skipMethodBodyBlockScope = false;
|
|
19730
19767
|
// Track conditional taints: which variables were tainted under which conditions
|
|
19731
19768
|
// Maps condition expression string → set of variables tainted under that condition
|
|
19732
19769
|
conditionalTaints = /* @__PURE__ */ new Map();
|
|
@@ -19740,6 +19777,8 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19740
19777
|
inSynchronizedBlock = false;
|
|
19741
19778
|
// Track iterator sources: iterator variable name → collection name it was created from
|
|
19742
19779
|
iteratorSources = /* @__PURE__ */ new Map();
|
|
19780
|
+
/** Vars assigned from `iter.next()` — do not retro-clear on later iterator rebind (#476). */
|
|
19781
|
+
taintedFromIteratorNext = /* @__PURE__ */ new Set();
|
|
19743
19782
|
// Track class field names (declared at class level, not local variables)
|
|
19744
19783
|
classFields = /* @__PURE__ */ new Set();
|
|
19745
19784
|
// Track tainted method parameters for inter-procedural analysis
|
|
@@ -19797,12 +19836,16 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19797
19836
|
this.sanitizedVars.clear();
|
|
19798
19837
|
this.sanitizedVarUncovered.clear();
|
|
19799
19838
|
this.currentMethod = null;
|
|
19839
|
+
this.blockScopeStack = [];
|
|
19840
|
+
this.lineBlockScopes.clear();
|
|
19841
|
+
this.skipMethodBodyBlockScope = false;
|
|
19800
19842
|
this.conditionalTaints.clear();
|
|
19801
19843
|
this.conditionStack = [];
|
|
19802
19844
|
this.lineConditions.clear();
|
|
19803
19845
|
this.synchronizedLines.clear();
|
|
19804
19846
|
this.inSynchronizedBlock = false;
|
|
19805
19847
|
this.iteratorSources.clear();
|
|
19848
|
+
this.taintedFromIteratorNext.clear();
|
|
19806
19849
|
this.classFields.clear();
|
|
19807
19850
|
this.instanceFieldTaint.clear();
|
|
19808
19851
|
this.currentClassName = null;
|
|
@@ -19827,6 +19870,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19827
19870
|
const resultSymbols = new Map(this.symbols);
|
|
19828
19871
|
for (const name2 of this.tainted) {
|
|
19829
19872
|
if (name2.includes(":")) {
|
|
19873
|
+
if (/^[^:]+:b\d+:/.test(name2)) {
|
|
19874
|
+
continue;
|
|
19875
|
+
}
|
|
19830
19876
|
const unscoped = name2.substring(name2.indexOf(":") + 1);
|
|
19831
19877
|
resultTainted.add(unscoped);
|
|
19832
19878
|
}
|
|
@@ -19841,6 +19887,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19841
19887
|
for (const [name2, value] of this.symbols) {
|
|
19842
19888
|
if (this.goScopedNames.has(name2)) continue;
|
|
19843
19889
|
if (name2.includes(":")) {
|
|
19890
|
+
if (/^[^:]+:b\d+:/.test(name2)) {
|
|
19891
|
+
continue;
|
|
19892
|
+
}
|
|
19844
19893
|
const unscoped = name2.substring(name2.indexOf(":") + 1);
|
|
19845
19894
|
if (!resultSymbols.has(unscoped)) {
|
|
19846
19895
|
resultSymbols.set(unscoped, value);
|
|
@@ -19850,6 +19899,7 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19850
19899
|
return {
|
|
19851
19900
|
symbols: resultSymbols,
|
|
19852
19901
|
tainted: resultTainted,
|
|
19902
|
+
lineBlockScopes: new Map(this.lineBlockScopes),
|
|
19853
19903
|
unreachableLines: new Set(this.unreachableLines),
|
|
19854
19904
|
taintedCollections: new Map(this.taintedCollections),
|
|
19855
19905
|
taintedArrayElements: new Map(this.taintedArrayElements),
|
|
@@ -20393,6 +20443,18 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20393
20443
|
// ===========================================================================
|
|
20394
20444
|
// Taint Refinement
|
|
20395
20445
|
// ===========================================================================
|
|
20446
|
+
/** True when `node` is `iter.next()` (possibly under a cast). */
|
|
20447
|
+
isIteratorNextExpression(node) {
|
|
20448
|
+
const text = getNodeText2(node, this.source);
|
|
20449
|
+
if (/\.\s*next\s*\(/.test(text)) return true;
|
|
20450
|
+
let current = node;
|
|
20451
|
+
if (current.type === "cast_expression" || current.type === "parenthesized_expression") {
|
|
20452
|
+
current = current.childForFieldName("operand") ?? current.childForFieldName("expression");
|
|
20453
|
+
}
|
|
20454
|
+
if (!current || current.type !== "method_invocation") return false;
|
|
20455
|
+
const nameNode = current.childForFieldName("name");
|
|
20456
|
+
return nameNode ? getNodeText2(nameNode, this.source) === "next" : false;
|
|
20457
|
+
}
|
|
20396
20458
|
refineTaintFromConstants() {
|
|
20397
20459
|
let changed = true;
|
|
20398
20460
|
let iterations = 0;
|
|
@@ -20402,6 +20464,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20402
20464
|
iterations++;
|
|
20403
20465
|
const toRemove = [];
|
|
20404
20466
|
for (const varName of this.tainted) {
|
|
20467
|
+
if (this.taintedFromIteratorNext.has(varName)) {
|
|
20468
|
+
continue;
|
|
20469
|
+
}
|
|
20405
20470
|
const symbol = this.symbols.get(varName);
|
|
20406
20471
|
if (symbol && symbol.type !== "unknown") {
|
|
20407
20472
|
toRemove.push(varName);
|
|
@@ -20450,6 +20515,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20450
20515
|
if (this.unreachableLines.has(line)) {
|
|
20451
20516
|
return true;
|
|
20452
20517
|
}
|
|
20518
|
+
if (this.blockScopeStack.length > 0) {
|
|
20519
|
+
this.lineBlockScopes.set(line, this.blockScopeStack[this.blockScopeStack.length - 1]);
|
|
20520
|
+
}
|
|
20453
20521
|
if (this.conditionStack.length > 0 && !this.lineConditions.has(line)) {
|
|
20454
20522
|
this.lineConditions.set(line, this.conditionStack[this.conditionStack.length - 1]);
|
|
20455
20523
|
}
|
|
@@ -20481,6 +20549,15 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20481
20549
|
case "if_statement":
|
|
20482
20550
|
this.handleIfStatement(node);
|
|
20483
20551
|
return true;
|
|
20552
|
+
case "block": {
|
|
20553
|
+
if (this.skipMethodBodyBlockScope) {
|
|
20554
|
+
this.skipMethodBodyBlockScope = false;
|
|
20555
|
+
}
|
|
20556
|
+
for (const child of node.children) {
|
|
20557
|
+
this.visit(child);
|
|
20558
|
+
}
|
|
20559
|
+
return true;
|
|
20560
|
+
}
|
|
20484
20561
|
case "switch_expression":
|
|
20485
20562
|
case "switch_statement":
|
|
20486
20563
|
this.handleSwitch(node);
|
|
@@ -20566,7 +20643,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20566
20643
|
}
|
|
20567
20644
|
const body2 = node.childForFieldName("body");
|
|
20568
20645
|
if (body2) {
|
|
20646
|
+
this.skipMethodBodyBlockScope = true;
|
|
20569
20647
|
this.visit(body2);
|
|
20648
|
+
this.skipMethodBodyBlockScope = false;
|
|
20570
20649
|
}
|
|
20571
20650
|
this.currentMethod = prevMethod;
|
|
20572
20651
|
this.inConstructor = prevInConstructor;
|
|
@@ -20580,10 +20659,31 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20580
20659
|
if (varName.includes(".") || varName.includes(":")) {
|
|
20581
20660
|
return varName;
|
|
20582
20661
|
}
|
|
20662
|
+
const blockId = this.blockScopeStack.length > 0 ? this.blockScopeStack[this.blockScopeStack.length - 1] : void 0;
|
|
20583
20663
|
if (this.currentMethod) {
|
|
20584
|
-
return `${this.currentMethod}:${varName}`;
|
|
20664
|
+
return blockId !== void 0 ? `${this.currentMethod}:b${blockId}:${varName}` : `${this.currentMethod}:${varName}`;
|
|
20585
20665
|
}
|
|
20586
|
-
return varName;
|
|
20666
|
+
return blockId !== void 0 ? `b${blockId}:${varName}` : varName;
|
|
20667
|
+
}
|
|
20668
|
+
/** Innermost block → method → unscoped, for reads that may bind to an outer declaration. */
|
|
20669
|
+
getScopedNameCandidates(varName) {
|
|
20670
|
+
if (varName.includes(".") || varName.includes(":")) {
|
|
20671
|
+
return [varName];
|
|
20672
|
+
}
|
|
20673
|
+
const out2 = [];
|
|
20674
|
+
for (let i2 = this.blockScopeStack.length - 1; i2 >= 0; i2--) {
|
|
20675
|
+
const blockId = this.blockScopeStack[i2];
|
|
20676
|
+
if (this.currentMethod) {
|
|
20677
|
+
out2.push(`${this.currentMethod}:b${blockId}:${varName}`);
|
|
20678
|
+
} else {
|
|
20679
|
+
out2.push(`b${blockId}:${varName}`);
|
|
20680
|
+
}
|
|
20681
|
+
}
|
|
20682
|
+
if (this.currentMethod) {
|
|
20683
|
+
out2.push(`${this.currentMethod}:${varName}`);
|
|
20684
|
+
}
|
|
20685
|
+
out2.push(varName);
|
|
20686
|
+
return out2;
|
|
20587
20687
|
}
|
|
20588
20688
|
/**
|
|
20589
20689
|
* Look up a variable value, checking both scoped and unscoped names.
|
|
@@ -20591,11 +20691,12 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20591
20691
|
* either local (scoped) or global (unscoped, like class fields).
|
|
20592
20692
|
*/
|
|
20593
20693
|
lookupSymbol(varName) {
|
|
20594
|
-
if (
|
|
20595
|
-
const scopedName
|
|
20596
|
-
|
|
20597
|
-
|
|
20598
|
-
|
|
20694
|
+
if (!varName.includes(".") && !varName.includes(":")) {
|
|
20695
|
+
for (const scopedName of this.getScopedNameCandidates(varName)) {
|
|
20696
|
+
const scopedValue = this.symbols.get(scopedName);
|
|
20697
|
+
if (scopedValue) {
|
|
20698
|
+
return scopedValue;
|
|
20699
|
+
}
|
|
20599
20700
|
}
|
|
20600
20701
|
}
|
|
20601
20702
|
return this.symbols.get(varName);
|
|
@@ -20647,8 +20748,22 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20647
20748
|
this.trackIteratorsInNode(initNode);
|
|
20648
20749
|
}
|
|
20649
20750
|
}
|
|
20650
|
-
|
|
20651
|
-
|
|
20751
|
+
if (node.type === "while_statement" || node.type === "do_statement") {
|
|
20752
|
+
const body2 = node.childForFieldName("body");
|
|
20753
|
+
for (const child of node.children) {
|
|
20754
|
+
if (child !== body2) this.visit(child);
|
|
20755
|
+
}
|
|
20756
|
+
if (body2?.type === "block") {
|
|
20757
|
+
this.blockScopeStack.push(getNodeLine(body2));
|
|
20758
|
+
this.visit(body2);
|
|
20759
|
+
this.blockScopeStack.pop();
|
|
20760
|
+
} else if (body2) {
|
|
20761
|
+
this.visit(body2);
|
|
20762
|
+
}
|
|
20763
|
+
} else {
|
|
20764
|
+
for (const child of node.children) {
|
|
20765
|
+
this.visit(child);
|
|
20766
|
+
}
|
|
20652
20767
|
}
|
|
20653
20768
|
for (const varName of loopVarNames) {
|
|
20654
20769
|
this.symbols.set(varName, createUnknown(getNodeLine(node)));
|
|
@@ -20790,6 +20905,10 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20790
20905
|
this.sanitizedVars.delete(scopedName);
|
|
20791
20906
|
this.sanitizedVarUncovered.delete(scopedName);
|
|
20792
20907
|
this.definitionNodes.set(scopedName, valueNode);
|
|
20908
|
+
if (this.isIteratorNextExpression(valueNode)) {
|
|
20909
|
+
this.taintedFromIteratorNext.add(scopedName);
|
|
20910
|
+
this.taintedFromIteratorNext.add(varName);
|
|
20911
|
+
}
|
|
20793
20912
|
this.symbols.set(scopedName, createUnknown(line));
|
|
20794
20913
|
} else {
|
|
20795
20914
|
if (this.inConditionalBranch) {
|
|
@@ -20917,6 +21036,12 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20917
21036
|
this.sanitizedVars.delete(scopedName);
|
|
20918
21037
|
this.sanitizedVarUncovered.delete(scopedName);
|
|
20919
21038
|
this.definitionNodes.set(scopedName, right);
|
|
21039
|
+
if (this.isIteratorNextExpression(right)) {
|
|
21040
|
+
this.taintedFromIteratorNext.add(scopedName);
|
|
21041
|
+
if (!varName.includes(".")) {
|
|
21042
|
+
this.taintedFromIteratorNext.add(varName);
|
|
21043
|
+
}
|
|
21044
|
+
}
|
|
20920
21045
|
this.symbols.set(scopedName, createUnknown(line));
|
|
20921
21046
|
if (this.inConstructor && varName.startsWith("this.")) {
|
|
20922
21047
|
const fieldName = varName.substring(5);
|
|
@@ -21907,10 +22032,17 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
21907
22032
|
}
|
|
21908
22033
|
}
|
|
21909
22034
|
if (node.type === "identifier") {
|
|
21910
|
-
|
|
21911
|
-
|
|
21912
|
-
|
|
21913
|
-
if (this.
|
|
22035
|
+
let isTainted = false;
|
|
22036
|
+
let boundName = null;
|
|
22037
|
+
for (const candidate of this.getScopedNameCandidates(text)) {
|
|
22038
|
+
if (this.tainted.has(candidate)) {
|
|
22039
|
+
isTainted = true;
|
|
22040
|
+
boundName = candidate;
|
|
22041
|
+
break;
|
|
22042
|
+
}
|
|
22043
|
+
}
|
|
22044
|
+
if (isTainted && boundName) {
|
|
22045
|
+
if (this.isExcludedByCondition(boundName) || this.isExcludedByCondition(text)) {
|
|
21914
22046
|
return false;
|
|
21915
22047
|
}
|
|
21916
22048
|
}
|