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
|
@@ -4663,14 +4663,43 @@ function extractJavaTypes(tree, cache, options) {
|
|
|
4663
4663
|
);
|
|
4664
4664
|
const path = node ? enclosingTypePath(node) : [];
|
|
4665
4665
|
if (path.length > 0) t.enclosing_type = path.join(".");
|
|
4666
|
-
const
|
|
4667
|
-
if (
|
|
4668
|
-
|
|
4669
|
-
|
|
4666
|
+
const tp = typeParameters(node);
|
|
4667
|
+
if (tp.names.length > 0) t.type_parameters = tp.names;
|
|
4668
|
+
if (Object.keys(tp.bounds).length > 0) t.type_parameter_bounds = tp.bounds;
|
|
4669
|
+
}
|
|
4670
|
+
const allMethods = /* @__PURE__ */ new Map();
|
|
4671
|
+
for (const t of types) {
|
|
4672
|
+
for (const m of t.methods) allMethods.set(`${m.start_line}:${m.name}`, m);
|
|
4673
|
+
}
|
|
4674
|
+
for (const kind of ["method_declaration", "constructor_declaration"]) {
|
|
4675
|
+
for (const node of getNodesFromCache(tree.rootNode, kind, cache)) {
|
|
4676
|
+
const name2 = getIdentifier(node, "name");
|
|
4677
|
+
const info2 = name2 ? allMethods.get(`${node.startPosition.row + 1}:${name2}`) : void 0;
|
|
4678
|
+
if (!info2) continue;
|
|
4679
|
+
const tp = typeParameters(node);
|
|
4680
|
+
if (tp.names.length > 0) info2.type_parameters = tp.names;
|
|
4681
|
+
if (Object.keys(tp.bounds).length > 0) info2.type_parameter_bounds = tp.bounds;
|
|
4670
4682
|
}
|
|
4671
4683
|
}
|
|
4672
4684
|
return types;
|
|
4673
4685
|
}
|
|
4686
|
+
function typeParameters(node) {
|
|
4687
|
+
const names = [];
|
|
4688
|
+
const bounds = {};
|
|
4689
|
+
const list = node ? node.childForFieldName("type_parameters") ?? node.namedChildren.find((c) => c?.type === "type_parameters") ?? null : null;
|
|
4690
|
+
if (!list) return { names, bounds };
|
|
4691
|
+
for (const c of list.namedChildren) {
|
|
4692
|
+
if (c?.type !== "type_parameter") continue;
|
|
4693
|
+
const id = c.namedChildren.find((x) => x?.type === "type_identifier" || x?.type === "identifier");
|
|
4694
|
+
const name2 = id ? getNodeText(id) : "";
|
|
4695
|
+
if (!name2) continue;
|
|
4696
|
+
names.push(name2);
|
|
4697
|
+
const bound = c.namedChildren.find((x) => x?.type === "type_bound");
|
|
4698
|
+
const first = bound?.namedChildren.find((x) => x !== null && x.type !== "annotation" && x.type !== "marker_annotation");
|
|
4699
|
+
if (first) bounds[name2] = getNodeText(first);
|
|
4700
|
+
}
|
|
4701
|
+
return { names, bounds };
|
|
4702
|
+
}
|
|
4674
4703
|
function recordComponents(node) {
|
|
4675
4704
|
const out2 = [];
|
|
4676
4705
|
const params = node.childForFieldName("parameters");
|
|
@@ -16735,6 +16764,85 @@ function passesMutParam(line, n) {
|
|
|
16735
16764
|
if (!new RegExp(String.raw`(?<![\w.])(?:[A-Za-z_]\w*(?:\s*::\s*[A-Za-z_]\w*)*)\s*\((?:[^)]*,\s*)?${n}\b`).test(line)) return false;
|
|
16736
16765
|
return !new RegExp(String.raw`\b${n}\s*\.\s*\w+\s*\(`).test(line) || new RegExp(String.raw`(?<![\w.])\w+\s*\(\s*${n}\b`).test(line);
|
|
16737
16766
|
}
|
|
16767
|
+
function findRustAllowlistTernarySanitizers(code) {
|
|
16768
|
+
const masked = maskRust(code);
|
|
16769
|
+
const origLines = code.split("\n");
|
|
16770
|
+
const maskedLines = masked.split("\n");
|
|
16771
|
+
const out2 = [];
|
|
16772
|
+
const allowlistVar = (cond) => {
|
|
16773
|
+
const c = cond.trim();
|
|
16774
|
+
let m = new RegExp(String.raw`^!?(\w+)\s*\.\s*contains\s*\(\s*&\s*(\w+)(?:\s*\.\s*as_str\s*\(\s*\))?\s*\)$`).exec(c);
|
|
16775
|
+
if (m) return m[2];
|
|
16776
|
+
if (m = new RegExp(String.raw`^!\s*\[\s*${STR}(?:\s*,\s*${STR})*\s*,?\s*\]\s*\.\s*contains\s*\(\s*&\s*${VAR}\s*\)$`).exec(c)) {
|
|
16777
|
+
return m[1];
|
|
16778
|
+
}
|
|
16779
|
+
return guardedVariable(c);
|
|
16780
|
+
};
|
|
16781
|
+
const LITERAL_CHAIN = /* @__PURE__ */ new Set(["to_string", "into", "to_owned", "clone"]);
|
|
16782
|
+
const literalOnly = (block) => {
|
|
16783
|
+
const maskedB = maskRust(block.trim());
|
|
16784
|
+
if (!maskedB.trim()) return false;
|
|
16785
|
+
const re = /[A-Za-z_]\w*/g;
|
|
16786
|
+
let m;
|
|
16787
|
+
while ((m = re.exec(maskedB)) !== null) {
|
|
16788
|
+
const id = m[0];
|
|
16789
|
+
if (KEYWORDS.has(id) || /[A-Z]/.test(id[0]) || LITERAL_CHAIN.has(id)) continue;
|
|
16790
|
+
return false;
|
|
16791
|
+
}
|
|
16792
|
+
return true;
|
|
16793
|
+
};
|
|
16794
|
+
const usesOnlyVar = (block, name2) => {
|
|
16795
|
+
const b = block.trim();
|
|
16796
|
+
if (!b) return false;
|
|
16797
|
+
const maskedB = maskRust(b);
|
|
16798
|
+
const re = new RegExp(String.raw`(?<![\w.])${name2}\b`);
|
|
16799
|
+
if (!re.test(maskedB)) return false;
|
|
16800
|
+
return !readsOtherValue(b, maskedB, name2);
|
|
16801
|
+
};
|
|
16802
|
+
for (const m of masked.matchAll(/\blet\s+(\w+)\s*=\s*if\b/g)) {
|
|
16803
|
+
const bound = m[1];
|
|
16804
|
+
const at = m.index + m[0].length - 2;
|
|
16805
|
+
let depth = 0;
|
|
16806
|
+
let open = -1;
|
|
16807
|
+
for (let i2 = at + 2; i2 < masked.length; i2++) {
|
|
16808
|
+
const ch = masked[i2];
|
|
16809
|
+
if (ch === "(" || ch === "[") depth++;
|
|
16810
|
+
else if (ch === ")" || ch === "]") depth--;
|
|
16811
|
+
else if (ch === "{" && depth === 0) {
|
|
16812
|
+
open = i2;
|
|
16813
|
+
break;
|
|
16814
|
+
} else if (ch === ";" || ch === "}") break;
|
|
16815
|
+
}
|
|
16816
|
+
if (open < 0) continue;
|
|
16817
|
+
const cond = code.slice(at + 2, open).trim();
|
|
16818
|
+
const guarded = allowlistVar(cond);
|
|
16819
|
+
if (!guarded) continue;
|
|
16820
|
+
const thenClose = matchBrace(masked, open);
|
|
16821
|
+
if (thenClose < 0) continue;
|
|
16822
|
+
const thenBody = code.slice(open + 1, thenClose);
|
|
16823
|
+
if (!/^\s*else\b/.test(masked.slice(thenClose + 1, thenClose + 10))) continue;
|
|
16824
|
+
const elseOpen = masked.indexOf("{", thenClose + 1);
|
|
16825
|
+
if (elseOpen < 0) continue;
|
|
16826
|
+
const elseClose = matchBrace(masked, elseOpen);
|
|
16827
|
+
if (elseClose < 0) continue;
|
|
16828
|
+
const elseBody = code.slice(elseOpen + 1, elseClose);
|
|
16829
|
+
const inverted = /^\s*!/.test(cond);
|
|
16830
|
+
const ok = inverted ? literalOnly(thenBody) && usesOnlyVar(elseBody, guarded) : usesOnlyVar(thenBody, guarded) && literalOnly(elseBody);
|
|
16831
|
+
if (!ok) continue;
|
|
16832
|
+
const assignLine = masked.slice(0, at).split("\n").length;
|
|
16833
|
+
const mention = new RegExp(String.raw`(?<![\w.])${bound}\b`);
|
|
16834
|
+
for (let l = assignLine; l < origLines.length; l++) {
|
|
16835
|
+
if (!mention.test(maskedLines[l])) continue;
|
|
16836
|
+
out2.push({
|
|
16837
|
+
type: "rust_allowlist_ternary",
|
|
16838
|
+
method: "guard",
|
|
16839
|
+
line: l + 1,
|
|
16840
|
+
sanitizes: CREDITED_TYPES
|
|
16841
|
+
});
|
|
16842
|
+
}
|
|
16843
|
+
}
|
|
16844
|
+
return out2;
|
|
16845
|
+
}
|
|
16738
16846
|
function findRustLiteralEqualityGuardSanitizers(code) {
|
|
16739
16847
|
const masked = maskRust(code);
|
|
16740
16848
|
const origLines = code.split("\n");
|
|
@@ -16855,7 +16963,7 @@ function findRustLiteralEqualityGuardSanitizers(code) {
|
|
|
16855
16963
|
});
|
|
16856
16964
|
}
|
|
16857
16965
|
}
|
|
16858
|
-
return out2;
|
|
16966
|
+
return [...out2, ...findRustAllowlistTernarySanitizers(code)];
|
|
16859
16967
|
}
|
|
16860
16968
|
function statementSpan(code, masked, lineStart, endIdx) {
|
|
16861
16969
|
let start2 = lineStart;
|
|
@@ -18233,6 +18341,7 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
18233
18341
|
const assignRe = /^\s*(?:await\s+)?(?:using\s*\(?\s*)?(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
|
|
18234
18342
|
const arrayElementAssignRe = /^\s*([A-Za-z_]\w*)\s*\[[^\]]+\]\s*=\s*(.+?);?\s*$/;
|
|
18235
18343
|
const memberFieldAssignRe = /^\s*([A-Za-z_]\w*)\.([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
|
|
18344
|
+
const collectionAddRe = /^\s*([A-Za-z_]\w*)\s*\.\s*Add\s*\(\s*(.+?)\s*\)\s*;?\s*$/;
|
|
18236
18345
|
const consoleReadRe = /\bConsole\s*\.\s*ReadLine\s*\(/;
|
|
18237
18346
|
const envReadRe = /\bEnvironment\s*\.\s*GetEnvironmentVariables?\s*(?:\(|\[)/;
|
|
18238
18347
|
const formFileParams = /* @__PURE__ */ new Set();
|
|
@@ -18326,6 +18435,25 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
18326
18435
|
continue;
|
|
18327
18436
|
}
|
|
18328
18437
|
}
|
|
18438
|
+
const addEl = collectionAddRe.exec(stmt);
|
|
18439
|
+
if (addEl) {
|
|
18440
|
+
const [, baseName, rhs2] = addEl;
|
|
18441
|
+
const type2 = classify(rhs2);
|
|
18442
|
+
if (type2 && baseName) {
|
|
18443
|
+
const lineNumber2 = i2 + 1;
|
|
18444
|
+
if (!sources.some((s) => s.line === lineNumber2 && s.variable === baseName)) {
|
|
18445
|
+
sources.push({
|
|
18446
|
+
type: type2,
|
|
18447
|
+
location: `${baseName}.Add(${rhs2.trim().substring(0, 50)}${rhs2.length > 50 ? "..." : ""})`,
|
|
18448
|
+
severity: type2 === "db_input" ? "medium" : "high",
|
|
18449
|
+
line: lineNumber2,
|
|
18450
|
+
confidence: 1,
|
|
18451
|
+
variable: baseName
|
|
18452
|
+
});
|
|
18453
|
+
}
|
|
18454
|
+
continue;
|
|
18455
|
+
}
|
|
18456
|
+
}
|
|
18329
18457
|
const m = assignRe.exec(stmt);
|
|
18330
18458
|
const rhsCrossesBlock = m !== null && m[2].includes("{");
|
|
18331
18459
|
if (!m || rhsCrossesBlock) {
|
|
@@ -26404,6 +26532,9 @@ function isInterproceduralTaintableType(typeName, language) {
|
|
|
26404
26532
|
"IEnumerable",
|
|
26405
26533
|
"ICollection",
|
|
26406
26534
|
"IList",
|
|
26535
|
+
"List",
|
|
26536
|
+
"Hashtable",
|
|
26537
|
+
"ArrayList",
|
|
26407
26538
|
"Dictionary",
|
|
26408
26539
|
"IDictionary",
|
|
26409
26540
|
"IReadOnlyList",
|
|
@@ -32027,6 +32158,11 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32027
32158
|
taintedArrayElements = /* @__PURE__ */ new Map();
|
|
32028
32159
|
// Track current method name for scoping local variables
|
|
32029
32160
|
currentMethod = null;
|
|
32161
|
+
/** Start lines of nested `{ ... }` blocks — disambiguate same-named locals (#476 Collections10). */
|
|
32162
|
+
blockScopeStack = [];
|
|
32163
|
+
lineBlockScopes = /* @__PURE__ */ new Map();
|
|
32164
|
+
/** Skip block-scope push on the method/constructor body `{ ... }` (#476). */
|
|
32165
|
+
skipMethodBodyBlockScope = false;
|
|
32030
32166
|
// Track conditional taints: which variables were tainted under which conditions
|
|
32031
32167
|
// Maps condition expression string → set of variables tainted under that condition
|
|
32032
32168
|
conditionalTaints = /* @__PURE__ */ new Map();
|
|
@@ -32040,6 +32176,8 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32040
32176
|
inSynchronizedBlock = false;
|
|
32041
32177
|
// Track iterator sources: iterator variable name → collection name it was created from
|
|
32042
32178
|
iteratorSources = /* @__PURE__ */ new Map();
|
|
32179
|
+
/** Vars assigned from `iter.next()` — do not retro-clear on later iterator rebind (#476). */
|
|
32180
|
+
taintedFromIteratorNext = /* @__PURE__ */ new Set();
|
|
32043
32181
|
// Track class field names (declared at class level, not local variables)
|
|
32044
32182
|
classFields = /* @__PURE__ */ new Set();
|
|
32045
32183
|
// Track tainted method parameters for inter-procedural analysis
|
|
@@ -32097,12 +32235,16 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32097
32235
|
this.sanitizedVars.clear();
|
|
32098
32236
|
this.sanitizedVarUncovered.clear();
|
|
32099
32237
|
this.currentMethod = null;
|
|
32238
|
+
this.blockScopeStack = [];
|
|
32239
|
+
this.lineBlockScopes.clear();
|
|
32240
|
+
this.skipMethodBodyBlockScope = false;
|
|
32100
32241
|
this.conditionalTaints.clear();
|
|
32101
32242
|
this.conditionStack = [];
|
|
32102
32243
|
this.lineConditions.clear();
|
|
32103
32244
|
this.synchronizedLines.clear();
|
|
32104
32245
|
this.inSynchronizedBlock = false;
|
|
32105
32246
|
this.iteratorSources.clear();
|
|
32247
|
+
this.taintedFromIteratorNext.clear();
|
|
32106
32248
|
this.classFields.clear();
|
|
32107
32249
|
this.instanceFieldTaint.clear();
|
|
32108
32250
|
this.currentClassName = null;
|
|
@@ -32127,6 +32269,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32127
32269
|
const resultSymbols = new Map(this.symbols);
|
|
32128
32270
|
for (const name2 of this.tainted) {
|
|
32129
32271
|
if (name2.includes(":")) {
|
|
32272
|
+
if (/^[^:]+:b\d+:/.test(name2)) {
|
|
32273
|
+
continue;
|
|
32274
|
+
}
|
|
32130
32275
|
const unscoped = name2.substring(name2.indexOf(":") + 1);
|
|
32131
32276
|
resultTainted.add(unscoped);
|
|
32132
32277
|
}
|
|
@@ -32141,6 +32286,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32141
32286
|
for (const [name2, value] of this.symbols) {
|
|
32142
32287
|
if (this.goScopedNames.has(name2)) continue;
|
|
32143
32288
|
if (name2.includes(":")) {
|
|
32289
|
+
if (/^[^:]+:b\d+:/.test(name2)) {
|
|
32290
|
+
continue;
|
|
32291
|
+
}
|
|
32144
32292
|
const unscoped = name2.substring(name2.indexOf(":") + 1);
|
|
32145
32293
|
if (!resultSymbols.has(unscoped)) {
|
|
32146
32294
|
resultSymbols.set(unscoped, value);
|
|
@@ -32150,6 +32298,7 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32150
32298
|
return {
|
|
32151
32299
|
symbols: resultSymbols,
|
|
32152
32300
|
tainted: resultTainted,
|
|
32301
|
+
lineBlockScopes: new Map(this.lineBlockScopes),
|
|
32153
32302
|
unreachableLines: new Set(this.unreachableLines),
|
|
32154
32303
|
taintedCollections: new Map(this.taintedCollections),
|
|
32155
32304
|
taintedArrayElements: new Map(this.taintedArrayElements),
|
|
@@ -32693,6 +32842,18 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32693
32842
|
// ===========================================================================
|
|
32694
32843
|
// Taint Refinement
|
|
32695
32844
|
// ===========================================================================
|
|
32845
|
+
/** True when `node` is `iter.next()` (possibly under a cast). */
|
|
32846
|
+
isIteratorNextExpression(node) {
|
|
32847
|
+
const text = getNodeText2(node, this.source);
|
|
32848
|
+
if (/\.\s*next\s*\(/.test(text)) return true;
|
|
32849
|
+
let current = node;
|
|
32850
|
+
if (current.type === "cast_expression" || current.type === "parenthesized_expression") {
|
|
32851
|
+
current = current.childForFieldName("operand") ?? current.childForFieldName("expression");
|
|
32852
|
+
}
|
|
32853
|
+
if (!current || current.type !== "method_invocation") return false;
|
|
32854
|
+
const nameNode = current.childForFieldName("name");
|
|
32855
|
+
return nameNode ? getNodeText2(nameNode, this.source) === "next" : false;
|
|
32856
|
+
}
|
|
32696
32857
|
refineTaintFromConstants() {
|
|
32697
32858
|
let changed = true;
|
|
32698
32859
|
let iterations = 0;
|
|
@@ -32702,6 +32863,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32702
32863
|
iterations++;
|
|
32703
32864
|
const toRemove = [];
|
|
32704
32865
|
for (const varName of this.tainted) {
|
|
32866
|
+
if (this.taintedFromIteratorNext.has(varName)) {
|
|
32867
|
+
continue;
|
|
32868
|
+
}
|
|
32705
32869
|
const symbol = this.symbols.get(varName);
|
|
32706
32870
|
if (symbol && symbol.type !== "unknown") {
|
|
32707
32871
|
toRemove.push(varName);
|
|
@@ -32750,6 +32914,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32750
32914
|
if (this.unreachableLines.has(line)) {
|
|
32751
32915
|
return true;
|
|
32752
32916
|
}
|
|
32917
|
+
if (this.blockScopeStack.length > 0) {
|
|
32918
|
+
this.lineBlockScopes.set(line, this.blockScopeStack[this.blockScopeStack.length - 1]);
|
|
32919
|
+
}
|
|
32753
32920
|
if (this.conditionStack.length > 0 && !this.lineConditions.has(line)) {
|
|
32754
32921
|
this.lineConditions.set(line, this.conditionStack[this.conditionStack.length - 1]);
|
|
32755
32922
|
}
|
|
@@ -32781,6 +32948,15 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32781
32948
|
case "if_statement":
|
|
32782
32949
|
this.handleIfStatement(node);
|
|
32783
32950
|
return true;
|
|
32951
|
+
case "block": {
|
|
32952
|
+
if (this.skipMethodBodyBlockScope) {
|
|
32953
|
+
this.skipMethodBodyBlockScope = false;
|
|
32954
|
+
}
|
|
32955
|
+
for (const child of node.children) {
|
|
32956
|
+
this.visit(child);
|
|
32957
|
+
}
|
|
32958
|
+
return true;
|
|
32959
|
+
}
|
|
32784
32960
|
case "switch_expression":
|
|
32785
32961
|
case "switch_statement":
|
|
32786
32962
|
this.handleSwitch(node);
|
|
@@ -32866,7 +33042,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32866
33042
|
}
|
|
32867
33043
|
const body2 = node.childForFieldName("body");
|
|
32868
33044
|
if (body2) {
|
|
33045
|
+
this.skipMethodBodyBlockScope = true;
|
|
32869
33046
|
this.visit(body2);
|
|
33047
|
+
this.skipMethodBodyBlockScope = false;
|
|
32870
33048
|
}
|
|
32871
33049
|
this.currentMethod = prevMethod;
|
|
32872
33050
|
this.inConstructor = prevInConstructor;
|
|
@@ -32880,10 +33058,31 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32880
33058
|
if (varName.includes(".") || varName.includes(":")) {
|
|
32881
33059
|
return varName;
|
|
32882
33060
|
}
|
|
33061
|
+
const blockId = this.blockScopeStack.length > 0 ? this.blockScopeStack[this.blockScopeStack.length - 1] : void 0;
|
|
32883
33062
|
if (this.currentMethod) {
|
|
32884
|
-
return `${this.currentMethod}:${varName}`;
|
|
33063
|
+
return blockId !== void 0 ? `${this.currentMethod}:b${blockId}:${varName}` : `${this.currentMethod}:${varName}`;
|
|
32885
33064
|
}
|
|
32886
|
-
return varName;
|
|
33065
|
+
return blockId !== void 0 ? `b${blockId}:${varName}` : varName;
|
|
33066
|
+
}
|
|
33067
|
+
/** Innermost block → method → unscoped, for reads that may bind to an outer declaration. */
|
|
33068
|
+
getScopedNameCandidates(varName) {
|
|
33069
|
+
if (varName.includes(".") || varName.includes(":")) {
|
|
33070
|
+
return [varName];
|
|
33071
|
+
}
|
|
33072
|
+
const out2 = [];
|
|
33073
|
+
for (let i2 = this.blockScopeStack.length - 1; i2 >= 0; i2--) {
|
|
33074
|
+
const blockId = this.blockScopeStack[i2];
|
|
33075
|
+
if (this.currentMethod) {
|
|
33076
|
+
out2.push(`${this.currentMethod}:b${blockId}:${varName}`);
|
|
33077
|
+
} else {
|
|
33078
|
+
out2.push(`b${blockId}:${varName}`);
|
|
33079
|
+
}
|
|
33080
|
+
}
|
|
33081
|
+
if (this.currentMethod) {
|
|
33082
|
+
out2.push(`${this.currentMethod}:${varName}`);
|
|
33083
|
+
}
|
|
33084
|
+
out2.push(varName);
|
|
33085
|
+
return out2;
|
|
32887
33086
|
}
|
|
32888
33087
|
/**
|
|
32889
33088
|
* Look up a variable value, checking both scoped and unscoped names.
|
|
@@ -32891,11 +33090,12 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32891
33090
|
* either local (scoped) or global (unscoped, like class fields).
|
|
32892
33091
|
*/
|
|
32893
33092
|
lookupSymbol(varName) {
|
|
32894
|
-
if (
|
|
32895
|
-
const scopedName
|
|
32896
|
-
|
|
32897
|
-
|
|
32898
|
-
|
|
33093
|
+
if (!varName.includes(".") && !varName.includes(":")) {
|
|
33094
|
+
for (const scopedName of this.getScopedNameCandidates(varName)) {
|
|
33095
|
+
const scopedValue = this.symbols.get(scopedName);
|
|
33096
|
+
if (scopedValue) {
|
|
33097
|
+
return scopedValue;
|
|
33098
|
+
}
|
|
32899
33099
|
}
|
|
32900
33100
|
}
|
|
32901
33101
|
return this.symbols.get(varName);
|
|
@@ -32947,8 +33147,22 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
32947
33147
|
this.trackIteratorsInNode(initNode);
|
|
32948
33148
|
}
|
|
32949
33149
|
}
|
|
32950
|
-
|
|
32951
|
-
|
|
33150
|
+
if (node.type === "while_statement" || node.type === "do_statement") {
|
|
33151
|
+
const body2 = node.childForFieldName("body");
|
|
33152
|
+
for (const child of node.children) {
|
|
33153
|
+
if (child !== body2) this.visit(child);
|
|
33154
|
+
}
|
|
33155
|
+
if (body2?.type === "block") {
|
|
33156
|
+
this.blockScopeStack.push(getNodeLine(body2));
|
|
33157
|
+
this.visit(body2);
|
|
33158
|
+
this.blockScopeStack.pop();
|
|
33159
|
+
} else if (body2) {
|
|
33160
|
+
this.visit(body2);
|
|
33161
|
+
}
|
|
33162
|
+
} else {
|
|
33163
|
+
for (const child of node.children) {
|
|
33164
|
+
this.visit(child);
|
|
33165
|
+
}
|
|
32952
33166
|
}
|
|
32953
33167
|
for (const varName of loopVarNames) {
|
|
32954
33168
|
this.symbols.set(varName, createUnknown(getNodeLine(node)));
|
|
@@ -33090,6 +33304,10 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
33090
33304
|
this.sanitizedVars.delete(scopedName);
|
|
33091
33305
|
this.sanitizedVarUncovered.delete(scopedName);
|
|
33092
33306
|
this.definitionNodes.set(scopedName, valueNode);
|
|
33307
|
+
if (this.isIteratorNextExpression(valueNode)) {
|
|
33308
|
+
this.taintedFromIteratorNext.add(scopedName);
|
|
33309
|
+
this.taintedFromIteratorNext.add(varName);
|
|
33310
|
+
}
|
|
33093
33311
|
this.symbols.set(scopedName, createUnknown(line));
|
|
33094
33312
|
} else {
|
|
33095
33313
|
if (this.inConditionalBranch) {
|
|
@@ -33217,6 +33435,12 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
33217
33435
|
this.sanitizedVars.delete(scopedName);
|
|
33218
33436
|
this.sanitizedVarUncovered.delete(scopedName);
|
|
33219
33437
|
this.definitionNodes.set(scopedName, right);
|
|
33438
|
+
if (this.isIteratorNextExpression(right)) {
|
|
33439
|
+
this.taintedFromIteratorNext.add(scopedName);
|
|
33440
|
+
if (!varName.includes(".")) {
|
|
33441
|
+
this.taintedFromIteratorNext.add(varName);
|
|
33442
|
+
}
|
|
33443
|
+
}
|
|
33220
33444
|
this.symbols.set(scopedName, createUnknown(line));
|
|
33221
33445
|
if (this.inConstructor && varName.startsWith("this.")) {
|
|
33222
33446
|
const fieldName = varName.substring(5);
|
|
@@ -34207,10 +34431,17 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
34207
34431
|
}
|
|
34208
34432
|
}
|
|
34209
34433
|
if (node.type === "identifier") {
|
|
34210
|
-
|
|
34211
|
-
|
|
34212
|
-
|
|
34213
|
-
if (this.
|
|
34434
|
+
let isTainted = false;
|
|
34435
|
+
let boundName = null;
|
|
34436
|
+
for (const candidate of this.getScopedNameCandidates(text)) {
|
|
34437
|
+
if (this.tainted.has(candidate)) {
|
|
34438
|
+
isTainted = true;
|
|
34439
|
+
boundName = candidate;
|
|
34440
|
+
break;
|
|
34441
|
+
}
|
|
34442
|
+
}
|
|
34443
|
+
if (isTainted && boundName) {
|
|
34444
|
+
if (this.isExcludedByCondition(boundName) || this.isExcludedByCondition(text)) {
|
|
34214
34445
|
return false;
|
|
34215
34446
|
}
|
|
34216
34447
|
}
|
|
@@ -34417,6 +34648,14 @@ function analyzeConstantPropagation(tree, sourceCode, options = {}) {
|
|
|
34417
34648
|
options.taintedParameters ?? []
|
|
34418
34649
|
);
|
|
34419
34650
|
}
|
|
34651
|
+
function constPropScopedVar(method, varName, line, result) {
|
|
34652
|
+
if (!method) return varName;
|
|
34653
|
+
const blockId = result.lineBlockScopes?.get(line);
|
|
34654
|
+
if (blockId !== void 0) {
|
|
34655
|
+
return `${method}:b${blockId}:${varName}`;
|
|
34656
|
+
}
|
|
34657
|
+
return `${method}:${varName}`;
|
|
34658
|
+
}
|
|
34420
34659
|
function isFalsePositive(result, sinkLine, taintedVar) {
|
|
34421
34660
|
if (result.unreachableLines.has(sinkLine)) {
|
|
34422
34661
|
return { isFalsePositive: true, reason: "sink_in_dead_code" };
|
|
@@ -43296,6 +43535,15 @@ function isReactHtmlComponent(m, f, lines) {
|
|
|
43296
43535
|
const sig = lines[m.start_line - 1] ?? "";
|
|
43297
43536
|
return /\bfunction\s+[A-Z][A-Za-z0-9]*\s*\(\s*\{/.test(sig);
|
|
43298
43537
|
}
|
|
43538
|
+
function isPythonYamlLoadParamDeser(f, lines) {
|
|
43539
|
+
if (f.source_type !== "interprocedural_param" || f.sink_type !== "deserialization") return false;
|
|
43540
|
+
const head = f.path[0]?.variable;
|
|
43541
|
+
if (!head || f.path.length < 2) return false;
|
|
43542
|
+
const sinkLine = lines[f.sink_line - 1] ?? "";
|
|
43543
|
+
if (!/\byaml\.load\s*\(/.test(sinkLine) || /\bLoader\s*=\s*yaml\.SafeLoader/.test(sinkLine)) return false;
|
|
43544
|
+
const esc = head.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
43545
|
+
return new RegExp(`\\byaml\\.load\\s*\\(\\s*${esc}\\b`).test(sinkLine);
|
|
43546
|
+
}
|
|
43299
43547
|
var FIELD_CARRIED_SOURCE = /constructor-injected field|returns tainted field/;
|
|
43300
43548
|
var EXECUTOR_CALLEES = /* @__PURE__ */ new Map([
|
|
43301
43549
|
["submit", 0],
|
|
@@ -43435,7 +43683,7 @@ var ParamSourceFlowGatePass = class {
|
|
|
43435
43683
|
let keep = true;
|
|
43436
43684
|
if (f.source_type === "interprocedural_param" && !realSourceLines.has(f.source_line)) {
|
|
43437
43685
|
const m = enclosingMethod(methods, f.source_line);
|
|
43438
|
-
keep = m !== void 0 && (isHandler(m) || fedByRealSource(f, m) || isReactHtmlComponent(m, f, lines));
|
|
43686
|
+
keep = m !== void 0 && (isHandler(m) || fedByRealSource(f, m) || isReactHtmlComponent(m, f, lines) || ctx.language === "python" && isPythonYamlLoadParamDeser(f, lines));
|
|
43439
43687
|
}
|
|
43440
43688
|
if (keep) flows[w++] = f;
|
|
43441
43689
|
else {
|
|
@@ -44068,6 +44316,32 @@ function javaReceiverChainDerivedVar(rhsByLocal, localVar, seedVar) {
|
|
|
44068
44316
|
const receiverOnRhs = new RegExp(`(?:^|[^\\w.$])${esc}\\s*\\.`, "u");
|
|
44069
44317
|
return rhsList.some((rhs) => receiverOnRhs.test(rhs));
|
|
44070
44318
|
}
|
|
44319
|
+
function defLineForUse(graph, varName, useLine) {
|
|
44320
|
+
const own = (graph.defsByLine.get(useLine) ?? []).find((d2) => d2.variable === varName);
|
|
44321
|
+
if (own) return own.line;
|
|
44322
|
+
const use = (graph.usesByLine.get(useLine) ?? []).find((u) => u.variable === varName && u.def_id != null);
|
|
44323
|
+
const d = use ? graph.defById.get(use.def_id) : void 0;
|
|
44324
|
+
return d?.line ?? useLine;
|
|
44325
|
+
}
|
|
44326
|
+
function constPropFlowLookup(language, method, varName, blockLine, constProp, preferMethodScoped = false) {
|
|
44327
|
+
if (language === "go") {
|
|
44328
|
+
return constPropScopedVar(method, varName, blockLine, constProp);
|
|
44329
|
+
}
|
|
44330
|
+
if (!method || varName.includes(":")) return varName;
|
|
44331
|
+
const blockScoped = constPropScopedVar(method, varName, blockLine, constProp);
|
|
44332
|
+
const hasTaintBinding = (name2) => constProp.tainted.has(name2) || constProp.sanitizedVars.has(name2);
|
|
44333
|
+
if (blockScoped.includes(":b") && hasTaintBinding(blockScoped)) {
|
|
44334
|
+
return blockScoped;
|
|
44335
|
+
}
|
|
44336
|
+
const methodScoped = `${method}:${varName}`;
|
|
44337
|
+
if (hasTaintBinding(methodScoped)) {
|
|
44338
|
+
return methodScoped;
|
|
44339
|
+
}
|
|
44340
|
+
if (preferMethodScoped && method) {
|
|
44341
|
+
return methodScoped;
|
|
44342
|
+
}
|
|
44343
|
+
return constProp.lineBlockScopes?.has(blockLine) ? blockScoped : varName;
|
|
44344
|
+
}
|
|
44071
44345
|
function indexJavaLocalAssignments(code) {
|
|
44072
44346
|
const declRe = /^\s*(?:public|private|protected|static|final|volatile|transient|\s)*\s*(?:[A-Za-z_][\w.]*(?:\s*<[^>]*>)?(?:\s*\[\s*\])*)\s+([A-Za-z_]\w*)\s*=\s*(.+?);\s*$/;
|
|
44073
44347
|
const assignRe = /^\s*([A-Za-z_]\w*)\s*=\s*(.+?);\s*$/;
|
|
@@ -44113,10 +44387,24 @@ function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers
|
|
|
44113
44387
|
};
|
|
44114
44388
|
return (step, sourceLine, sinkLine, sinkType) => {
|
|
44115
44389
|
const method = methodAtLine(graph, step.line);
|
|
44116
|
-
const
|
|
44117
|
-
const
|
|
44390
|
+
const blockLine = defLineForUse(graph, step.variable, step.line);
|
|
44391
|
+
const fpLookup = constPropFlowLookup(
|
|
44392
|
+
language,
|
|
44393
|
+
method,
|
|
44394
|
+
step.variable,
|
|
44395
|
+
blockLine,
|
|
44396
|
+
constProp
|
|
44397
|
+
);
|
|
44398
|
+
const r = isFalsePositive(constProp, step.line, fpLookup);
|
|
44118
44399
|
if (!r.isFalsePositive) return false;
|
|
44119
44400
|
if (r.reason !== "variable_not_tainted") return true;
|
|
44401
|
+
if (r.reason === "variable_not_tainted" && language === "java" && method && fpLookup === step.variable && !step.variable.includes(".")) {
|
|
44402
|
+
const v = step.variable;
|
|
44403
|
+
if (constProp.tainted.has(`${method}:${v}`)) return false;
|
|
44404
|
+
for (const t of constProp.tainted) {
|
|
44405
|
+
if (t.startsWith(`${method}:b`) && t.endsWith(`:${v}`)) return false;
|
|
44406
|
+
}
|
|
44407
|
+
}
|
|
44120
44408
|
if (sinkType !== void 0 && constProp.sanitizedVarUncovered) {
|
|
44121
44409
|
const unc = constProp.sanitizedVarUncovered;
|
|
44122
44410
|
if (step.variable.includes(":")) {
|
|
@@ -44124,7 +44412,9 @@ function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers
|
|
|
44124
44412
|
} else {
|
|
44125
44413
|
const method2 = methodAtLine(graph, step.line);
|
|
44126
44414
|
if (method2) {
|
|
44127
|
-
if (unc.get(
|
|
44415
|
+
if (unc.get(fpLookup)?.has(sinkType)) {
|
|
44416
|
+
return false;
|
|
44417
|
+
}
|
|
44128
44418
|
} else if (unc.get(step.variable)?.has(sinkType)) {
|
|
44129
44419
|
return false;
|
|
44130
44420
|
}
|
|
@@ -44162,7 +44452,7 @@ function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers
|
|
|
44162
44452
|
}
|
|
44163
44453
|
if (!d) return true;
|
|
44164
44454
|
if (d.line === sourceLine && sources.some((s) => s.line === sourceLine && !CONTAINER_READ_SOURCES.has(s.type))) {
|
|
44165
|
-
if (step.line === sinkLine && !constProp.tainted.has(
|
|
44455
|
+
if (step.line === sinkLine && !constProp.tainted.has(fpLookup)) {
|
|
44166
44456
|
for (let ln = sourceLine; ln <= sinkLine; ln++) {
|
|
44167
44457
|
const calleeMatch = (lines[ln - 1] ?? "").match(/:=\s*(\w+)\s*\(/);
|
|
44168
44458
|
if (calleeMatch && constProp.constantReturningMethods?.has(calleeMatch[1])) {
|
|
@@ -44341,7 +44631,15 @@ var TaintPropagationPass = class {
|
|
|
44341
44631
|
for (const f of arrayFlows) {
|
|
44342
44632
|
pushIfNew(f);
|
|
44343
44633
|
}
|
|
44344
|
-
const collectionFlows = detectCollectionFlows(
|
|
44634
|
+
const collectionFlows = detectCollectionFlows(
|
|
44635
|
+
calls,
|
|
44636
|
+
sources,
|
|
44637
|
+
sinks,
|
|
44638
|
+
constProp,
|
|
44639
|
+
ctx.code,
|
|
44640
|
+
types,
|
|
44641
|
+
graph
|
|
44642
|
+
) ?? [];
|
|
44345
44643
|
for (const f of collectionFlows) {
|
|
44346
44644
|
if (flowKeys.has(flowKey(f))) continue;
|
|
44347
44645
|
const flowForCheck = {
|
|
@@ -44395,6 +44693,17 @@ var TaintPropagationPass = class {
|
|
|
44395
44693
|
pushIfNew(f);
|
|
44396
44694
|
}
|
|
44397
44695
|
}
|
|
44696
|
+
if ((ctx.language === "javascript" || ctx.language === "typescript") && typeof ctx.code === "string") {
|
|
44697
|
+
const jsModuleFlows = detectJsModuleScopeFlows(
|
|
44698
|
+
ctx.code,
|
|
44699
|
+
sources,
|
|
44700
|
+
sinks,
|
|
44701
|
+
constProp.unreachableLines
|
|
44702
|
+
) ?? [];
|
|
44703
|
+
for (const f of jsModuleFlows) {
|
|
44704
|
+
pushIfNew(f);
|
|
44705
|
+
}
|
|
44706
|
+
}
|
|
44398
44707
|
const sanitizedNames = constProp.sanitizedVars;
|
|
44399
44708
|
const sanitizedUncovered = constProp.sanitizedVarUncovered;
|
|
44400
44709
|
let finalFlows = sanitizedNames.size === 0 ? flows : flows.filter((f) => {
|
|
@@ -44808,6 +45117,85 @@ function detectGoPackageGlobalFlows(code, _calls, sources, sinks, unreachableLin
|
|
|
44808
45117
|
}
|
|
44809
45118
|
return flows;
|
|
44810
45119
|
}
|
|
45120
|
+
function detectJsModuleScopeFlows(code, sources, sinks, unreachableLines) {
|
|
45121
|
+
const flows = [];
|
|
45122
|
+
if (!code || sinks.length === 0) return flows;
|
|
45123
|
+
const lines = code.split("\n");
|
|
45124
|
+
const moduleVars = /* @__PURE__ */ new Set();
|
|
45125
|
+
for (const line of lines) {
|
|
45126
|
+
const stripped = line.replace(/\s+$/, "");
|
|
45127
|
+
if (!stripped || /^[ \t]/.test(line)) continue;
|
|
45128
|
+
const m = stripped.match(/^(?:let|var|const)\s+([A-Za-z_$][\w$]*)\b/);
|
|
45129
|
+
if (m) moduleVars.add(m[1]);
|
|
45130
|
+
}
|
|
45131
|
+
if (moduleVars.size === 0) return flows;
|
|
45132
|
+
const sourceVarNames = /* @__PURE__ */ new Set();
|
|
45133
|
+
for (const s of sources) {
|
|
45134
|
+
if (typeof s.variable === "string" && s.variable.length > 0) {
|
|
45135
|
+
sourceVarNames.add(s.variable);
|
|
45136
|
+
}
|
|
45137
|
+
}
|
|
45138
|
+
const JS_HTTP_SOURCE_RE = /\b(?:req|request)\.(?:body|query|params|headers|cookies|get)\b|\breq\.(?:param|query|body)\(/;
|
|
45139
|
+
const writes = [];
|
|
45140
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
45141
|
+
const line = lines[i2];
|
|
45142
|
+
if (unreachableLines.has(i2 + 1)) continue;
|
|
45143
|
+
for (const lhs of moduleVars) {
|
|
45144
|
+
const declRe = new RegExp(`\\b(?:let|const|var)\\s+${lhs.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`);
|
|
45145
|
+
if (declRe.test(line)) continue;
|
|
45146
|
+
const m = line.match(
|
|
45147
|
+
new RegExp(`\\b${lhs.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*=\\s*([^;]+)`)
|
|
45148
|
+
);
|
|
45149
|
+
if (!m) continue;
|
|
45150
|
+
const rhs = m[1];
|
|
45151
|
+
let taintedBy = null;
|
|
45152
|
+
const onLine = sources.filter((s) => s.line === i2 + 1);
|
|
45153
|
+
if (onLine.length > 0) {
|
|
45154
|
+
taintedBy = { line: onLine[0].line, type: onLine[0].type };
|
|
45155
|
+
} else if (JS_HTTP_SOURCE_RE.test(rhs)) {
|
|
45156
|
+
taintedBy = { line: i2 + 1, type: "http_body" };
|
|
45157
|
+
} else {
|
|
45158
|
+
for (const v of sourceVarNames) {
|
|
45159
|
+
const re = new RegExp(`(?<![A-Za-z0-9_$])${v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_$])`);
|
|
45160
|
+
if (re.test(rhs)) {
|
|
45161
|
+
const src = sources.find((s) => s.variable === v);
|
|
45162
|
+
if (src) {
|
|
45163
|
+
taintedBy = { line: src.line, type: src.type };
|
|
45164
|
+
break;
|
|
45165
|
+
}
|
|
45166
|
+
}
|
|
45167
|
+
}
|
|
45168
|
+
}
|
|
45169
|
+
if (!taintedBy) continue;
|
|
45170
|
+
writes.push({ varName: lhs, line: i2 + 1, sourceLine: taintedBy.line, sourceType: taintedBy.type });
|
|
45171
|
+
}
|
|
45172
|
+
}
|
|
45173
|
+
if (writes.length === 0) return flows;
|
|
45174
|
+
for (const sink of sinks) {
|
|
45175
|
+
if (unreachableLines.has(sink.line)) continue;
|
|
45176
|
+
const sinkLineText = lines[sink.line - 1] ?? "";
|
|
45177
|
+
if (!sinkLineText) continue;
|
|
45178
|
+
for (const w of writes) {
|
|
45179
|
+
if (w.line >= sink.line) continue;
|
|
45180
|
+
const re = new RegExp(`(?<![A-Za-z0-9_$])${w.varName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_$])`);
|
|
45181
|
+
if (!re.test(sinkLineText)) continue;
|
|
45182
|
+
flows.push({
|
|
45183
|
+
source_line: w.sourceLine,
|
|
45184
|
+
sink_line: sink.line,
|
|
45185
|
+
source_type: w.sourceType,
|
|
45186
|
+
sink_type: sink.type,
|
|
45187
|
+
path: [
|
|
45188
|
+
{ variable: w.varName, line: w.sourceLine, type: "source" },
|
|
45189
|
+
{ variable: w.varName, line: w.line, type: "assignment" },
|
|
45190
|
+
{ variable: w.varName, line: sink.line, type: "sink" }
|
|
45191
|
+
],
|
|
45192
|
+
confidence: 0.9,
|
|
45193
|
+
sanitized: false
|
|
45194
|
+
});
|
|
45195
|
+
}
|
|
45196
|
+
}
|
|
45197
|
+
return flows;
|
|
45198
|
+
}
|
|
44811
45199
|
function methodHasLocalDecl(code, methodName, varName, atLine) {
|
|
44812
45200
|
if (!code || !methodName) return false;
|
|
44813
45201
|
const esc = methodName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -44875,7 +45263,9 @@ function isTaintedInMethod(taintedVars, varName, scopedName, fieldNames, isField
|
|
|
44875
45263
|
}
|
|
44876
45264
|
return true;
|
|
44877
45265
|
}
|
|
44878
|
-
function detectCollectionFlows(calls, sources, sinks,
|
|
45266
|
+
function detectCollectionFlows(calls, sources, sinks, constProp, code, types, graph) {
|
|
45267
|
+
const taintedVars = constProp.tainted;
|
|
45268
|
+
const unreachableLines = constProp.unreachableLines;
|
|
44879
45269
|
const flows = [];
|
|
44880
45270
|
const fieldNames = /* @__PURE__ */ new Set();
|
|
44881
45271
|
for (const t of types ?? []) for (const f of t.fields ?? []) fieldNames.add(f.name);
|
|
@@ -44895,7 +45285,18 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
44895
45285
|
}
|
|
44896
45286
|
if (arg.variable) {
|
|
44897
45287
|
const varName = arg.variable;
|
|
44898
|
-
|
|
45288
|
+
let scopedName = varName;
|
|
45289
|
+
if (call.in_method) {
|
|
45290
|
+
const blockLine = graph ? defLineForUse(graph, varName, call.location.line) : call.location.line;
|
|
45291
|
+
scopedName = constPropFlowLookup(
|
|
45292
|
+
"java",
|
|
45293
|
+
call.in_method,
|
|
45294
|
+
varName,
|
|
45295
|
+
blockLine,
|
|
45296
|
+
constProp,
|
|
45297
|
+
true
|
|
45298
|
+
);
|
|
45299
|
+
}
|
|
44899
45300
|
const expr = arg.expression ?? "";
|
|
44900
45301
|
const explicitField = isFieldUseExpression(
|
|
44901
45302
|
expr,
|