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
|
@@ -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");
|
|
@@ -10361,6 +10390,79 @@ function processStatement2(stmt, startDefId, startUseId, scopeStack, isJavaScrip
|
|
|
10361
10390
|
carryBranchDefsOut(popped, scopeStack, alwaysRuns);
|
|
10362
10391
|
break;
|
|
10363
10392
|
}
|
|
10393
|
+
case "try_statement":
|
|
10394
|
+
case "try_with_resources_statement": {
|
|
10395
|
+
if (isJavaScript) {
|
|
10396
|
+
const stmtUses = extractUses(stmt, useId, scopeStack, isJavaScript);
|
|
10397
|
+
uses.push(...stmtUses.uses);
|
|
10398
|
+
useId = stmtUses.nextId;
|
|
10399
|
+
break;
|
|
10400
|
+
}
|
|
10401
|
+
scopeStack.push(/* @__PURE__ */ new Map());
|
|
10402
|
+
const resources = stmt.childForFieldName("resources");
|
|
10403
|
+
for (const res of resources?.namedChildren ?? []) {
|
|
10404
|
+
if (!res || res.type !== "resource") continue;
|
|
10405
|
+
const nameNode = res.childForFieldName("name");
|
|
10406
|
+
const value = res.childForFieldName("value");
|
|
10407
|
+
if (value) {
|
|
10408
|
+
const valueUses = extractUses(value, useId, scopeStack, isJavaScript);
|
|
10409
|
+
uses.push(...valueUses.uses);
|
|
10410
|
+
useId = valueUses.nextId;
|
|
10411
|
+
}
|
|
10412
|
+
if (nameNode && nameNode.type === "identifier") {
|
|
10413
|
+
const def = { id: defId++, variable: getNodeText(nameNode), line: nameNode.startPosition.row + 1, column: nameNode.startPosition.column, kind: "local" };
|
|
10414
|
+
defs.push(def);
|
|
10415
|
+
currentScope(scopeStack).set(def.variable, def.id);
|
|
10416
|
+
markDeclared(currentScope(scopeStack), def.variable);
|
|
10417
|
+
} else if (!value) {
|
|
10418
|
+
const resUses = extractUses(res, useId, scopeStack, isJavaScript);
|
|
10419
|
+
uses.push(...resUses.uses);
|
|
10420
|
+
useId = resUses.nextId;
|
|
10421
|
+
}
|
|
10422
|
+
}
|
|
10423
|
+
for (const part of stmt.namedChildren) {
|
|
10424
|
+
if (!part) continue;
|
|
10425
|
+
if (part.type === "block") {
|
|
10426
|
+
const r = processStatement2(part, defId, useId, scopeStack, isJavaScript);
|
|
10427
|
+
defs.push(...r.defs);
|
|
10428
|
+
uses.push(...r.uses);
|
|
10429
|
+
defId = r.nextDefId;
|
|
10430
|
+
useId = r.nextUseId;
|
|
10431
|
+
} else if (part.type === "catch_clause") {
|
|
10432
|
+
scopeStack.push(/* @__PURE__ */ new Map());
|
|
10433
|
+
for (const param of part.namedChildren) {
|
|
10434
|
+
if (!param || param.type !== "catch_formal_parameter") continue;
|
|
10435
|
+
const pname = param.childForFieldName("name");
|
|
10436
|
+
if (pname) {
|
|
10437
|
+
const def = { id: defId++, variable: getNodeText(pname), line: pname.startPosition.row + 1, column: pname.startPosition.column, kind: "local" };
|
|
10438
|
+
defs.push(def);
|
|
10439
|
+
currentScope(scopeStack).set(def.variable, def.id);
|
|
10440
|
+
markDeclared(currentScope(scopeStack), def.variable);
|
|
10441
|
+
}
|
|
10442
|
+
}
|
|
10443
|
+
const body2 = part.childForFieldName("body");
|
|
10444
|
+
if (body2) {
|
|
10445
|
+
const r = processStatement2(body2, defId, useId, scopeStack, isJavaScript);
|
|
10446
|
+
defs.push(...r.defs);
|
|
10447
|
+
uses.push(...r.uses);
|
|
10448
|
+
defId = r.nextDefId;
|
|
10449
|
+
useId = r.nextUseId;
|
|
10450
|
+
}
|
|
10451
|
+
carryBranchDefsOut(scopeStack.pop(), scopeStack, false);
|
|
10452
|
+
} else if (part.type === "finally_clause") {
|
|
10453
|
+
for (const b of part.namedChildren) {
|
|
10454
|
+
if (!b || b.type !== "block") continue;
|
|
10455
|
+
const r = processStatement2(b, defId, useId, scopeStack, isJavaScript);
|
|
10456
|
+
defs.push(...r.defs);
|
|
10457
|
+
uses.push(...r.uses);
|
|
10458
|
+
defId = r.nextDefId;
|
|
10459
|
+
useId = r.nextUseId;
|
|
10460
|
+
}
|
|
10461
|
+
}
|
|
10462
|
+
}
|
|
10463
|
+
carryBranchDefsOut(scopeStack.pop(), scopeStack, false);
|
|
10464
|
+
break;
|
|
10465
|
+
}
|
|
10364
10466
|
case "else_clause": {
|
|
10365
10467
|
for (let i2 = 0; i2 < stmt.childCount; i2++) {
|
|
10366
10468
|
const arm = stmt.child(i2);
|
|
@@ -10976,6 +11078,42 @@ function buildBashDFG(tree) {
|
|
|
10976
11078
|
defs.push(def);
|
|
10977
11079
|
currentScope(scopeStack).set(p, def.id);
|
|
10978
11080
|
}
|
|
11081
|
+
const bashFnDefs = /* @__PURE__ */ new Map();
|
|
11082
|
+
const bashFnByNodeId = /* @__PURE__ */ new Map();
|
|
11083
|
+
for (const fn of findNodes(tree.rootNode, "function_definition")) {
|
|
11084
|
+
const nameNode = fn.childForFieldName("name");
|
|
11085
|
+
if (!nameNode) continue;
|
|
11086
|
+
const fnName = getNodeText(nameNode);
|
|
11087
|
+
if (!fnName || bashFnDefs.has(fnName)) continue;
|
|
11088
|
+
const m = /* @__PURE__ */ new Map();
|
|
11089
|
+
for (const p of positionalParams) {
|
|
11090
|
+
const def = {
|
|
11091
|
+
id: defIdCounter++,
|
|
11092
|
+
variable: p,
|
|
11093
|
+
line: fn.startPosition.row + 1,
|
|
11094
|
+
kind: "param",
|
|
11095
|
+
expression: `bash-fn:${fnName}`
|
|
11096
|
+
};
|
|
11097
|
+
defs.push(def);
|
|
11098
|
+
m.set(p, def.id);
|
|
11099
|
+
}
|
|
11100
|
+
bashFnDefs.set(fnName, m);
|
|
11101
|
+
bashFnByNodeId.set(fn.id, fnName);
|
|
11102
|
+
}
|
|
11103
|
+
const enclosingBashFn = (node) => {
|
|
11104
|
+
let cur = node.parent;
|
|
11105
|
+
while (cur) {
|
|
11106
|
+
if (cur.type === "function_definition") return bashFnByNodeId.get(cur.id) ?? null;
|
|
11107
|
+
cur = cur.parent;
|
|
11108
|
+
}
|
|
11109
|
+
return null;
|
|
11110
|
+
};
|
|
11111
|
+
const positionalDef = (varName, node, fallback) => {
|
|
11112
|
+
if (!positionalParams.includes(varName)) return fallback;
|
|
11113
|
+
const fnName = enclosingBashFn(node);
|
|
11114
|
+
if (!fnName) return fallback;
|
|
11115
|
+
return bashFnDefs.get(fnName)?.get(varName) ?? fallback;
|
|
11116
|
+
};
|
|
10979
11117
|
walkTree(tree.rootNode, (node) => {
|
|
10980
11118
|
if (node.type === "variable_assignment") {
|
|
10981
11119
|
const nameNode = node.childForFieldName("name");
|
|
@@ -11028,7 +11166,7 @@ function buildBashDFG(tree) {
|
|
|
11028
11166
|
if (varNameNode) {
|
|
11029
11167
|
const varName = getNodeText(varNameNode);
|
|
11030
11168
|
if (varName && !varName.startsWith("?") && !varName.startsWith("#")) {
|
|
11031
|
-
let reachingDef = findReachingDef(varName, scopeStack);
|
|
11169
|
+
let reachingDef = positionalDef(varName, node, findReachingDef(varName, scopeStack));
|
|
11032
11170
|
if (reachingDef === null && !positionalParams.includes(varName)) {
|
|
11033
11171
|
const def = {
|
|
11034
11172
|
id: defIdCounter++,
|
|
@@ -11052,7 +11190,7 @@ function buildBashDFG(tree) {
|
|
|
11052
11190
|
const varNameNode = node.namedChildCount > 0 ? node.namedChild(0) : null;
|
|
11053
11191
|
if (varNameNode && varNameNode.type === "variable_name") {
|
|
11054
11192
|
const varName = getNodeText(varNameNode);
|
|
11055
|
-
let reachingDef = findReachingDef(varName, scopeStack);
|
|
11193
|
+
let reachingDef = positionalDef(varName, node, findReachingDef(varName, scopeStack));
|
|
11056
11194
|
if (reachingDef === null && !positionalParams.includes(varName)) {
|
|
11057
11195
|
const def = {
|
|
11058
11196
|
id: defIdCounter++,
|
|
@@ -11074,8 +11212,56 @@ function buildBashDFG(tree) {
|
|
|
11074
11212
|
}
|
|
11075
11213
|
});
|
|
11076
11214
|
const chains = computeChains(defs, uses);
|
|
11215
|
+
chains.push(...bashCallArgChains(tree, bashFnDefs, uses, positionalParams));
|
|
11077
11216
|
return { defs, uses, chains };
|
|
11078
11217
|
}
|
|
11218
|
+
function bashCallArgChains(tree, bashFnDefs, uses, positionalParams) {
|
|
11219
|
+
const chains = [];
|
|
11220
|
+
if (bashFnDefs.size === 0) return chains;
|
|
11221
|
+
const renumbers = /* @__PURE__ */ new Set();
|
|
11222
|
+
for (const fn of findNodes(tree.rootNode, "function_definition")) {
|
|
11223
|
+
const nameNode = fn.childForFieldName("name");
|
|
11224
|
+
if (!nameNode) continue;
|
|
11225
|
+
if (/\bshift\b|\bset\s+--/.test(getNodeText(fn))) renumbers.add(getNodeText(nameNode));
|
|
11226
|
+
}
|
|
11227
|
+
const usesByLine = /* @__PURE__ */ new Map();
|
|
11228
|
+
for (const u of uses) {
|
|
11229
|
+
const l = usesByLine.get(u.line) ?? [];
|
|
11230
|
+
l.push(u);
|
|
11231
|
+
usesByLine.set(u.line, l);
|
|
11232
|
+
}
|
|
11233
|
+
for (const cmd of findNodes(tree.rootNode, "command")) {
|
|
11234
|
+
const nameNode = cmd.childForFieldName("name");
|
|
11235
|
+
if (!nameNode) continue;
|
|
11236
|
+
const fnDefs = bashFnDefs.get(getNodeText(nameNode));
|
|
11237
|
+
if (!fnDefs) continue;
|
|
11238
|
+
const fnName = getNodeText(nameNode);
|
|
11239
|
+
let position = 0;
|
|
11240
|
+
for (let i2 = 0; i2 < cmd.childCount; i2++) {
|
|
11241
|
+
const arg = cmd.child(i2);
|
|
11242
|
+
if (!arg || arg.id === nameNode.id) continue;
|
|
11243
|
+
if (arg.type.includes("redirect") || arg.type === "heredoc_body" || arg.type === "file_descriptor") continue;
|
|
11244
|
+
const text = getNodeText(arg);
|
|
11245
|
+
if (!text.trim()) continue;
|
|
11246
|
+
position++;
|
|
11247
|
+
const names = /* @__PURE__ */ new Set();
|
|
11248
|
+
const re = /\$\{?([A-Za-z_][A-Za-z0-9_]*|[0-9@*])/g;
|
|
11249
|
+
let m;
|
|
11250
|
+
while ((m = re.exec(text)) !== null) names.add(m[1]);
|
|
11251
|
+
if (names.size === 0) continue;
|
|
11252
|
+
const spreads = /\$\{?[@*]/.test(text);
|
|
11253
|
+
const targets = spreads || renumbers.has(fnName) ? positionalParams.map((p) => fnDefs.get(p)).filter((d) => d !== void 0) : [fnDefs.get(String(position))].filter((d) => d !== void 0);
|
|
11254
|
+
const lineUses = usesByLine.get(arg.startPosition.row + 1) ?? [];
|
|
11255
|
+
for (const name2 of names) {
|
|
11256
|
+
for (const u of lineUses) {
|
|
11257
|
+
if (u.variable !== name2 || u.def_id === null) continue;
|
|
11258
|
+
for (const t of targets) chains.push({ from_def: u.def_id, to_def: t, via: name2 });
|
|
11259
|
+
}
|
|
11260
|
+
}
|
|
11261
|
+
}
|
|
11262
|
+
}
|
|
11263
|
+
return chains;
|
|
11264
|
+
}
|
|
11079
11265
|
function buildRustDFG(tree, cache) {
|
|
11080
11266
|
const defs = [];
|
|
11081
11267
|
const uses = [];
|
|
@@ -11785,6 +11971,198 @@ function javaReplaceCrLfCoverage(method, rawFrom) {
|
|
|
11785
11971
|
return { cr, lf };
|
|
11786
11972
|
}
|
|
11787
11973
|
|
|
11974
|
+
// src/analysis/js-crlf-strip.ts
|
|
11975
|
+
var MAX_PATTERN_LENGTH = 200;
|
|
11976
|
+
var NEUTRAL_AFTER_REPLACE = /* @__PURE__ */ new Set([
|
|
11977
|
+
"replace",
|
|
11978
|
+
"replaceAll",
|
|
11979
|
+
"trim",
|
|
11980
|
+
"trimStart",
|
|
11981
|
+
"trimEnd",
|
|
11982
|
+
"toString",
|
|
11983
|
+
"toLowerCase",
|
|
11984
|
+
"toUpperCase",
|
|
11985
|
+
"slice",
|
|
11986
|
+
"substring"
|
|
11987
|
+
]);
|
|
11988
|
+
var PROBE = "a\r\rb\n\nc\r\nd\n\re x ";
|
|
11989
|
+
function readQuoted(s, i2) {
|
|
11990
|
+
const q = s[i2];
|
|
11991
|
+
if (q !== '"' && q !== "'" && q !== "`") return null;
|
|
11992
|
+
let out2 = "";
|
|
11993
|
+
for (let j = i2 + 1; j < s.length; j++) {
|
|
11994
|
+
const c = s[j];
|
|
11995
|
+
if (c === q) return { value: out2, end: j + 1 };
|
|
11996
|
+
if (q === "`" && c === "$" && s[j + 1] === "{") return null;
|
|
11997
|
+
if (c === "\\") {
|
|
11998
|
+
const n = s[j + 1];
|
|
11999
|
+
if (n === void 0) return null;
|
|
12000
|
+
switch (n) {
|
|
12001
|
+
case "n":
|
|
12002
|
+
out2 += "\n";
|
|
12003
|
+
j++;
|
|
12004
|
+
continue;
|
|
12005
|
+
case "r":
|
|
12006
|
+
out2 += "\r";
|
|
12007
|
+
j++;
|
|
12008
|
+
continue;
|
|
12009
|
+
case "t":
|
|
12010
|
+
out2 += " ";
|
|
12011
|
+
j++;
|
|
12012
|
+
continue;
|
|
12013
|
+
case "0":
|
|
12014
|
+
out2 += "\0";
|
|
12015
|
+
j++;
|
|
12016
|
+
continue;
|
|
12017
|
+
case "x": {
|
|
12018
|
+
const h = /^[0-9a-fA-F]{2}/.exec(s.slice(j + 2, j + 4));
|
|
12019
|
+
if (!h) return null;
|
|
12020
|
+
out2 += String.fromCharCode(parseInt(h[0], 16));
|
|
12021
|
+
j += 3;
|
|
12022
|
+
continue;
|
|
12023
|
+
}
|
|
12024
|
+
case "u": {
|
|
12025
|
+
const h = /^[0-9a-fA-F]{4}/.exec(s.slice(j + 2, j + 6));
|
|
12026
|
+
if (!h) return null;
|
|
12027
|
+
out2 += String.fromCharCode(parseInt(h[0], 16));
|
|
12028
|
+
j += 5;
|
|
12029
|
+
continue;
|
|
12030
|
+
}
|
|
12031
|
+
default:
|
|
12032
|
+
out2 += n;
|
|
12033
|
+
j++;
|
|
12034
|
+
continue;
|
|
12035
|
+
}
|
|
12036
|
+
}
|
|
12037
|
+
if (c === "\n" && q !== "`") return null;
|
|
12038
|
+
out2 += c;
|
|
12039
|
+
}
|
|
12040
|
+
return null;
|
|
12041
|
+
}
|
|
12042
|
+
function readRegexLiteral(s, i2) {
|
|
12043
|
+
if (s[i2] !== "/") return null;
|
|
12044
|
+
let inClass = false;
|
|
12045
|
+
for (let j = i2 + 1; j < s.length; j++) {
|
|
12046
|
+
const c = s[j];
|
|
12047
|
+
if (c === "\n") return null;
|
|
12048
|
+
if (c === "\\") {
|
|
12049
|
+
j++;
|
|
12050
|
+
continue;
|
|
12051
|
+
}
|
|
12052
|
+
if (inClass) {
|
|
12053
|
+
if (c === "]") inClass = false;
|
|
12054
|
+
continue;
|
|
12055
|
+
}
|
|
12056
|
+
if (c === "[") {
|
|
12057
|
+
inClass = true;
|
|
12058
|
+
continue;
|
|
12059
|
+
}
|
|
12060
|
+
if (c === "/") {
|
|
12061
|
+
const flags2 = /^[dgimsuvy]*/.exec(s.slice(j + 1))[0];
|
|
12062
|
+
return { source: s.slice(i2 + 1, j), flags: flags2, end: j + 1 + flags2.length };
|
|
12063
|
+
}
|
|
12064
|
+
}
|
|
12065
|
+
return null;
|
|
12066
|
+
}
|
|
12067
|
+
var skipWs = (s, i2) => {
|
|
12068
|
+
while (i2 < s.length && /\s/.test(s[i2])) i2++;
|
|
12069
|
+
return i2;
|
|
12070
|
+
};
|
|
12071
|
+
function parseSteps(chainText) {
|
|
12072
|
+
const steps = [];
|
|
12073
|
+
const callRe = /\.\s*(replaceAll|replace)\s*\(/g;
|
|
12074
|
+
let m;
|
|
12075
|
+
while ((m = callRe.exec(chainText)) !== null) {
|
|
12076
|
+
const kind = m[1];
|
|
12077
|
+
let i2 = skipWs(chainText, m.index + m[0].length);
|
|
12078
|
+
let pattern;
|
|
12079
|
+
const rx = readRegexLiteral(chainText, i2);
|
|
12080
|
+
if (rx) {
|
|
12081
|
+
if (rx.source.length > MAX_PATTERN_LENGTH) return null;
|
|
12082
|
+
try {
|
|
12083
|
+
pattern = new RegExp(rx.source, rx.flags);
|
|
12084
|
+
} catch {
|
|
12085
|
+
return null;
|
|
12086
|
+
}
|
|
12087
|
+
i2 = rx.end;
|
|
12088
|
+
} else {
|
|
12089
|
+
const str = readQuoted(chainText, i2);
|
|
12090
|
+
if (!str) return null;
|
|
12091
|
+
pattern = str.value;
|
|
12092
|
+
i2 = str.end;
|
|
12093
|
+
}
|
|
12094
|
+
i2 = skipWs(chainText, i2);
|
|
12095
|
+
if (chainText[i2] !== ",") return null;
|
|
12096
|
+
i2 = skipWs(chainText, i2 + 1);
|
|
12097
|
+
const rep = readQuoted(chainText, i2);
|
|
12098
|
+
if (!rep) return null;
|
|
12099
|
+
i2 = skipWs(chainText, rep.end);
|
|
12100
|
+
if (chainText[i2] !== ")") return null;
|
|
12101
|
+
steps.push({ kind, pattern, replacement: rep.value });
|
|
12102
|
+
callRe.lastIndex = i2 + 1;
|
|
12103
|
+
}
|
|
12104
|
+
return steps;
|
|
12105
|
+
}
|
|
12106
|
+
function run2(steps, input) {
|
|
12107
|
+
let s = input;
|
|
12108
|
+
try {
|
|
12109
|
+
for (const st of steps) {
|
|
12110
|
+
if (st.kind === "replaceAll" && st.pattern instanceof RegExp && !st.pattern.global) return null;
|
|
12111
|
+
s = st.kind === "replaceAll" ? s.replaceAll(st.pattern, st.replacement) : s.replace(st.pattern, st.replacement);
|
|
12112
|
+
}
|
|
12113
|
+
} catch {
|
|
12114
|
+
return null;
|
|
12115
|
+
}
|
|
12116
|
+
return s;
|
|
12117
|
+
}
|
|
12118
|
+
function jsReplaceChainVerdict(chainText) {
|
|
12119
|
+
const steps = parseSteps(chainText);
|
|
12120
|
+
if (!steps || steps.length === 0) return "undecided";
|
|
12121
|
+
const first = chainText.search(/\.\s*(?:replaceAll|replace)\s*\(/);
|
|
12122
|
+
for (const seg of chainText.slice(first + 1).matchAll(/\.\s*([A-Za-z_$][\w$]*)\s*\(/g)) {
|
|
12123
|
+
if (!NEUTRAL_AFTER_REPLACE.has(seg[1])) return "undecided";
|
|
12124
|
+
}
|
|
12125
|
+
const out2 = run2(steps, PROBE);
|
|
12126
|
+
if (out2 === null) return "undecided";
|
|
12127
|
+
return out2.includes("\r") || out2.includes("\n") ? "leaves-crlf" : "strips-crlf";
|
|
12128
|
+
}
|
|
12129
|
+
function jsChainTextAt(sourceLines, line, column) {
|
|
12130
|
+
const text = [(sourceLines[line - 1] ?? "").slice(column), ...sourceLines.slice(line, line + 7)].join("\n");
|
|
12131
|
+
let depth = 0;
|
|
12132
|
+
let prevSig = "";
|
|
12133
|
+
let i2 = 0;
|
|
12134
|
+
for (; i2 < text.length; i2++) {
|
|
12135
|
+
const c = text[i2];
|
|
12136
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
12137
|
+
for (i2++; i2 < text.length && text[i2] !== c; i2++) if (text[i2] === "\\") i2++;
|
|
12138
|
+
prevSig = c;
|
|
12139
|
+
continue;
|
|
12140
|
+
}
|
|
12141
|
+
if (c === "/" && depth > 0 && (prevSig === "(" || prevSig === ",")) {
|
|
12142
|
+
const rx = readRegexLiteral(text, i2);
|
|
12143
|
+
if (rx) {
|
|
12144
|
+
i2 = rx.end - 1;
|
|
12145
|
+
prevSig = "/";
|
|
12146
|
+
continue;
|
|
12147
|
+
}
|
|
12148
|
+
}
|
|
12149
|
+
if (c === "(" || c === "[" || c === "{") {
|
|
12150
|
+
depth++;
|
|
12151
|
+
prevSig = c;
|
|
12152
|
+
continue;
|
|
12153
|
+
}
|
|
12154
|
+
if (c === ")" || c === "]" || c === "}") {
|
|
12155
|
+
if (depth === 0) break;
|
|
12156
|
+
depth--;
|
|
12157
|
+
prevSig = c;
|
|
12158
|
+
continue;
|
|
12159
|
+
}
|
|
12160
|
+
if (depth === 0 && !/[\w$.\s]/.test(c)) break;
|
|
12161
|
+
if (!/\s/.test(c)) prevSig = c;
|
|
12162
|
+
}
|
|
12163
|
+
return text.slice(0, i2);
|
|
12164
|
+
}
|
|
12165
|
+
|
|
11788
12166
|
// src/analysis/passes/language-sources-pass.ts
|
|
11789
12167
|
var JINJA_LITERAL_ARG_RE_FRAG = `(?:"[^"\\\\]*"|'[^'\\\\]*')`;
|
|
11790
12168
|
var JINJA_SAFE_RTS_RE = new RegExp(
|
|
@@ -12568,6 +12946,10 @@ var DEFAULT_SOURCES = [
|
|
|
12568
12946
|
{ method: "get", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
|
|
12569
12947
|
{ method: "hget", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
|
|
12570
12948
|
{ method: "mget", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
|
|
12949
|
+
// Node redis clients are often bound to `cache` from `createClient()`; class
|
|
12950
|
+
// `cache` is scoped to JS/TS (Python Django cache above) and Map.get is
|
|
12951
|
+
// filtered in sink-filter-pass (#818 src14).
|
|
12952
|
+
{ method: "get", class: "cache", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
|
|
12571
12953
|
{ method: "get", class: "Jedis", type: "db_input", severity: "medium", return_tainted: true, languages: ["java"] },
|
|
12572
12954
|
{ method: "hget", class: "Jedis", type: "db_input", severity: "medium", return_tainted: true, languages: ["java"] },
|
|
12573
12955
|
{ method: "mget", class: "Jedis", type: "db_input", severity: "medium", return_tainted: true, languages: ["java"] },
|
|
@@ -12747,7 +13129,9 @@ var TRUST_BOUNDARY_FRAMEWORK_SINKS = [
|
|
|
12747
13129
|
// on arg[1] (the *Cookie) catches struct-literal composite tainting.
|
|
12748
13130
|
{ method: "SetCookie", class: "http", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
12749
13131
|
// gin: c.SetCookie(name, value, maxAge, path, domain, secure, httpOnly).
|
|
12750
|
-
{ method: "SetCookie", class: "Context", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["go"] }
|
|
13132
|
+
{ method: "SetCookie", class: "Context", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
13133
|
+
// --- Rust: actix-session / tower-sessions write -------------------------
|
|
13134
|
+
{ method: "insert", class: "Session", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["rust"] }
|
|
12751
13135
|
];
|
|
12752
13136
|
var DEFAULT_SINKS = [
|
|
12753
13137
|
// SQL Injection (CWE-89)
|
|
@@ -13408,7 +13792,9 @@ var DEFAULT_SINKS = [
|
|
|
13408
13792
|
{ method: "addParametersToContext", class: "StaticParametersInterceptor", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
|
|
13409
13793
|
{ method: "build", class: "HttpParameters", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
|
|
13410
13794
|
// Cron expression parsing (DoS/ReDoS)
|
|
13411
|
-
|
|
13795
|
+
// cognium-dev#998 — Java cron-utils `CronParser`; scoped to Java so a JS/TS
|
|
13796
|
+
// `parser.parse(...)` (tree-sitter, a lockfile parser) never matches it by name.
|
|
13797
|
+
{ method: "parse", class: "CronParser", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
13412
13798
|
{ method: "isValid", class: "CronValidator", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0] },
|
|
13413
13799
|
// ff4j feature flags
|
|
13414
13800
|
{ method: "check", class: "FF4j", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0] },
|
|
@@ -13505,9 +13891,13 @@ var DEFAULT_SINKS = [
|
|
|
13505
13891
|
{ method: "selectNodes", class: "Node", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0] },
|
|
13506
13892
|
{ method: "selectSingleNode", class: "Node", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0] },
|
|
13507
13893
|
// XXE - XML External Entity (CWE-611)
|
|
13508
|
-
|
|
13509
|
-
|
|
13510
|
-
|
|
13894
|
+
// cognium-dev#998 — JAXP parsers are JVM classes. Scoped to Java so the
|
|
13895
|
+
// receiver-name heuristic (`parser` → SAXParser) cannot turn a JS/TS
|
|
13896
|
+
// `parser.parse(text)` into an XXE sink. JS/TS XML parsers (sax, xml2js,
|
|
13897
|
+
// libxmljs, DOMParser) have their own entries.
|
|
13898
|
+
{ method: "parse", class: "DocumentBuilder", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
13899
|
+
{ method: "parse", class: "SAXParser", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
13900
|
+
{ method: "parse", class: "XMLReader", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
13511
13901
|
{ method: "unmarshal", class: "Unmarshaller", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0] },
|
|
13512
13902
|
{ method: "newTransformer", class: "TransformerFactory", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0] },
|
|
13513
13903
|
{ method: "transform", class: "Transformer", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0] },
|
|
@@ -13762,6 +14152,46 @@ var DEFAULT_SINKS = [
|
|
|
13762
14152
|
{ method: "each", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
|
|
13763
14153
|
{ method: "get", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
|
|
13764
14154
|
{ method: "exec", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
|
|
14155
|
+
// DuckDB from JS/TS: `@duckdb/node-api` (DuckDBConnection), the classic
|
|
14156
|
+
// `duckdb` / `duckdb-async` bindings (Database / Connection) and
|
|
14157
|
+
// `react-native-duckdb` (Database / Transaction). The method names are
|
|
14158
|
+
// generic and the receivers come from factories (`await instance.connect()`,
|
|
14159
|
+
// `open({...})`), so the receiver cannot be typed: the import is the gate
|
|
14160
|
+
// (`requires_import`). Only the SQL text (arg 0) is a sink — values passed
|
|
14161
|
+
// as bound parameters (`run(sql, [v])`, `execute(sql, params)`,
|
|
14162
|
+
// `executeNamed(sql, {k: v})`, `stmt.bind(...)`) are parameterised and do
|
|
14163
|
+
// not reach it.
|
|
14164
|
+
{ method: "run", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api", "duckdb", "duckdb-async"] },
|
|
14165
|
+
{ method: "prepare", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api", "duckdb", "duckdb-async", "react-native-duckdb"] },
|
|
14166
|
+
{ method: "stream", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api", "duckdb", "duckdb-async", "react-native-duckdb"] },
|
|
14167
|
+
{ method: "runAndRead", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14168
|
+
{ method: "runAndReadAll", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14169
|
+
{ method: "runAndReadUntil", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14170
|
+
{ method: "streamAndRead", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14171
|
+
{ method: "streamAndReadAll", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14172
|
+
{ method: "streamAndReadUntil", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14173
|
+
{ method: "start", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14174
|
+
{ method: "startStream", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14175
|
+
{ method: "extractStatements", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14176
|
+
{ method: "all", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
|
|
14177
|
+
{ method: "each", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
|
|
14178
|
+
{ method: "exec", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
|
|
14179
|
+
{ method: "arrowIPCAll", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
|
|
14180
|
+
{ method: "arrowIPCStream", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
|
|
14181
|
+
{ method: "execute", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
|
|
14182
|
+
{ method: "executeSync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
|
|
14183
|
+
{ method: "executeNamed", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
|
|
14184
|
+
{ method: "executeSyncNamed", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
|
|
14185
|
+
{ method: "streamNamed", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
|
|
14186
|
+
// RxDB (Mango queries, any storage incl. SQLite): a selector or query
|
|
14187
|
+
// object, a field name passed to the query builder, or a `$regex` pattern
|
|
14188
|
+
// under caller control is NoSQL operator injection (CWE-943). Gated on the
|
|
14189
|
+
// `rxdb` import for the same reason as DuckDB above. `find` is already a
|
|
14190
|
+
// classless JS NoSQL sink in the JavaScript plugin.
|
|
14191
|
+
{ method: "findOne", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["rxdb"] },
|
|
14192
|
+
{ method: "count", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["rxdb"] },
|
|
14193
|
+
{ method: "where", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["rxdb"] },
|
|
14194
|
+
{ method: "regex", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["rxdb"] },
|
|
13765
14195
|
// TypeORM QueryBuilder raw fragments (CWE-89). TypeORM's raw `.query(sql)`
|
|
13766
14196
|
// escape hatch is already covered by the classless `query` sink in the JS
|
|
13767
14197
|
// plugin (getBuiltinSinks), so it is NOT re-registered here — the dedup
|
|
@@ -13872,6 +14302,8 @@ var DEFAULT_SINKS = [
|
|
|
13872
14302
|
{ method: "get", class: "http", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
13873
14303
|
{ method: "request", class: "https", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
13874
14304
|
{ method: "get", class: "https", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
14305
|
+
// Node.js dns.promises (CWE-918 / NUL-in-name SSRF class — #895)
|
|
14306
|
+
{ method: "resolve", class: "dns.promises", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
13875
14307
|
// needle library (used in NodeGoat)
|
|
13876
14308
|
{ method: "get", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
13877
14309
|
{ method: "post", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
@@ -14366,7 +14798,7 @@ var DEFAULT_SINKS = [
|
|
|
14366
14798
|
// receiver-taint or operator-LHS-taint tracking — the format string is the
|
|
14367
14799
|
// receiver, not an argument. Deferred to Sprint 6 (#86 follow-up).
|
|
14368
14800
|
// C-style: printf / fprintf / sprintf / snprintf via ctypes/cffi.
|
|
14369
|
-
{ method: "printf", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
14801
|
+
{ method: "printf", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["python", "rust"] },
|
|
14370
14802
|
{ method: "fprintf", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [1], languages: ["python"] },
|
|
14371
14803
|
// Go: fmt.Sprintf/Printf/Fprintf/Errorf — format string is first/second arg
|
|
14372
14804
|
{ method: "Sprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
@@ -15435,198 +15867,6 @@ function getDefaultConfig() {
|
|
|
15435
15867
|
};
|
|
15436
15868
|
}
|
|
15437
15869
|
|
|
15438
|
-
// src/analysis/js-crlf-strip.ts
|
|
15439
|
-
var MAX_PATTERN_LENGTH = 200;
|
|
15440
|
-
var NEUTRAL_AFTER_REPLACE = /* @__PURE__ */ new Set([
|
|
15441
|
-
"replace",
|
|
15442
|
-
"replaceAll",
|
|
15443
|
-
"trim",
|
|
15444
|
-
"trimStart",
|
|
15445
|
-
"trimEnd",
|
|
15446
|
-
"toString",
|
|
15447
|
-
"toLowerCase",
|
|
15448
|
-
"toUpperCase",
|
|
15449
|
-
"slice",
|
|
15450
|
-
"substring"
|
|
15451
|
-
]);
|
|
15452
|
-
var PROBE = "a\r\rb\n\nc\r\nd\n\re x ";
|
|
15453
|
-
function readQuoted(s, i2) {
|
|
15454
|
-
const q = s[i2];
|
|
15455
|
-
if (q !== '"' && q !== "'" && q !== "`") return null;
|
|
15456
|
-
let out2 = "";
|
|
15457
|
-
for (let j = i2 + 1; j < s.length; j++) {
|
|
15458
|
-
const c = s[j];
|
|
15459
|
-
if (c === q) return { value: out2, end: j + 1 };
|
|
15460
|
-
if (q === "`" && c === "$" && s[j + 1] === "{") return null;
|
|
15461
|
-
if (c === "\\") {
|
|
15462
|
-
const n = s[j + 1];
|
|
15463
|
-
if (n === void 0) return null;
|
|
15464
|
-
switch (n) {
|
|
15465
|
-
case "n":
|
|
15466
|
-
out2 += "\n";
|
|
15467
|
-
j++;
|
|
15468
|
-
continue;
|
|
15469
|
-
case "r":
|
|
15470
|
-
out2 += "\r";
|
|
15471
|
-
j++;
|
|
15472
|
-
continue;
|
|
15473
|
-
case "t":
|
|
15474
|
-
out2 += " ";
|
|
15475
|
-
j++;
|
|
15476
|
-
continue;
|
|
15477
|
-
case "0":
|
|
15478
|
-
out2 += "\0";
|
|
15479
|
-
j++;
|
|
15480
|
-
continue;
|
|
15481
|
-
case "x": {
|
|
15482
|
-
const h = /^[0-9a-fA-F]{2}/.exec(s.slice(j + 2, j + 4));
|
|
15483
|
-
if (!h) return null;
|
|
15484
|
-
out2 += String.fromCharCode(parseInt(h[0], 16));
|
|
15485
|
-
j += 3;
|
|
15486
|
-
continue;
|
|
15487
|
-
}
|
|
15488
|
-
case "u": {
|
|
15489
|
-
const h = /^[0-9a-fA-F]{4}/.exec(s.slice(j + 2, j + 6));
|
|
15490
|
-
if (!h) return null;
|
|
15491
|
-
out2 += String.fromCharCode(parseInt(h[0], 16));
|
|
15492
|
-
j += 5;
|
|
15493
|
-
continue;
|
|
15494
|
-
}
|
|
15495
|
-
default:
|
|
15496
|
-
out2 += n;
|
|
15497
|
-
j++;
|
|
15498
|
-
continue;
|
|
15499
|
-
}
|
|
15500
|
-
}
|
|
15501
|
-
if (c === "\n" && q !== "`") return null;
|
|
15502
|
-
out2 += c;
|
|
15503
|
-
}
|
|
15504
|
-
return null;
|
|
15505
|
-
}
|
|
15506
|
-
function readRegexLiteral(s, i2) {
|
|
15507
|
-
if (s[i2] !== "/") return null;
|
|
15508
|
-
let inClass = false;
|
|
15509
|
-
for (let j = i2 + 1; j < s.length; j++) {
|
|
15510
|
-
const c = s[j];
|
|
15511
|
-
if (c === "\n") return null;
|
|
15512
|
-
if (c === "\\") {
|
|
15513
|
-
j++;
|
|
15514
|
-
continue;
|
|
15515
|
-
}
|
|
15516
|
-
if (inClass) {
|
|
15517
|
-
if (c === "]") inClass = false;
|
|
15518
|
-
continue;
|
|
15519
|
-
}
|
|
15520
|
-
if (c === "[") {
|
|
15521
|
-
inClass = true;
|
|
15522
|
-
continue;
|
|
15523
|
-
}
|
|
15524
|
-
if (c === "/") {
|
|
15525
|
-
const flags2 = /^[dgimsuvy]*/.exec(s.slice(j + 1))[0];
|
|
15526
|
-
return { source: s.slice(i2 + 1, j), flags: flags2, end: j + 1 + flags2.length };
|
|
15527
|
-
}
|
|
15528
|
-
}
|
|
15529
|
-
return null;
|
|
15530
|
-
}
|
|
15531
|
-
var skipWs = (s, i2) => {
|
|
15532
|
-
while (i2 < s.length && /\s/.test(s[i2])) i2++;
|
|
15533
|
-
return i2;
|
|
15534
|
-
};
|
|
15535
|
-
function parseSteps(chainText) {
|
|
15536
|
-
const steps = [];
|
|
15537
|
-
const callRe = /\.\s*(replaceAll|replace)\s*\(/g;
|
|
15538
|
-
let m;
|
|
15539
|
-
while ((m = callRe.exec(chainText)) !== null) {
|
|
15540
|
-
const kind = m[1];
|
|
15541
|
-
let i2 = skipWs(chainText, m.index + m[0].length);
|
|
15542
|
-
let pattern;
|
|
15543
|
-
const rx = readRegexLiteral(chainText, i2);
|
|
15544
|
-
if (rx) {
|
|
15545
|
-
if (rx.source.length > MAX_PATTERN_LENGTH) return null;
|
|
15546
|
-
try {
|
|
15547
|
-
pattern = new RegExp(rx.source, rx.flags);
|
|
15548
|
-
} catch {
|
|
15549
|
-
return null;
|
|
15550
|
-
}
|
|
15551
|
-
i2 = rx.end;
|
|
15552
|
-
} else {
|
|
15553
|
-
const str = readQuoted(chainText, i2);
|
|
15554
|
-
if (!str) return null;
|
|
15555
|
-
pattern = str.value;
|
|
15556
|
-
i2 = str.end;
|
|
15557
|
-
}
|
|
15558
|
-
i2 = skipWs(chainText, i2);
|
|
15559
|
-
if (chainText[i2] !== ",") return null;
|
|
15560
|
-
i2 = skipWs(chainText, i2 + 1);
|
|
15561
|
-
const rep = readQuoted(chainText, i2);
|
|
15562
|
-
if (!rep) return null;
|
|
15563
|
-
i2 = skipWs(chainText, rep.end);
|
|
15564
|
-
if (chainText[i2] !== ")") return null;
|
|
15565
|
-
steps.push({ kind, pattern, replacement: rep.value });
|
|
15566
|
-
callRe.lastIndex = i2 + 1;
|
|
15567
|
-
}
|
|
15568
|
-
return steps;
|
|
15569
|
-
}
|
|
15570
|
-
function run2(steps, input) {
|
|
15571
|
-
let s = input;
|
|
15572
|
-
try {
|
|
15573
|
-
for (const st of steps) {
|
|
15574
|
-
if (st.kind === "replaceAll" && st.pattern instanceof RegExp && !st.pattern.global) return null;
|
|
15575
|
-
s = st.kind === "replaceAll" ? s.replaceAll(st.pattern, st.replacement) : s.replace(st.pattern, st.replacement);
|
|
15576
|
-
}
|
|
15577
|
-
} catch {
|
|
15578
|
-
return null;
|
|
15579
|
-
}
|
|
15580
|
-
return s;
|
|
15581
|
-
}
|
|
15582
|
-
function jsReplaceChainVerdict(chainText) {
|
|
15583
|
-
const steps = parseSteps(chainText);
|
|
15584
|
-
if (!steps || steps.length === 0) return "undecided";
|
|
15585
|
-
const first = chainText.search(/\.\s*(?:replaceAll|replace)\s*\(/);
|
|
15586
|
-
for (const seg of chainText.slice(first + 1).matchAll(/\.\s*([A-Za-z_$][\w$]*)\s*\(/g)) {
|
|
15587
|
-
if (!NEUTRAL_AFTER_REPLACE.has(seg[1])) return "undecided";
|
|
15588
|
-
}
|
|
15589
|
-
const out2 = run2(steps, PROBE);
|
|
15590
|
-
if (out2 === null) return "undecided";
|
|
15591
|
-
return out2.includes("\r") || out2.includes("\n") ? "leaves-crlf" : "strips-crlf";
|
|
15592
|
-
}
|
|
15593
|
-
function jsChainTextAt(sourceLines, line, column) {
|
|
15594
|
-
const text = [(sourceLines[line - 1] ?? "").slice(column), ...sourceLines.slice(line, line + 7)].join("\n");
|
|
15595
|
-
let depth = 0;
|
|
15596
|
-
let prevSig = "";
|
|
15597
|
-
let i2 = 0;
|
|
15598
|
-
for (; i2 < text.length; i2++) {
|
|
15599
|
-
const c = text[i2];
|
|
15600
|
-
if (c === '"' || c === "'" || c === "`") {
|
|
15601
|
-
for (i2++; i2 < text.length && text[i2] !== c; i2++) if (text[i2] === "\\") i2++;
|
|
15602
|
-
prevSig = c;
|
|
15603
|
-
continue;
|
|
15604
|
-
}
|
|
15605
|
-
if (c === "/" && depth > 0 && (prevSig === "(" || prevSig === ",")) {
|
|
15606
|
-
const rx = readRegexLiteral(text, i2);
|
|
15607
|
-
if (rx) {
|
|
15608
|
-
i2 = rx.end - 1;
|
|
15609
|
-
prevSig = "/";
|
|
15610
|
-
continue;
|
|
15611
|
-
}
|
|
15612
|
-
}
|
|
15613
|
-
if (c === "(" || c === "[" || c === "{") {
|
|
15614
|
-
depth++;
|
|
15615
|
-
prevSig = c;
|
|
15616
|
-
continue;
|
|
15617
|
-
}
|
|
15618
|
-
if (c === ")" || c === "]" || c === "}") {
|
|
15619
|
-
if (depth === 0) break;
|
|
15620
|
-
depth--;
|
|
15621
|
-
prevSig = c;
|
|
15622
|
-
continue;
|
|
15623
|
-
}
|
|
15624
|
-
if (depth === 0 && !/[\w$.\s]/.test(c)) break;
|
|
15625
|
-
if (!/\s/.test(c)) prevSig = c;
|
|
15626
|
-
}
|
|
15627
|
-
return text.slice(0, i2);
|
|
15628
|
-
}
|
|
15629
|
-
|
|
15630
15870
|
// src/analysis/constant-propagation/patterns.ts
|
|
15631
15871
|
var TAINT_PATTERNS = [
|
|
15632
15872
|
// HTTP Sources (Servlet API)
|
|
@@ -16016,13 +16256,35 @@ function dropCSharpObjectCarriedWaypoints(sinks, calls) {
|
|
|
16016
16256
|
function analyzeTaint(calls, types, config = getDefaultConfig(), typeHierarchy, language, code, imports) {
|
|
16017
16257
|
const sourceLines = code !== void 0 ? code.split("\n") : void 0;
|
|
16018
16258
|
const sources = findSources(calls, types, config.sources, sourceLines, language, imports);
|
|
16019
|
-
let sinkPatterns =
|
|
16259
|
+
let sinkPatterns = config.sinks.filter((p) => sinkPatternImportApplies(p, imports));
|
|
16260
|
+
sinkPatterns = expandPromisifyAliases(sinkPatterns, sourceLines, language);
|
|
16020
16261
|
sinkPatterns = expandIndirectEvalAliases(sinkPatterns, sourceLines, language);
|
|
16021
16262
|
const sinks = findSinks(calls, sinkPatterns, typeHierarchy, language, sourceLines, types);
|
|
16022
16263
|
const gatedSinks = language === "csharp" ? dropCSharpObjectCarriedWaypoints(sinks, calls) : sinks;
|
|
16023
16264
|
const sanitizers = findSanitizers(calls, types, config.sanitizers, sourceLines, language);
|
|
16024
16265
|
return { sources, sinks: gatedSinks, sanitizers };
|
|
16025
16266
|
}
|
|
16267
|
+
function isBoundParameterCall(call, pattern, sourceLines) {
|
|
16268
|
+
if (!pattern.requires_import || pattern.type !== "sql_injection") return false;
|
|
16269
|
+
const first = call.arguments[0]?.expression?.trim() ?? "";
|
|
16270
|
+
if (first.startsWith("[") || first.startsWith("{")) return true;
|
|
16271
|
+
const receiver = call.receiver;
|
|
16272
|
+
if (!receiver || !sourceLines || !/^[A-Za-z_$][\w$]*$/.test(receiver)) return false;
|
|
16273
|
+
const assignedFromPrepare = new RegExp(
|
|
16274
|
+
`\\b${receiver.replace(/\$/g, "\\$")}\\s*(?::[^=]+)?=\\s*(?:await\\s+)?[\\w$.]+\\.prepare\\s*\\(`
|
|
16275
|
+
);
|
|
16276
|
+
return sourceLines.some((line) => assignedFromPrepare.test(line));
|
|
16277
|
+
}
|
|
16278
|
+
function sinkPatternImportApplies(pattern, imports) {
|
|
16279
|
+
const required = pattern.requires_import;
|
|
16280
|
+
if (!required || required.length === 0) return true;
|
|
16281
|
+
if (!imports || imports.length === 0) return false;
|
|
16282
|
+
return imports.some((imp) => {
|
|
16283
|
+
const from = imp.from_package;
|
|
16284
|
+
if (!from) return false;
|
|
16285
|
+
return required.some((m) => from === m || from.startsWith(m + "/"));
|
|
16286
|
+
});
|
|
16287
|
+
}
|
|
16026
16288
|
function sinkPatternAppliesTo(pattern, language) {
|
|
16027
16289
|
if (language === void 0) return true;
|
|
16028
16290
|
if (pattern.languages && pattern.languages.length > 0 && !pattern.languages.includes(language)) {
|
|
@@ -16424,6 +16686,9 @@ function isInterproceduralTaintableType(typeName, language) {
|
|
|
16424
16686
|
"IEnumerable",
|
|
16425
16687
|
"ICollection",
|
|
16426
16688
|
"IList",
|
|
16689
|
+
"List",
|
|
16690
|
+
"Hashtable",
|
|
16691
|
+
"ArrayList",
|
|
16427
16692
|
"Dictionary",
|
|
16428
16693
|
"IDictionary",
|
|
16429
16694
|
"IReadOnlyList",
|
|
@@ -16437,6 +16702,46 @@ function isInterproceduralTaintableType(typeName, language) {
|
|
|
16437
16702
|
const leaf = baseType.split(".").pop() ?? baseType;
|
|
16438
16703
|
if (csharpTaintable.includes(baseType) || csharpTaintable.includes(leaf)) return true;
|
|
16439
16704
|
}
|
|
16705
|
+
if (language === "rust") {
|
|
16706
|
+
const trimmed = typeName.trim();
|
|
16707
|
+
const unref = trimmed.replace(/^&mut\s+/, "").replace(/^&\s*/, "");
|
|
16708
|
+
const rustBase = unref.split("<")[0].trim();
|
|
16709
|
+
const rustScalars = [
|
|
16710
|
+
"String",
|
|
16711
|
+
"str",
|
|
16712
|
+
"bool",
|
|
16713
|
+
"char",
|
|
16714
|
+
"i8",
|
|
16715
|
+
"i16",
|
|
16716
|
+
"i32",
|
|
16717
|
+
"i64",
|
|
16718
|
+
"i128",
|
|
16719
|
+
"isize",
|
|
16720
|
+
"u8",
|
|
16721
|
+
"u16",
|
|
16722
|
+
"u32",
|
|
16723
|
+
"u64",
|
|
16724
|
+
"u128",
|
|
16725
|
+
"usize",
|
|
16726
|
+
"f32",
|
|
16727
|
+
"f64"
|
|
16728
|
+
];
|
|
16729
|
+
if (rustScalars.includes(rustBase)) return true;
|
|
16730
|
+
if (rustBase === "()" || rustBase === "!") return false;
|
|
16731
|
+
if (unref.startsWith("Vec<") || unref.startsWith("Option<") || unref.startsWith("Result<")) {
|
|
16732
|
+
return true;
|
|
16733
|
+
}
|
|
16734
|
+
if (/^[A-Z][A-Za-z0-9_]*$/.test(rustBase)) return true;
|
|
16735
|
+
}
|
|
16736
|
+
if (language === "javascript" || language === "typescript") {
|
|
16737
|
+
const trimmed = typeName.trim();
|
|
16738
|
+
const jsScalars = ["string", "number", "boolean", "bigint", "any", "unknown", "object"];
|
|
16739
|
+
if (jsScalars.includes(baseType)) return true;
|
|
16740
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.startsWith("(")) return true;
|
|
16741
|
+
if (/\bRecord\s*</.test(trimmed)) return true;
|
|
16742
|
+
if (["void", "undefined", "null", "never"].includes(baseType)) return false;
|
|
16743
|
+
if (/^[A-Za-z_][\w.$]*$/.test(baseType)) return true;
|
|
16744
|
+
}
|
|
16440
16745
|
if (typeName.endsWith("[]")) {
|
|
16441
16746
|
const elementType = typeName.slice(0, -2);
|
|
16442
16747
|
if (elementType === "String" || elementType === "Object" || elementType === "byte") {
|
|
@@ -16551,6 +16856,31 @@ function isSafeGoExecCommandCall(call, pattern, language) {
|
|
|
16551
16856
|
return false;
|
|
16552
16857
|
}
|
|
16553
16858
|
}
|
|
16859
|
+
if (program === "git") {
|
|
16860
|
+
const subcommandArgPos = pattern.method === "CommandContext" ? 2 : 1;
|
|
16861
|
+
const subcommandArg = call.arguments.find((a) => a.position === subcommandArgPos);
|
|
16862
|
+
let subcommand;
|
|
16863
|
+
if (subcommandArg?.literal !== null && subcommandArg?.literal !== void 0) {
|
|
16864
|
+
subcommand = String(subcommandArg.literal);
|
|
16865
|
+
} else {
|
|
16866
|
+
const expr = (subcommandArg?.expression ?? "").trim();
|
|
16867
|
+
if (expr.startsWith('"') || expr.startsWith("`") || expr.startsWith("'")) {
|
|
16868
|
+
subcommand = expr.slice(1, -1);
|
|
16869
|
+
}
|
|
16870
|
+
}
|
|
16871
|
+
if (subcommand === "ls-remote") {
|
|
16872
|
+
const hasEndOfOptions = call.arguments.some((a) => {
|
|
16873
|
+
if (a.literal !== null && a.literal !== void 0) {
|
|
16874
|
+
return String(a.literal) === "--";
|
|
16875
|
+
}
|
|
16876
|
+
const expr = (a.expression ?? "").trim();
|
|
16877
|
+
return expr === '"--"' || expr === "'--'" || expr === "`--`";
|
|
16878
|
+
});
|
|
16879
|
+
if (!hasEndOfOptions) {
|
|
16880
|
+
return false;
|
|
16881
|
+
}
|
|
16882
|
+
}
|
|
16883
|
+
}
|
|
16554
16884
|
return true;
|
|
16555
16885
|
}
|
|
16556
16886
|
function isSafeJSChildProcessCall(call, pattern, language) {
|
|
@@ -16954,6 +17284,29 @@ function isRegexReceiver(receiver, sourceLines) {
|
|
|
16954
17284
|
const decl = new RegExp(`(?:const|let|var)\\s+${escapeRe(r)}\\s*=\\s*(?:/|(?:new\\s+)?RegExp\\s*\\()`);
|
|
16955
17285
|
return sourceLines.some((l) => decl.test(l));
|
|
16956
17286
|
}
|
|
17287
|
+
function isNavigationReceiverName(receiver) {
|
|
17288
|
+
const r = (receiver ?? "").trim();
|
|
17289
|
+
if (!r) return false;
|
|
17290
|
+
const last = r.slice(r.lastIndexOf(".") + 1).replace(/\(\)$/, "");
|
|
17291
|
+
return /router|history|navigat|^nav$/i.test(last);
|
|
17292
|
+
}
|
|
17293
|
+
function isInMemoryMapGetReceiver(receiver, sourceLines) {
|
|
17294
|
+
const raw = (receiver ?? "").trim();
|
|
17295
|
+
if (!raw || !sourceLines) return false;
|
|
17296
|
+
if (/\.properties$/i.test(raw)) return true;
|
|
17297
|
+
const lastDot = raw.lastIndexOf(".");
|
|
17298
|
+
const r = (lastDot >= 0 ? raw.slice(lastDot + 1) : raw).trim();
|
|
17299
|
+
if (/^(properties|memoryCache)$/i.test(r)) return true;
|
|
17300
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(r)) return false;
|
|
17301
|
+
const n = escapeRe(r);
|
|
17302
|
+
const mapCtor = new RegExp(
|
|
17303
|
+
`\\b${n}\\s*(?::[^=;]*)?=\\s*new\\s+(?:Weak)?Map\\s*(?:<.*>)?\\s*\\(`
|
|
17304
|
+
);
|
|
17305
|
+
const mapType = new RegExp(
|
|
17306
|
+
`\\b${n}\\s*\\??\\s*:\\s*(?:Readonly)?(?:Weak)?Map\\s*<`
|
|
17307
|
+
);
|
|
17308
|
+
return sourceLines.some((l) => mapCtor.test(l) || mapType.test(l));
|
|
17309
|
+
}
|
|
16957
17310
|
function isDeclaredFunctionReference(arg, sourceLines) {
|
|
16958
17311
|
if (arg.literal !== null && arg.literal !== void 0) return false;
|
|
16959
17312
|
if (!sourceLines) return false;
|
|
@@ -17158,6 +17511,12 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
17158
17511
|
if (isParameterizedQueryCall(call, pattern)) {
|
|
17159
17512
|
continue;
|
|
17160
17513
|
}
|
|
17514
|
+
if ((language === "javascript" || language === "typescript") && (pattern.type === "sql_injection" || pattern.type === "nosql_injection") && call.arguments.length === 0 && pattern.arg_positions.every((p) => p >= 0)) {
|
|
17515
|
+
continue;
|
|
17516
|
+
}
|
|
17517
|
+
if (isBoundParameterCall(call, pattern, sourceLines)) {
|
|
17518
|
+
continue;
|
|
17519
|
+
}
|
|
17161
17520
|
if (isSafePythonSubprocessCall(call, pattern, language)) {
|
|
17162
17521
|
continue;
|
|
17163
17522
|
}
|
|
@@ -17215,9 +17574,15 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
17215
17574
|
if (isArgumentlessJsResponseWriter(call, pattern, language)) {
|
|
17216
17575
|
continue;
|
|
17217
17576
|
}
|
|
17577
|
+
if (pattern.type === "open_redirect" && (language === "javascript" || language === "typescript") && (call.method_name === "push" || call.method_name === "replace") && (pattern.class ?? "").toLowerCase() === "router" && !call.receiver_type && !call.receiver_type_fqn && !isNavigationReceiverName(call.receiver)) {
|
|
17578
|
+
continue;
|
|
17579
|
+
}
|
|
17218
17580
|
if ((pattern.type === "command_injection" || pattern.type === "sql_injection") && call.method_name === "exec" && (language === "javascript" || language === "typescript") && isRegexReceiver(call.receiver, sourceLines)) {
|
|
17219
17581
|
continue;
|
|
17220
17582
|
}
|
|
17583
|
+
if (pattern.type === "sql_injection" && call.method_name === "get" && pattern.class === "Connection" && (language === "javascript" || language === "typescript") && isInMemoryMapGetReceiver(call.receiver, sourceLines)) {
|
|
17584
|
+
continue;
|
|
17585
|
+
}
|
|
17221
17586
|
if (pattern.type === "code_injection" && language === "python" && call.method_name === "compile" && call.receiver && call.receiver !== "builtins") {
|
|
17222
17587
|
continue;
|
|
17223
17588
|
}
|
|
@@ -17234,7 +17599,7 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
17234
17599
|
continue;
|
|
17235
17600
|
}
|
|
17236
17601
|
const location = formatCallLocation(call);
|
|
17237
|
-
const key = `${location}:${call.location.line}:${pattern.cwe}`;
|
|
17602
|
+
const key = `${location}:${call.location.line}:${call.location.column}:${pattern.cwe}`;
|
|
17238
17603
|
const confidence = calculateSinkConfidence(call, pattern);
|
|
17239
17604
|
const existing = sinkMap.get(key);
|
|
17240
17605
|
if (!existing || confidence > existing.confidence) {
|
|
@@ -17245,6 +17610,7 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
17245
17610
|
cwe: pattern.cwe,
|
|
17246
17611
|
location,
|
|
17247
17612
|
line: call.location.line,
|
|
17613
|
+
callColumn: call.location.column,
|
|
17248
17614
|
confidence,
|
|
17249
17615
|
method: call.method_name,
|
|
17250
17616
|
argPositions: pattern.arg_positions,
|
|
@@ -17282,6 +17648,15 @@ function matchesSourcePattern(call, pattern, language, types, imports) {
|
|
|
17282
17648
|
return false;
|
|
17283
17649
|
}
|
|
17284
17650
|
}
|
|
17651
|
+
if (!pattern.class && pattern.return_tainted && pattern.method && (language === "javascript" || language === "typescript")) {
|
|
17652
|
+
const expressRequestAccessors = /* @__PURE__ */ new Set(["query", "body", "params", "headers", "cookies"]);
|
|
17653
|
+
if (expressRequestAccessors.has(pattern.method)) {
|
|
17654
|
+
const recv = call.receiver?.toLowerCase() ?? "";
|
|
17655
|
+
if (recv !== "req" && recv !== "request" && recv !== "ctx") {
|
|
17656
|
+
return false;
|
|
17657
|
+
}
|
|
17658
|
+
}
|
|
17659
|
+
}
|
|
17285
17660
|
return pattern.return_tainted === true;
|
|
17286
17661
|
}
|
|
17287
17662
|
return false;
|
|
@@ -17375,6 +17750,7 @@ var SAFE_RECEIVERS_BY_METHOD = {
|
|
|
17375
17750
|
"database",
|
|
17376
17751
|
"sqlite",
|
|
17377
17752
|
"conn",
|
|
17753
|
+
"con",
|
|
17378
17754
|
"connection",
|
|
17379
17755
|
"client",
|
|
17380
17756
|
"pool",
|
|
@@ -17651,7 +18027,7 @@ function matchesSinkPattern(call, pattern, typeHierarchy, language) {
|
|
|
17651
18027
|
}
|
|
17652
18028
|
}
|
|
17653
18029
|
}
|
|
17654
|
-
if (!pattern.class && call.receiver) {
|
|
18030
|
+
if (!pattern.class && call.receiver && !pattern.requires_import) {
|
|
17655
18031
|
if (isKnownSafeReceiverForMethod(call.receiver, call.method_name, pattern.type)) {
|
|
17656
18032
|
return false;
|
|
17657
18033
|
}
|
|
@@ -18384,6 +18760,8 @@ function javaReplaceChainCredit(chainText) {
|
|
|
18384
18760
|
let lf = false;
|
|
18385
18761
|
let lt = false;
|
|
18386
18762
|
let gt = false;
|
|
18763
|
+
let openBrace = false;
|
|
18764
|
+
let closeBrace = false;
|
|
18387
18765
|
for (const m of chainText.matchAll(JAVA_REPLACE_ARGS_RE)) {
|
|
18388
18766
|
const kind = m[1];
|
|
18389
18767
|
const rawFrom = m[2] ?? m[3] ?? "";
|
|
@@ -18394,10 +18772,13 @@ function javaReplaceChainCredit(chainText) {
|
|
|
18394
18772
|
if (cov.lf) lf = true;
|
|
18395
18773
|
if (from === "<" && to.startsWith("&")) lt = true;
|
|
18396
18774
|
if (from === ">" && to.startsWith("&")) gt = true;
|
|
18775
|
+
if (from === "{") openBrace = true;
|
|
18776
|
+
if (from === "}") closeBrace = true;
|
|
18397
18777
|
}
|
|
18398
18778
|
const out2 = [];
|
|
18399
18779
|
if (cr && lf) out2.push("log_injection");
|
|
18400
18780
|
if (lt && gt) out2.push("xss");
|
|
18781
|
+
if (openBrace && closeBrace) out2.push("code_injection");
|
|
18401
18782
|
return out2;
|
|
18402
18783
|
}
|
|
18403
18784
|
function matchesSanitizerPattern(call, pattern) {
|
|
@@ -19727,6 +20108,11 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19727
20108
|
taintedArrayElements = /* @__PURE__ */ new Map();
|
|
19728
20109
|
// Track current method name for scoping local variables
|
|
19729
20110
|
currentMethod = null;
|
|
20111
|
+
/** Start lines of nested `{ ... }` blocks — disambiguate same-named locals (#476 Collections10). */
|
|
20112
|
+
blockScopeStack = [];
|
|
20113
|
+
lineBlockScopes = /* @__PURE__ */ new Map();
|
|
20114
|
+
/** Skip block-scope push on the method/constructor body `{ ... }` (#476). */
|
|
20115
|
+
skipMethodBodyBlockScope = false;
|
|
19730
20116
|
// Track conditional taints: which variables were tainted under which conditions
|
|
19731
20117
|
// Maps condition expression string → set of variables tainted under that condition
|
|
19732
20118
|
conditionalTaints = /* @__PURE__ */ new Map();
|
|
@@ -19740,6 +20126,8 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19740
20126
|
inSynchronizedBlock = false;
|
|
19741
20127
|
// Track iterator sources: iterator variable name → collection name it was created from
|
|
19742
20128
|
iteratorSources = /* @__PURE__ */ new Map();
|
|
20129
|
+
/** Vars assigned from `iter.next()` — do not retro-clear on later iterator rebind (#476). */
|
|
20130
|
+
taintedFromIteratorNext = /* @__PURE__ */ new Set();
|
|
19743
20131
|
// Track class field names (declared at class level, not local variables)
|
|
19744
20132
|
classFields = /* @__PURE__ */ new Set();
|
|
19745
20133
|
// Track tainted method parameters for inter-procedural analysis
|
|
@@ -19797,12 +20185,16 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19797
20185
|
this.sanitizedVars.clear();
|
|
19798
20186
|
this.sanitizedVarUncovered.clear();
|
|
19799
20187
|
this.currentMethod = null;
|
|
20188
|
+
this.blockScopeStack = [];
|
|
20189
|
+
this.lineBlockScopes.clear();
|
|
20190
|
+
this.skipMethodBodyBlockScope = false;
|
|
19800
20191
|
this.conditionalTaints.clear();
|
|
19801
20192
|
this.conditionStack = [];
|
|
19802
20193
|
this.lineConditions.clear();
|
|
19803
20194
|
this.synchronizedLines.clear();
|
|
19804
20195
|
this.inSynchronizedBlock = false;
|
|
19805
20196
|
this.iteratorSources.clear();
|
|
20197
|
+
this.taintedFromIteratorNext.clear();
|
|
19806
20198
|
this.classFields.clear();
|
|
19807
20199
|
this.instanceFieldTaint.clear();
|
|
19808
20200
|
this.currentClassName = null;
|
|
@@ -19827,6 +20219,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19827
20219
|
const resultSymbols = new Map(this.symbols);
|
|
19828
20220
|
for (const name2 of this.tainted) {
|
|
19829
20221
|
if (name2.includes(":")) {
|
|
20222
|
+
if (/^[^:]+:b\d+:/.test(name2)) {
|
|
20223
|
+
continue;
|
|
20224
|
+
}
|
|
19830
20225
|
const unscoped = name2.substring(name2.indexOf(":") + 1);
|
|
19831
20226
|
resultTainted.add(unscoped);
|
|
19832
20227
|
}
|
|
@@ -19841,6 +20236,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19841
20236
|
for (const [name2, value] of this.symbols) {
|
|
19842
20237
|
if (this.goScopedNames.has(name2)) continue;
|
|
19843
20238
|
if (name2.includes(":")) {
|
|
20239
|
+
if (/^[^:]+:b\d+:/.test(name2)) {
|
|
20240
|
+
continue;
|
|
20241
|
+
}
|
|
19844
20242
|
const unscoped = name2.substring(name2.indexOf(":") + 1);
|
|
19845
20243
|
if (!resultSymbols.has(unscoped)) {
|
|
19846
20244
|
resultSymbols.set(unscoped, value);
|
|
@@ -19850,6 +20248,7 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19850
20248
|
return {
|
|
19851
20249
|
symbols: resultSymbols,
|
|
19852
20250
|
tainted: resultTainted,
|
|
20251
|
+
lineBlockScopes: new Map(this.lineBlockScopes),
|
|
19853
20252
|
unreachableLines: new Set(this.unreachableLines),
|
|
19854
20253
|
taintedCollections: new Map(this.taintedCollections),
|
|
19855
20254
|
taintedArrayElements: new Map(this.taintedArrayElements),
|
|
@@ -20393,6 +20792,18 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20393
20792
|
// ===========================================================================
|
|
20394
20793
|
// Taint Refinement
|
|
20395
20794
|
// ===========================================================================
|
|
20795
|
+
/** True when `node` is `iter.next()` (possibly under a cast). */
|
|
20796
|
+
isIteratorNextExpression(node) {
|
|
20797
|
+
const text = getNodeText2(node, this.source);
|
|
20798
|
+
if (/\.\s*next\s*\(/.test(text)) return true;
|
|
20799
|
+
let current = node;
|
|
20800
|
+
if (current.type === "cast_expression" || current.type === "parenthesized_expression") {
|
|
20801
|
+
current = current.childForFieldName("operand") ?? current.childForFieldName("expression");
|
|
20802
|
+
}
|
|
20803
|
+
if (!current || current.type !== "method_invocation") return false;
|
|
20804
|
+
const nameNode = current.childForFieldName("name");
|
|
20805
|
+
return nameNode ? getNodeText2(nameNode, this.source) === "next" : false;
|
|
20806
|
+
}
|
|
20396
20807
|
refineTaintFromConstants() {
|
|
20397
20808
|
let changed = true;
|
|
20398
20809
|
let iterations = 0;
|
|
@@ -20402,6 +20813,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20402
20813
|
iterations++;
|
|
20403
20814
|
const toRemove = [];
|
|
20404
20815
|
for (const varName of this.tainted) {
|
|
20816
|
+
if (this.taintedFromIteratorNext.has(varName)) {
|
|
20817
|
+
continue;
|
|
20818
|
+
}
|
|
20405
20819
|
const symbol = this.symbols.get(varName);
|
|
20406
20820
|
if (symbol && symbol.type !== "unknown") {
|
|
20407
20821
|
toRemove.push(varName);
|
|
@@ -20450,6 +20864,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20450
20864
|
if (this.unreachableLines.has(line)) {
|
|
20451
20865
|
return true;
|
|
20452
20866
|
}
|
|
20867
|
+
if (this.blockScopeStack.length > 0) {
|
|
20868
|
+
this.lineBlockScopes.set(line, this.blockScopeStack[this.blockScopeStack.length - 1]);
|
|
20869
|
+
}
|
|
20453
20870
|
if (this.conditionStack.length > 0 && !this.lineConditions.has(line)) {
|
|
20454
20871
|
this.lineConditions.set(line, this.conditionStack[this.conditionStack.length - 1]);
|
|
20455
20872
|
}
|
|
@@ -20475,12 +20892,24 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20475
20892
|
case "assignment_statement":
|
|
20476
20893
|
this.handleGoAssignment(node);
|
|
20477
20894
|
return false;
|
|
20895
|
+
case "var_declaration":
|
|
20896
|
+
this.handleGoVarDeclaration(node);
|
|
20897
|
+
return false;
|
|
20478
20898
|
case "update_expression":
|
|
20479
20899
|
this.handleUpdateExpression(node);
|
|
20480
20900
|
return false;
|
|
20481
20901
|
case "if_statement":
|
|
20482
20902
|
this.handleIfStatement(node);
|
|
20483
20903
|
return true;
|
|
20904
|
+
case "block": {
|
|
20905
|
+
if (this.skipMethodBodyBlockScope) {
|
|
20906
|
+
this.skipMethodBodyBlockScope = false;
|
|
20907
|
+
}
|
|
20908
|
+
for (const child of node.children) {
|
|
20909
|
+
this.visit(child);
|
|
20910
|
+
}
|
|
20911
|
+
return true;
|
|
20912
|
+
}
|
|
20484
20913
|
case "switch_expression":
|
|
20485
20914
|
case "switch_statement":
|
|
20486
20915
|
this.handleSwitch(node);
|
|
@@ -20566,7 +20995,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20566
20995
|
}
|
|
20567
20996
|
const body2 = node.childForFieldName("body");
|
|
20568
20997
|
if (body2) {
|
|
20998
|
+
this.skipMethodBodyBlockScope = true;
|
|
20569
20999
|
this.visit(body2);
|
|
21000
|
+
this.skipMethodBodyBlockScope = false;
|
|
20570
21001
|
}
|
|
20571
21002
|
this.currentMethod = prevMethod;
|
|
20572
21003
|
this.inConstructor = prevInConstructor;
|
|
@@ -20580,10 +21011,31 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20580
21011
|
if (varName.includes(".") || varName.includes(":")) {
|
|
20581
21012
|
return varName;
|
|
20582
21013
|
}
|
|
21014
|
+
const blockId = this.blockScopeStack.length > 0 ? this.blockScopeStack[this.blockScopeStack.length - 1] : void 0;
|
|
20583
21015
|
if (this.currentMethod) {
|
|
20584
|
-
return `${this.currentMethod}:${varName}`;
|
|
21016
|
+
return blockId !== void 0 ? `${this.currentMethod}:b${blockId}:${varName}` : `${this.currentMethod}:${varName}`;
|
|
20585
21017
|
}
|
|
20586
|
-
return varName;
|
|
21018
|
+
return blockId !== void 0 ? `b${blockId}:${varName}` : varName;
|
|
21019
|
+
}
|
|
21020
|
+
/** Innermost block → method → unscoped, for reads that may bind to an outer declaration. */
|
|
21021
|
+
getScopedNameCandidates(varName) {
|
|
21022
|
+
if (varName.includes(".") || varName.includes(":")) {
|
|
21023
|
+
return [varName];
|
|
21024
|
+
}
|
|
21025
|
+
const out2 = [];
|
|
21026
|
+
for (let i2 = this.blockScopeStack.length - 1; i2 >= 0; i2--) {
|
|
21027
|
+
const blockId = this.blockScopeStack[i2];
|
|
21028
|
+
if (this.currentMethod) {
|
|
21029
|
+
out2.push(`${this.currentMethod}:b${blockId}:${varName}`);
|
|
21030
|
+
} else {
|
|
21031
|
+
out2.push(`b${blockId}:${varName}`);
|
|
21032
|
+
}
|
|
21033
|
+
}
|
|
21034
|
+
if (this.currentMethod) {
|
|
21035
|
+
out2.push(`${this.currentMethod}:${varName}`);
|
|
21036
|
+
}
|
|
21037
|
+
out2.push(varName);
|
|
21038
|
+
return out2;
|
|
20587
21039
|
}
|
|
20588
21040
|
/**
|
|
20589
21041
|
* Look up a variable value, checking both scoped and unscoped names.
|
|
@@ -20591,11 +21043,12 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20591
21043
|
* either local (scoped) or global (unscoped, like class fields).
|
|
20592
21044
|
*/
|
|
20593
21045
|
lookupSymbol(varName) {
|
|
20594
|
-
if (
|
|
20595
|
-
const scopedName
|
|
20596
|
-
|
|
20597
|
-
|
|
20598
|
-
|
|
21046
|
+
if (!varName.includes(".") && !varName.includes(":")) {
|
|
21047
|
+
for (const scopedName of this.getScopedNameCandidates(varName)) {
|
|
21048
|
+
const scopedValue = this.symbols.get(scopedName);
|
|
21049
|
+
if (scopedValue) {
|
|
21050
|
+
return scopedValue;
|
|
21051
|
+
}
|
|
20599
21052
|
}
|
|
20600
21053
|
}
|
|
20601
21054
|
return this.symbols.get(varName);
|
|
@@ -20647,8 +21100,22 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20647
21100
|
this.trackIteratorsInNode(initNode);
|
|
20648
21101
|
}
|
|
20649
21102
|
}
|
|
20650
|
-
|
|
20651
|
-
|
|
21103
|
+
if (node.type === "while_statement" || node.type === "do_statement") {
|
|
21104
|
+
const body2 = node.childForFieldName("body");
|
|
21105
|
+
for (const child of node.children) {
|
|
21106
|
+
if (child !== body2) this.visit(child);
|
|
21107
|
+
}
|
|
21108
|
+
if (body2?.type === "block") {
|
|
21109
|
+
this.blockScopeStack.push(getNodeLine(body2));
|
|
21110
|
+
this.visit(body2);
|
|
21111
|
+
this.blockScopeStack.pop();
|
|
21112
|
+
} else if (body2) {
|
|
21113
|
+
this.visit(body2);
|
|
21114
|
+
}
|
|
21115
|
+
} else {
|
|
21116
|
+
for (const child of node.children) {
|
|
21117
|
+
this.visit(child);
|
|
21118
|
+
}
|
|
20652
21119
|
}
|
|
20653
21120
|
for (const varName of loopVarNames) {
|
|
20654
21121
|
this.symbols.set(varName, createUnknown(getNodeLine(node)));
|
|
@@ -20790,6 +21257,10 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20790
21257
|
this.sanitizedVars.delete(scopedName);
|
|
20791
21258
|
this.sanitizedVarUncovered.delete(scopedName);
|
|
20792
21259
|
this.definitionNodes.set(scopedName, valueNode);
|
|
21260
|
+
if (this.isIteratorNextExpression(valueNode)) {
|
|
21261
|
+
this.taintedFromIteratorNext.add(scopedName);
|
|
21262
|
+
this.taintedFromIteratorNext.add(varName);
|
|
21263
|
+
}
|
|
20793
21264
|
this.symbols.set(scopedName, createUnknown(line));
|
|
20794
21265
|
} else {
|
|
20795
21266
|
if (this.inConditionalBranch) {
|
|
@@ -20831,6 +21302,64 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20831
21302
|
* helper that always returns a constant — clears the variable. Anything else stops being tracked here, which is what Go
|
|
20832
21303
|
* assignments got before this handler existed: the DFG decides.
|
|
20833
21304
|
*/
|
|
21305
|
+
/** `var x = expr` / `var x T = expr` — same clean/taint rules as `:=` (#783). */
|
|
21306
|
+
handleGoVarDeclaration(node) {
|
|
21307
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
21308
|
+
const spec = node.child(i2);
|
|
21309
|
+
if (!spec || spec.type !== "var_spec") continue;
|
|
21310
|
+
const nameNode = spec.childForFieldName("name");
|
|
21311
|
+
let valueNode = spec.childForFieldName("value");
|
|
21312
|
+
if (!nameNode || !valueNode) continue;
|
|
21313
|
+
const names = [];
|
|
21314
|
+
if (nameNode.type === "identifier") {
|
|
21315
|
+
names.push(getNodeText2(nameNode, this.source));
|
|
21316
|
+
} else if (nameNode.type === "identifier_list") {
|
|
21317
|
+
for (const c of nameNode.children) {
|
|
21318
|
+
if (c.type === "identifier") names.push(getNodeText2(c, this.source));
|
|
21319
|
+
}
|
|
21320
|
+
}
|
|
21321
|
+
if (valueNode.type === "expression_list") {
|
|
21322
|
+
valueNode = valueNode.children.find((c) => c.type !== "," && c.type !== "comment") ?? valueNode;
|
|
21323
|
+
}
|
|
21324
|
+
if (names.length === 0) continue;
|
|
21325
|
+
const line = getNodeLine(node);
|
|
21326
|
+
for (const varName of names) {
|
|
21327
|
+
if (varName === "_") continue;
|
|
21328
|
+
const scopedName = this.getScopedName(varName);
|
|
21329
|
+
this.goScopedNames.add(scopedName);
|
|
21330
|
+
if (this.isTaintedExpression(valueNode)) {
|
|
21331
|
+
this.goUnknownVars.add(scopedName);
|
|
21332
|
+
this.tainted.add(scopedName);
|
|
21333
|
+
this.sanitizedVars.delete(scopedName);
|
|
21334
|
+
this.sanitizedVarUncovered.delete(scopedName);
|
|
21335
|
+
this.definitionNodes.set(scopedName, valueNode);
|
|
21336
|
+
this.symbols.set(scopedName, createUnknown(line));
|
|
21337
|
+
continue;
|
|
21338
|
+
}
|
|
21339
|
+
if (!this.isGoKnownCleanRhs(valueNode) || this.goUnknownVars.has(scopedName)) {
|
|
21340
|
+
this.goUnknownVars.add(scopedName);
|
|
21341
|
+
this.symbols.delete(scopedName);
|
|
21342
|
+
this.sanitizedVars.delete(scopedName);
|
|
21343
|
+
this.sanitizedVarUncovered.delete(scopedName);
|
|
21344
|
+
if (!this.inConditionalBranch) {
|
|
21345
|
+
this.tainted.delete(scopedName);
|
|
21346
|
+
this.definitionNodes.delete(scopedName);
|
|
21347
|
+
}
|
|
21348
|
+
continue;
|
|
21349
|
+
}
|
|
21350
|
+
if (this.inConditionalBranch) {
|
|
21351
|
+
this.goUnknownVars.add(scopedName);
|
|
21352
|
+
this.symbols.delete(scopedName);
|
|
21353
|
+
continue;
|
|
21354
|
+
}
|
|
21355
|
+
this.tainted.delete(scopedName);
|
|
21356
|
+
this.definitionNodes.delete(scopedName);
|
|
21357
|
+
this.symbols.set(scopedName, this.evaluateExpression(valueNode));
|
|
21358
|
+
this.sanitizedVars.delete(scopedName);
|
|
21359
|
+
this.sanitizedVarUncovered.delete(scopedName);
|
|
21360
|
+
}
|
|
21361
|
+
}
|
|
21362
|
+
}
|
|
20834
21363
|
handleGoAssignment(node) {
|
|
20835
21364
|
const left = node.childForFieldName("left");
|
|
20836
21365
|
const right = node.childForFieldName("right");
|
|
@@ -20917,6 +21446,12 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20917
21446
|
this.sanitizedVars.delete(scopedName);
|
|
20918
21447
|
this.sanitizedVarUncovered.delete(scopedName);
|
|
20919
21448
|
this.definitionNodes.set(scopedName, right);
|
|
21449
|
+
if (this.isIteratorNextExpression(right)) {
|
|
21450
|
+
this.taintedFromIteratorNext.add(scopedName);
|
|
21451
|
+
if (!varName.includes(".")) {
|
|
21452
|
+
this.taintedFromIteratorNext.add(varName);
|
|
21453
|
+
}
|
|
21454
|
+
}
|
|
20920
21455
|
this.symbols.set(scopedName, createUnknown(line));
|
|
20921
21456
|
if (this.inConstructor && varName.startsWith("this.")) {
|
|
20922
21457
|
const fieldName = varName.substring(5);
|
|
@@ -21641,6 +22176,24 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
21641
22176
|
cache.set(rootId, false);
|
|
21642
22177
|
return false;
|
|
21643
22178
|
}
|
|
22179
|
+
/** Is an argument of `call` an inline sanitizer call over tainted input (itself possibly nested)? */
|
|
22180
|
+
argIsSanitizedTaint(call) {
|
|
22181
|
+
const argsNode = call.childForFieldName("arguments");
|
|
22182
|
+
if (!argsNode) return false;
|
|
22183
|
+
for (const arg of argsNode.children) {
|
|
22184
|
+
if (arg.type !== "method_invocation") continue;
|
|
22185
|
+
const name2 = arg.childForFieldName("name");
|
|
22186
|
+
if (!name2 || !SANITIZER_METHODS.has(getNodeText2(name2, this.source))) continue;
|
|
22187
|
+
const inner = arg.childForFieldName("arguments");
|
|
22188
|
+
if (!inner) continue;
|
|
22189
|
+
for (const a of inner.children) {
|
|
22190
|
+
if (a.type === "(" || a.type === ")" || a.type === ",") continue;
|
|
22191
|
+
if (this.isTaintedExpression(a)) return true;
|
|
22192
|
+
}
|
|
22193
|
+
if (this.argIsSanitizedTaint(arg)) return true;
|
|
22194
|
+
}
|
|
22195
|
+
return false;
|
|
22196
|
+
}
|
|
21644
22197
|
isTaintedExpressionStep(node) {
|
|
21645
22198
|
const text = getNodeText2(node, this.source);
|
|
21646
22199
|
if (node.type === "method_invocation") {
|
|
@@ -21648,6 +22201,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
21648
22201
|
const objectNode = node.childForFieldName("object");
|
|
21649
22202
|
if (nameNode) {
|
|
21650
22203
|
const methodName = getNodeText2(nameNode, this.source);
|
|
22204
|
+
if (ANTI_SANITIZER_METHODS.has(methodName) && this.argIsSanitizedTaint(node)) {
|
|
22205
|
+
return true;
|
|
22206
|
+
}
|
|
21651
22207
|
if (SANITIZER_METHODS.has(methodName) && !this.localNonSanitizing.has(methodName)) {
|
|
21652
22208
|
return false;
|
|
21653
22209
|
}
|
|
@@ -21907,10 +22463,17 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
21907
22463
|
}
|
|
21908
22464
|
}
|
|
21909
22465
|
if (node.type === "identifier") {
|
|
21910
|
-
|
|
21911
|
-
|
|
21912
|
-
|
|
21913
|
-
if (this.
|
|
22466
|
+
let isTainted = false;
|
|
22467
|
+
let boundName = null;
|
|
22468
|
+
for (const candidate of this.getScopedNameCandidates(text)) {
|
|
22469
|
+
if (this.tainted.has(candidate)) {
|
|
22470
|
+
isTainted = true;
|
|
22471
|
+
boundName = candidate;
|
|
22472
|
+
break;
|
|
22473
|
+
}
|
|
22474
|
+
}
|
|
22475
|
+
if (isTainted && boundName) {
|
|
22476
|
+
if (this.isExcludedByCondition(boundName) || this.isExcludedByCondition(text)) {
|
|
21914
22477
|
return false;
|
|
21915
22478
|
}
|
|
21916
22479
|
}
|