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
|
@@ -4709,14 +4709,43 @@ function extractJavaTypes(tree, cache, options) {
|
|
|
4709
4709
|
);
|
|
4710
4710
|
const path = node ? enclosingTypePath(node) : [];
|
|
4711
4711
|
if (path.length > 0) t.enclosing_type = path.join(".");
|
|
4712
|
-
const
|
|
4713
|
-
if (
|
|
4714
|
-
|
|
4715
|
-
|
|
4712
|
+
const tp = typeParameters(node);
|
|
4713
|
+
if (tp.names.length > 0) t.type_parameters = tp.names;
|
|
4714
|
+
if (Object.keys(tp.bounds).length > 0) t.type_parameter_bounds = tp.bounds;
|
|
4715
|
+
}
|
|
4716
|
+
const allMethods = /* @__PURE__ */ new Map();
|
|
4717
|
+
for (const t of types) {
|
|
4718
|
+
for (const m of t.methods) allMethods.set(`${m.start_line}:${m.name}`, m);
|
|
4719
|
+
}
|
|
4720
|
+
for (const kind of ["method_declaration", "constructor_declaration"]) {
|
|
4721
|
+
for (const node of getNodesFromCache(tree.rootNode, kind, cache)) {
|
|
4722
|
+
const name2 = getIdentifier(node, "name");
|
|
4723
|
+
const info2 = name2 ? allMethods.get(`${node.startPosition.row + 1}:${name2}`) : void 0;
|
|
4724
|
+
if (!info2) continue;
|
|
4725
|
+
const tp = typeParameters(node);
|
|
4726
|
+
if (tp.names.length > 0) info2.type_parameters = tp.names;
|
|
4727
|
+
if (Object.keys(tp.bounds).length > 0) info2.type_parameter_bounds = tp.bounds;
|
|
4716
4728
|
}
|
|
4717
4729
|
}
|
|
4718
4730
|
return types;
|
|
4719
4731
|
}
|
|
4732
|
+
function typeParameters(node) {
|
|
4733
|
+
const names = [];
|
|
4734
|
+
const bounds = {};
|
|
4735
|
+
const list = node ? node.childForFieldName("type_parameters") ?? node.namedChildren.find((c) => c?.type === "type_parameters") ?? null : null;
|
|
4736
|
+
if (!list) return { names, bounds };
|
|
4737
|
+
for (const c of list.namedChildren) {
|
|
4738
|
+
if (c?.type !== "type_parameter") continue;
|
|
4739
|
+
const id = c.namedChildren.find((x) => x?.type === "type_identifier" || x?.type === "identifier");
|
|
4740
|
+
const name2 = id ? getNodeText(id) : "";
|
|
4741
|
+
if (!name2) continue;
|
|
4742
|
+
names.push(name2);
|
|
4743
|
+
const bound = c.namedChildren.find((x) => x?.type === "type_bound");
|
|
4744
|
+
const first = bound?.namedChildren.find((x) => x !== null && x.type !== "annotation" && x.type !== "marker_annotation");
|
|
4745
|
+
if (first) bounds[name2] = getNodeText(first);
|
|
4746
|
+
}
|
|
4747
|
+
return { names, bounds };
|
|
4748
|
+
}
|
|
4720
4749
|
function recordComponents(node) {
|
|
4721
4750
|
const out2 = [];
|
|
4722
4751
|
const params = node.childForFieldName("parameters");
|
|
@@ -10427,6 +10456,79 @@ function processStatement2(stmt, startDefId, startUseId, scopeStack, isJavaScrip
|
|
|
10427
10456
|
carryBranchDefsOut(popped, scopeStack, alwaysRuns);
|
|
10428
10457
|
break;
|
|
10429
10458
|
}
|
|
10459
|
+
case "try_statement":
|
|
10460
|
+
case "try_with_resources_statement": {
|
|
10461
|
+
if (isJavaScript) {
|
|
10462
|
+
const stmtUses = extractUses(stmt, useId, scopeStack, isJavaScript);
|
|
10463
|
+
uses.push(...stmtUses.uses);
|
|
10464
|
+
useId = stmtUses.nextId;
|
|
10465
|
+
break;
|
|
10466
|
+
}
|
|
10467
|
+
scopeStack.push(/* @__PURE__ */ new Map());
|
|
10468
|
+
const resources = stmt.childForFieldName("resources");
|
|
10469
|
+
for (const res of resources?.namedChildren ?? []) {
|
|
10470
|
+
if (!res || res.type !== "resource") continue;
|
|
10471
|
+
const nameNode = res.childForFieldName("name");
|
|
10472
|
+
const value = res.childForFieldName("value");
|
|
10473
|
+
if (value) {
|
|
10474
|
+
const valueUses = extractUses(value, useId, scopeStack, isJavaScript);
|
|
10475
|
+
uses.push(...valueUses.uses);
|
|
10476
|
+
useId = valueUses.nextId;
|
|
10477
|
+
}
|
|
10478
|
+
if (nameNode && nameNode.type === "identifier") {
|
|
10479
|
+
const def = { id: defId++, variable: getNodeText(nameNode), line: nameNode.startPosition.row + 1, column: nameNode.startPosition.column, kind: "local" };
|
|
10480
|
+
defs.push(def);
|
|
10481
|
+
currentScope(scopeStack).set(def.variable, def.id);
|
|
10482
|
+
markDeclared(currentScope(scopeStack), def.variable);
|
|
10483
|
+
} else if (!value) {
|
|
10484
|
+
const resUses = extractUses(res, useId, scopeStack, isJavaScript);
|
|
10485
|
+
uses.push(...resUses.uses);
|
|
10486
|
+
useId = resUses.nextId;
|
|
10487
|
+
}
|
|
10488
|
+
}
|
|
10489
|
+
for (const part of stmt.namedChildren) {
|
|
10490
|
+
if (!part) continue;
|
|
10491
|
+
if (part.type === "block") {
|
|
10492
|
+
const r = processStatement2(part, defId, useId, scopeStack, isJavaScript);
|
|
10493
|
+
defs.push(...r.defs);
|
|
10494
|
+
uses.push(...r.uses);
|
|
10495
|
+
defId = r.nextDefId;
|
|
10496
|
+
useId = r.nextUseId;
|
|
10497
|
+
} else if (part.type === "catch_clause") {
|
|
10498
|
+
scopeStack.push(/* @__PURE__ */ new Map());
|
|
10499
|
+
for (const param of part.namedChildren) {
|
|
10500
|
+
if (!param || param.type !== "catch_formal_parameter") continue;
|
|
10501
|
+
const pname = param.childForFieldName("name");
|
|
10502
|
+
if (pname) {
|
|
10503
|
+
const def = { id: defId++, variable: getNodeText(pname), line: pname.startPosition.row + 1, column: pname.startPosition.column, kind: "local" };
|
|
10504
|
+
defs.push(def);
|
|
10505
|
+
currentScope(scopeStack).set(def.variable, def.id);
|
|
10506
|
+
markDeclared(currentScope(scopeStack), def.variable);
|
|
10507
|
+
}
|
|
10508
|
+
}
|
|
10509
|
+
const body2 = part.childForFieldName("body");
|
|
10510
|
+
if (body2) {
|
|
10511
|
+
const r = processStatement2(body2, defId, useId, scopeStack, isJavaScript);
|
|
10512
|
+
defs.push(...r.defs);
|
|
10513
|
+
uses.push(...r.uses);
|
|
10514
|
+
defId = r.nextDefId;
|
|
10515
|
+
useId = r.nextUseId;
|
|
10516
|
+
}
|
|
10517
|
+
carryBranchDefsOut(scopeStack.pop(), scopeStack, false);
|
|
10518
|
+
} else if (part.type === "finally_clause") {
|
|
10519
|
+
for (const b of part.namedChildren) {
|
|
10520
|
+
if (!b || b.type !== "block") continue;
|
|
10521
|
+
const r = processStatement2(b, defId, useId, scopeStack, isJavaScript);
|
|
10522
|
+
defs.push(...r.defs);
|
|
10523
|
+
uses.push(...r.uses);
|
|
10524
|
+
defId = r.nextDefId;
|
|
10525
|
+
useId = r.nextUseId;
|
|
10526
|
+
}
|
|
10527
|
+
}
|
|
10528
|
+
}
|
|
10529
|
+
carryBranchDefsOut(scopeStack.pop(), scopeStack, false);
|
|
10530
|
+
break;
|
|
10531
|
+
}
|
|
10430
10532
|
case "else_clause": {
|
|
10431
10533
|
for (let i2 = 0; i2 < stmt.childCount; i2++) {
|
|
10432
10534
|
const arm = stmt.child(i2);
|
|
@@ -11042,6 +11144,42 @@ function buildBashDFG(tree) {
|
|
|
11042
11144
|
defs.push(def);
|
|
11043
11145
|
currentScope(scopeStack).set(p, def.id);
|
|
11044
11146
|
}
|
|
11147
|
+
const bashFnDefs = /* @__PURE__ */ new Map();
|
|
11148
|
+
const bashFnByNodeId = /* @__PURE__ */ new Map();
|
|
11149
|
+
for (const fn of findNodes(tree.rootNode, "function_definition")) {
|
|
11150
|
+
const nameNode = fn.childForFieldName("name");
|
|
11151
|
+
if (!nameNode) continue;
|
|
11152
|
+
const fnName = getNodeText(nameNode);
|
|
11153
|
+
if (!fnName || bashFnDefs.has(fnName)) continue;
|
|
11154
|
+
const m = /* @__PURE__ */ new Map();
|
|
11155
|
+
for (const p of positionalParams) {
|
|
11156
|
+
const def = {
|
|
11157
|
+
id: defIdCounter++,
|
|
11158
|
+
variable: p,
|
|
11159
|
+
line: fn.startPosition.row + 1,
|
|
11160
|
+
kind: "param",
|
|
11161
|
+
expression: `bash-fn:${fnName}`
|
|
11162
|
+
};
|
|
11163
|
+
defs.push(def);
|
|
11164
|
+
m.set(p, def.id);
|
|
11165
|
+
}
|
|
11166
|
+
bashFnDefs.set(fnName, m);
|
|
11167
|
+
bashFnByNodeId.set(fn.id, fnName);
|
|
11168
|
+
}
|
|
11169
|
+
const enclosingBashFn = (node) => {
|
|
11170
|
+
let cur = node.parent;
|
|
11171
|
+
while (cur) {
|
|
11172
|
+
if (cur.type === "function_definition") return bashFnByNodeId.get(cur.id) ?? null;
|
|
11173
|
+
cur = cur.parent;
|
|
11174
|
+
}
|
|
11175
|
+
return null;
|
|
11176
|
+
};
|
|
11177
|
+
const positionalDef = (varName, node, fallback) => {
|
|
11178
|
+
if (!positionalParams.includes(varName)) return fallback;
|
|
11179
|
+
const fnName = enclosingBashFn(node);
|
|
11180
|
+
if (!fnName) return fallback;
|
|
11181
|
+
return bashFnDefs.get(fnName)?.get(varName) ?? fallback;
|
|
11182
|
+
};
|
|
11045
11183
|
walkTree(tree.rootNode, (node) => {
|
|
11046
11184
|
if (node.type === "variable_assignment") {
|
|
11047
11185
|
const nameNode = node.childForFieldName("name");
|
|
@@ -11094,7 +11232,7 @@ function buildBashDFG(tree) {
|
|
|
11094
11232
|
if (varNameNode) {
|
|
11095
11233
|
const varName = getNodeText(varNameNode);
|
|
11096
11234
|
if (varName && !varName.startsWith("?") && !varName.startsWith("#")) {
|
|
11097
|
-
let reachingDef = findReachingDef(varName, scopeStack);
|
|
11235
|
+
let reachingDef = positionalDef(varName, node, findReachingDef(varName, scopeStack));
|
|
11098
11236
|
if (reachingDef === null && !positionalParams.includes(varName)) {
|
|
11099
11237
|
const def = {
|
|
11100
11238
|
id: defIdCounter++,
|
|
@@ -11118,7 +11256,7 @@ function buildBashDFG(tree) {
|
|
|
11118
11256
|
const varNameNode = node.namedChildCount > 0 ? node.namedChild(0) : null;
|
|
11119
11257
|
if (varNameNode && varNameNode.type === "variable_name") {
|
|
11120
11258
|
const varName = getNodeText(varNameNode);
|
|
11121
|
-
let reachingDef = findReachingDef(varName, scopeStack);
|
|
11259
|
+
let reachingDef = positionalDef(varName, node, findReachingDef(varName, scopeStack));
|
|
11122
11260
|
if (reachingDef === null && !positionalParams.includes(varName)) {
|
|
11123
11261
|
const def = {
|
|
11124
11262
|
id: defIdCounter++,
|
|
@@ -11140,8 +11278,56 @@ function buildBashDFG(tree) {
|
|
|
11140
11278
|
}
|
|
11141
11279
|
});
|
|
11142
11280
|
const chains = computeChains(defs, uses);
|
|
11281
|
+
chains.push(...bashCallArgChains(tree, bashFnDefs, uses, positionalParams));
|
|
11143
11282
|
return { defs, uses, chains };
|
|
11144
11283
|
}
|
|
11284
|
+
function bashCallArgChains(tree, bashFnDefs, uses, positionalParams) {
|
|
11285
|
+
const chains = [];
|
|
11286
|
+
if (bashFnDefs.size === 0) return chains;
|
|
11287
|
+
const renumbers = /* @__PURE__ */ new Set();
|
|
11288
|
+
for (const fn of findNodes(tree.rootNode, "function_definition")) {
|
|
11289
|
+
const nameNode = fn.childForFieldName("name");
|
|
11290
|
+
if (!nameNode) continue;
|
|
11291
|
+
if (/\bshift\b|\bset\s+--/.test(getNodeText(fn))) renumbers.add(getNodeText(nameNode));
|
|
11292
|
+
}
|
|
11293
|
+
const usesByLine = /* @__PURE__ */ new Map();
|
|
11294
|
+
for (const u of uses) {
|
|
11295
|
+
const l = usesByLine.get(u.line) ?? [];
|
|
11296
|
+
l.push(u);
|
|
11297
|
+
usesByLine.set(u.line, l);
|
|
11298
|
+
}
|
|
11299
|
+
for (const cmd of findNodes(tree.rootNode, "command")) {
|
|
11300
|
+
const nameNode = cmd.childForFieldName("name");
|
|
11301
|
+
if (!nameNode) continue;
|
|
11302
|
+
const fnDefs = bashFnDefs.get(getNodeText(nameNode));
|
|
11303
|
+
if (!fnDefs) continue;
|
|
11304
|
+
const fnName = getNodeText(nameNode);
|
|
11305
|
+
let position = 0;
|
|
11306
|
+
for (let i2 = 0; i2 < cmd.childCount; i2++) {
|
|
11307
|
+
const arg = cmd.child(i2);
|
|
11308
|
+
if (!arg || arg.id === nameNode.id) continue;
|
|
11309
|
+
if (arg.type.includes("redirect") || arg.type === "heredoc_body" || arg.type === "file_descriptor") continue;
|
|
11310
|
+
const text = getNodeText(arg);
|
|
11311
|
+
if (!text.trim()) continue;
|
|
11312
|
+
position++;
|
|
11313
|
+
const names = /* @__PURE__ */ new Set();
|
|
11314
|
+
const re = /\$\{?([A-Za-z_][A-Za-z0-9_]*|[0-9@*])/g;
|
|
11315
|
+
let m;
|
|
11316
|
+
while ((m = re.exec(text)) !== null) names.add(m[1]);
|
|
11317
|
+
if (names.size === 0) continue;
|
|
11318
|
+
const spreads = /\$\{?[@*]/.test(text);
|
|
11319
|
+
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);
|
|
11320
|
+
const lineUses = usesByLine.get(arg.startPosition.row + 1) ?? [];
|
|
11321
|
+
for (const name2 of names) {
|
|
11322
|
+
for (const u of lineUses) {
|
|
11323
|
+
if (u.variable !== name2 || u.def_id === null) continue;
|
|
11324
|
+
for (const t of targets) chains.push({ from_def: u.def_id, to_def: t, via: name2 });
|
|
11325
|
+
}
|
|
11326
|
+
}
|
|
11327
|
+
}
|
|
11328
|
+
}
|
|
11329
|
+
return chains;
|
|
11330
|
+
}
|
|
11145
11331
|
function buildRustDFG(tree, cache) {
|
|
11146
11332
|
const defs = [];
|
|
11147
11333
|
const uses = [];
|
|
@@ -11851,6 +12037,198 @@ function javaReplaceCrLfCoverage(method, rawFrom) {
|
|
|
11851
12037
|
return { cr, lf };
|
|
11852
12038
|
}
|
|
11853
12039
|
|
|
12040
|
+
// src/analysis/js-crlf-strip.ts
|
|
12041
|
+
var MAX_PATTERN_LENGTH = 200;
|
|
12042
|
+
var NEUTRAL_AFTER_REPLACE = /* @__PURE__ */ new Set([
|
|
12043
|
+
"replace",
|
|
12044
|
+
"replaceAll",
|
|
12045
|
+
"trim",
|
|
12046
|
+
"trimStart",
|
|
12047
|
+
"trimEnd",
|
|
12048
|
+
"toString",
|
|
12049
|
+
"toLowerCase",
|
|
12050
|
+
"toUpperCase",
|
|
12051
|
+
"slice",
|
|
12052
|
+
"substring"
|
|
12053
|
+
]);
|
|
12054
|
+
var PROBE = "a\r\rb\n\nc\r\nd\n\re x ";
|
|
12055
|
+
function readQuoted(s, i2) {
|
|
12056
|
+
const q = s[i2];
|
|
12057
|
+
if (q !== '"' && q !== "'" && q !== "`") return null;
|
|
12058
|
+
let out2 = "";
|
|
12059
|
+
for (let j = i2 + 1; j < s.length; j++) {
|
|
12060
|
+
const c = s[j];
|
|
12061
|
+
if (c === q) return { value: out2, end: j + 1 };
|
|
12062
|
+
if (q === "`" && c === "$" && s[j + 1] === "{") return null;
|
|
12063
|
+
if (c === "\\") {
|
|
12064
|
+
const n = s[j + 1];
|
|
12065
|
+
if (n === void 0) return null;
|
|
12066
|
+
switch (n) {
|
|
12067
|
+
case "n":
|
|
12068
|
+
out2 += "\n";
|
|
12069
|
+
j++;
|
|
12070
|
+
continue;
|
|
12071
|
+
case "r":
|
|
12072
|
+
out2 += "\r";
|
|
12073
|
+
j++;
|
|
12074
|
+
continue;
|
|
12075
|
+
case "t":
|
|
12076
|
+
out2 += " ";
|
|
12077
|
+
j++;
|
|
12078
|
+
continue;
|
|
12079
|
+
case "0":
|
|
12080
|
+
out2 += "\0";
|
|
12081
|
+
j++;
|
|
12082
|
+
continue;
|
|
12083
|
+
case "x": {
|
|
12084
|
+
const h = /^[0-9a-fA-F]{2}/.exec(s.slice(j + 2, j + 4));
|
|
12085
|
+
if (!h) return null;
|
|
12086
|
+
out2 += String.fromCharCode(parseInt(h[0], 16));
|
|
12087
|
+
j += 3;
|
|
12088
|
+
continue;
|
|
12089
|
+
}
|
|
12090
|
+
case "u": {
|
|
12091
|
+
const h = /^[0-9a-fA-F]{4}/.exec(s.slice(j + 2, j + 6));
|
|
12092
|
+
if (!h) return null;
|
|
12093
|
+
out2 += String.fromCharCode(parseInt(h[0], 16));
|
|
12094
|
+
j += 5;
|
|
12095
|
+
continue;
|
|
12096
|
+
}
|
|
12097
|
+
default:
|
|
12098
|
+
out2 += n;
|
|
12099
|
+
j++;
|
|
12100
|
+
continue;
|
|
12101
|
+
}
|
|
12102
|
+
}
|
|
12103
|
+
if (c === "\n" && q !== "`") return null;
|
|
12104
|
+
out2 += c;
|
|
12105
|
+
}
|
|
12106
|
+
return null;
|
|
12107
|
+
}
|
|
12108
|
+
function readRegexLiteral(s, i2) {
|
|
12109
|
+
if (s[i2] !== "/") return null;
|
|
12110
|
+
let inClass = false;
|
|
12111
|
+
for (let j = i2 + 1; j < s.length; j++) {
|
|
12112
|
+
const c = s[j];
|
|
12113
|
+
if (c === "\n") return null;
|
|
12114
|
+
if (c === "\\") {
|
|
12115
|
+
j++;
|
|
12116
|
+
continue;
|
|
12117
|
+
}
|
|
12118
|
+
if (inClass) {
|
|
12119
|
+
if (c === "]") inClass = false;
|
|
12120
|
+
continue;
|
|
12121
|
+
}
|
|
12122
|
+
if (c === "[") {
|
|
12123
|
+
inClass = true;
|
|
12124
|
+
continue;
|
|
12125
|
+
}
|
|
12126
|
+
if (c === "/") {
|
|
12127
|
+
const flags2 = /^[dgimsuvy]*/.exec(s.slice(j + 1))[0];
|
|
12128
|
+
return { source: s.slice(i2 + 1, j), flags: flags2, end: j + 1 + flags2.length };
|
|
12129
|
+
}
|
|
12130
|
+
}
|
|
12131
|
+
return null;
|
|
12132
|
+
}
|
|
12133
|
+
var skipWs = (s, i2) => {
|
|
12134
|
+
while (i2 < s.length && /\s/.test(s[i2])) i2++;
|
|
12135
|
+
return i2;
|
|
12136
|
+
};
|
|
12137
|
+
function parseSteps(chainText) {
|
|
12138
|
+
const steps = [];
|
|
12139
|
+
const callRe = /\.\s*(replaceAll|replace)\s*\(/g;
|
|
12140
|
+
let m;
|
|
12141
|
+
while ((m = callRe.exec(chainText)) !== null) {
|
|
12142
|
+
const kind = m[1];
|
|
12143
|
+
let i2 = skipWs(chainText, m.index + m[0].length);
|
|
12144
|
+
let pattern;
|
|
12145
|
+
const rx = readRegexLiteral(chainText, i2);
|
|
12146
|
+
if (rx) {
|
|
12147
|
+
if (rx.source.length > MAX_PATTERN_LENGTH) return null;
|
|
12148
|
+
try {
|
|
12149
|
+
pattern = new RegExp(rx.source, rx.flags);
|
|
12150
|
+
} catch {
|
|
12151
|
+
return null;
|
|
12152
|
+
}
|
|
12153
|
+
i2 = rx.end;
|
|
12154
|
+
} else {
|
|
12155
|
+
const str = readQuoted(chainText, i2);
|
|
12156
|
+
if (!str) return null;
|
|
12157
|
+
pattern = str.value;
|
|
12158
|
+
i2 = str.end;
|
|
12159
|
+
}
|
|
12160
|
+
i2 = skipWs(chainText, i2);
|
|
12161
|
+
if (chainText[i2] !== ",") return null;
|
|
12162
|
+
i2 = skipWs(chainText, i2 + 1);
|
|
12163
|
+
const rep = readQuoted(chainText, i2);
|
|
12164
|
+
if (!rep) return null;
|
|
12165
|
+
i2 = skipWs(chainText, rep.end);
|
|
12166
|
+
if (chainText[i2] !== ")") return null;
|
|
12167
|
+
steps.push({ kind, pattern, replacement: rep.value });
|
|
12168
|
+
callRe.lastIndex = i2 + 1;
|
|
12169
|
+
}
|
|
12170
|
+
return steps;
|
|
12171
|
+
}
|
|
12172
|
+
function run2(steps, input) {
|
|
12173
|
+
let s = input;
|
|
12174
|
+
try {
|
|
12175
|
+
for (const st of steps) {
|
|
12176
|
+
if (st.kind === "replaceAll" && st.pattern instanceof RegExp && !st.pattern.global) return null;
|
|
12177
|
+
s = st.kind === "replaceAll" ? s.replaceAll(st.pattern, st.replacement) : s.replace(st.pattern, st.replacement);
|
|
12178
|
+
}
|
|
12179
|
+
} catch {
|
|
12180
|
+
return null;
|
|
12181
|
+
}
|
|
12182
|
+
return s;
|
|
12183
|
+
}
|
|
12184
|
+
function jsReplaceChainVerdict(chainText) {
|
|
12185
|
+
const steps = parseSteps(chainText);
|
|
12186
|
+
if (!steps || steps.length === 0) return "undecided";
|
|
12187
|
+
const first = chainText.search(/\.\s*(?:replaceAll|replace)\s*\(/);
|
|
12188
|
+
for (const seg of chainText.slice(first + 1).matchAll(/\.\s*([A-Za-z_$][\w$]*)\s*\(/g)) {
|
|
12189
|
+
if (!NEUTRAL_AFTER_REPLACE.has(seg[1])) return "undecided";
|
|
12190
|
+
}
|
|
12191
|
+
const out2 = run2(steps, PROBE);
|
|
12192
|
+
if (out2 === null) return "undecided";
|
|
12193
|
+
return out2.includes("\r") || out2.includes("\n") ? "leaves-crlf" : "strips-crlf";
|
|
12194
|
+
}
|
|
12195
|
+
function jsChainTextAt(sourceLines, line, column) {
|
|
12196
|
+
const text = [(sourceLines[line - 1] ?? "").slice(column), ...sourceLines.slice(line, line + 7)].join("\n");
|
|
12197
|
+
let depth = 0;
|
|
12198
|
+
let prevSig = "";
|
|
12199
|
+
let i2 = 0;
|
|
12200
|
+
for (; i2 < text.length; i2++) {
|
|
12201
|
+
const c = text[i2];
|
|
12202
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
12203
|
+
for (i2++; i2 < text.length && text[i2] !== c; i2++) if (text[i2] === "\\") i2++;
|
|
12204
|
+
prevSig = c;
|
|
12205
|
+
continue;
|
|
12206
|
+
}
|
|
12207
|
+
if (c === "/" && depth > 0 && (prevSig === "(" || prevSig === ",")) {
|
|
12208
|
+
const rx = readRegexLiteral(text, i2);
|
|
12209
|
+
if (rx) {
|
|
12210
|
+
i2 = rx.end - 1;
|
|
12211
|
+
prevSig = "/";
|
|
12212
|
+
continue;
|
|
12213
|
+
}
|
|
12214
|
+
}
|
|
12215
|
+
if (c === "(" || c === "[" || c === "{") {
|
|
12216
|
+
depth++;
|
|
12217
|
+
prevSig = c;
|
|
12218
|
+
continue;
|
|
12219
|
+
}
|
|
12220
|
+
if (c === ")" || c === "]" || c === "}") {
|
|
12221
|
+
if (depth === 0) break;
|
|
12222
|
+
depth--;
|
|
12223
|
+
prevSig = c;
|
|
12224
|
+
continue;
|
|
12225
|
+
}
|
|
12226
|
+
if (depth === 0 && !/[\w$.\s]/.test(c)) break;
|
|
12227
|
+
if (!/\s/.test(c)) prevSig = c;
|
|
12228
|
+
}
|
|
12229
|
+
return text.slice(0, i2);
|
|
12230
|
+
}
|
|
12231
|
+
|
|
11854
12232
|
// src/analysis/passes/language-sources-pass.ts
|
|
11855
12233
|
var JINJA_LITERAL_ARG_RE_FRAG = `(?:"[^"\\\\]*"|'[^'\\\\]*')`;
|
|
11856
12234
|
var JINJA_SAFE_RTS_RE = new RegExp(
|
|
@@ -12634,6 +13012,10 @@ var DEFAULT_SOURCES = [
|
|
|
12634
13012
|
{ method: "get", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
|
|
12635
13013
|
{ method: "hget", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
|
|
12636
13014
|
{ method: "mget", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
|
|
13015
|
+
// Node redis clients are often bound to `cache` from `createClient()`; class
|
|
13016
|
+
// `cache` is scoped to JS/TS (Python Django cache above) and Map.get is
|
|
13017
|
+
// filtered in sink-filter-pass (#818 src14).
|
|
13018
|
+
{ method: "get", class: "cache", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
|
|
12637
13019
|
{ method: "get", class: "Jedis", type: "db_input", severity: "medium", return_tainted: true, languages: ["java"] },
|
|
12638
13020
|
{ method: "hget", class: "Jedis", type: "db_input", severity: "medium", return_tainted: true, languages: ["java"] },
|
|
12639
13021
|
{ method: "mget", class: "Jedis", type: "db_input", severity: "medium", return_tainted: true, languages: ["java"] },
|
|
@@ -12813,7 +13195,9 @@ var TRUST_BOUNDARY_FRAMEWORK_SINKS = [
|
|
|
12813
13195
|
// on arg[1] (the *Cookie) catches struct-literal composite tainting.
|
|
12814
13196
|
{ method: "SetCookie", class: "http", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
12815
13197
|
// gin: c.SetCookie(name, value, maxAge, path, domain, secure, httpOnly).
|
|
12816
|
-
{ method: "SetCookie", class: "Context", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["go"] }
|
|
13198
|
+
{ method: "SetCookie", class: "Context", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
13199
|
+
// --- Rust: actix-session / tower-sessions write -------------------------
|
|
13200
|
+
{ method: "insert", class: "Session", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["rust"] }
|
|
12817
13201
|
];
|
|
12818
13202
|
var DEFAULT_SINKS = [
|
|
12819
13203
|
// SQL Injection (CWE-89)
|
|
@@ -13474,7 +13858,9 @@ var DEFAULT_SINKS = [
|
|
|
13474
13858
|
{ method: "addParametersToContext", class: "StaticParametersInterceptor", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
|
|
13475
13859
|
{ method: "build", class: "HttpParameters", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
|
|
13476
13860
|
// Cron expression parsing (DoS/ReDoS)
|
|
13477
|
-
|
|
13861
|
+
// cognium-dev#998 — Java cron-utils `CronParser`; scoped to Java so a JS/TS
|
|
13862
|
+
// `parser.parse(...)` (tree-sitter, a lockfile parser) never matches it by name.
|
|
13863
|
+
{ method: "parse", class: "CronParser", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
13478
13864
|
{ method: "isValid", class: "CronValidator", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0] },
|
|
13479
13865
|
// ff4j feature flags
|
|
13480
13866
|
{ method: "check", class: "FF4j", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0] },
|
|
@@ -13571,9 +13957,13 @@ var DEFAULT_SINKS = [
|
|
|
13571
13957
|
{ method: "selectNodes", class: "Node", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0] },
|
|
13572
13958
|
{ method: "selectSingleNode", class: "Node", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0] },
|
|
13573
13959
|
// XXE - XML External Entity (CWE-611)
|
|
13574
|
-
|
|
13575
|
-
|
|
13576
|
-
|
|
13960
|
+
// cognium-dev#998 — JAXP parsers are JVM classes. Scoped to Java so the
|
|
13961
|
+
// receiver-name heuristic (`parser` → SAXParser) cannot turn a JS/TS
|
|
13962
|
+
// `parser.parse(text)` into an XXE sink. JS/TS XML parsers (sax, xml2js,
|
|
13963
|
+
// libxmljs, DOMParser) have their own entries.
|
|
13964
|
+
{ method: "parse", class: "DocumentBuilder", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
13965
|
+
{ method: "parse", class: "SAXParser", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
13966
|
+
{ method: "parse", class: "XMLReader", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
13577
13967
|
{ method: "unmarshal", class: "Unmarshaller", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0] },
|
|
13578
13968
|
{ method: "newTransformer", class: "TransformerFactory", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0] },
|
|
13579
13969
|
{ method: "transform", class: "Transformer", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0] },
|
|
@@ -13828,6 +14218,46 @@ var DEFAULT_SINKS = [
|
|
|
13828
14218
|
{ method: "each", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
|
|
13829
14219
|
{ method: "get", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
|
|
13830
14220
|
{ method: "exec", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
|
|
14221
|
+
// DuckDB from JS/TS: `@duckdb/node-api` (DuckDBConnection), the classic
|
|
14222
|
+
// `duckdb` / `duckdb-async` bindings (Database / Connection) and
|
|
14223
|
+
// `react-native-duckdb` (Database / Transaction). The method names are
|
|
14224
|
+
// generic and the receivers come from factories (`await instance.connect()`,
|
|
14225
|
+
// `open({...})`), so the receiver cannot be typed: the import is the gate
|
|
14226
|
+
// (`requires_import`). Only the SQL text (arg 0) is a sink — values passed
|
|
14227
|
+
// as bound parameters (`run(sql, [v])`, `execute(sql, params)`,
|
|
14228
|
+
// `executeNamed(sql, {k: v})`, `stmt.bind(...)`) are parameterised and do
|
|
14229
|
+
// not reach it.
|
|
14230
|
+
{ method: "run", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api", "duckdb", "duckdb-async"] },
|
|
14231
|
+
{ 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"] },
|
|
14232
|
+
{ 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"] },
|
|
14233
|
+
{ method: "runAndRead", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14234
|
+
{ method: "runAndReadAll", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14235
|
+
{ method: "runAndReadUntil", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14236
|
+
{ method: "streamAndRead", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14237
|
+
{ method: "streamAndReadAll", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14238
|
+
{ method: "streamAndReadUntil", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14239
|
+
{ method: "start", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14240
|
+
{ method: "startStream", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14241
|
+
{ method: "extractStatements", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
|
|
14242
|
+
{ method: "all", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
|
|
14243
|
+
{ method: "each", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
|
|
14244
|
+
{ method: "exec", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
|
|
14245
|
+
{ method: "arrowIPCAll", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
|
|
14246
|
+
{ method: "arrowIPCStream", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
|
|
14247
|
+
{ method: "execute", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
|
|
14248
|
+
{ method: "executeSync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
|
|
14249
|
+
{ method: "executeNamed", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
|
|
14250
|
+
{ method: "executeSyncNamed", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
|
|
14251
|
+
{ method: "streamNamed", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
|
|
14252
|
+
// RxDB (Mango queries, any storage incl. SQLite): a selector or query
|
|
14253
|
+
// object, a field name passed to the query builder, or a `$regex` pattern
|
|
14254
|
+
// under caller control is NoSQL operator injection (CWE-943). Gated on the
|
|
14255
|
+
// `rxdb` import for the same reason as DuckDB above. `find` is already a
|
|
14256
|
+
// classless JS NoSQL sink in the JavaScript plugin.
|
|
14257
|
+
{ method: "findOne", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["rxdb"] },
|
|
14258
|
+
{ method: "count", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["rxdb"] },
|
|
14259
|
+
{ method: "where", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["rxdb"] },
|
|
14260
|
+
{ method: "regex", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["rxdb"] },
|
|
13831
14261
|
// TypeORM QueryBuilder raw fragments (CWE-89). TypeORM's raw `.query(sql)`
|
|
13832
14262
|
// escape hatch is already covered by the classless `query` sink in the JS
|
|
13833
14263
|
// plugin (getBuiltinSinks), so it is NOT re-registered here — the dedup
|
|
@@ -13938,6 +14368,8 @@ var DEFAULT_SINKS = [
|
|
|
13938
14368
|
{ method: "get", class: "http", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
13939
14369
|
{ method: "request", class: "https", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
13940
14370
|
{ method: "get", class: "https", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
14371
|
+
// Node.js dns.promises (CWE-918 / NUL-in-name SSRF class — #895)
|
|
14372
|
+
{ method: "resolve", class: "dns.promises", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
13941
14373
|
// needle library (used in NodeGoat)
|
|
13942
14374
|
{ method: "get", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
13943
14375
|
{ method: "post", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
@@ -14432,7 +14864,7 @@ var DEFAULT_SINKS = [
|
|
|
14432
14864
|
// receiver-taint or operator-LHS-taint tracking — the format string is the
|
|
14433
14865
|
// receiver, not an argument. Deferred to Sprint 6 (#86 follow-up).
|
|
14434
14866
|
// C-style: printf / fprintf / sprintf / snprintf via ctypes/cffi.
|
|
14435
|
-
{ method: "printf", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
14867
|
+
{ method: "printf", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["python", "rust"] },
|
|
14436
14868
|
{ method: "fprintf", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [1], languages: ["python"] },
|
|
14437
14869
|
// Go: fmt.Sprintf/Printf/Fprintf/Errorf — format string is first/second arg
|
|
14438
14870
|
{ method: "Sprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
@@ -15501,198 +15933,6 @@ function getDefaultConfig() {
|
|
|
15501
15933
|
};
|
|
15502
15934
|
}
|
|
15503
15935
|
|
|
15504
|
-
// src/analysis/js-crlf-strip.ts
|
|
15505
|
-
var MAX_PATTERN_LENGTH = 200;
|
|
15506
|
-
var NEUTRAL_AFTER_REPLACE = /* @__PURE__ */ new Set([
|
|
15507
|
-
"replace",
|
|
15508
|
-
"replaceAll",
|
|
15509
|
-
"trim",
|
|
15510
|
-
"trimStart",
|
|
15511
|
-
"trimEnd",
|
|
15512
|
-
"toString",
|
|
15513
|
-
"toLowerCase",
|
|
15514
|
-
"toUpperCase",
|
|
15515
|
-
"slice",
|
|
15516
|
-
"substring"
|
|
15517
|
-
]);
|
|
15518
|
-
var PROBE = "a\r\rb\n\nc\r\nd\n\re x ";
|
|
15519
|
-
function readQuoted(s, i2) {
|
|
15520
|
-
const q = s[i2];
|
|
15521
|
-
if (q !== '"' && q !== "'" && q !== "`") return null;
|
|
15522
|
-
let out2 = "";
|
|
15523
|
-
for (let j = i2 + 1; j < s.length; j++) {
|
|
15524
|
-
const c = s[j];
|
|
15525
|
-
if (c === q) return { value: out2, end: j + 1 };
|
|
15526
|
-
if (q === "`" && c === "$" && s[j + 1] === "{") return null;
|
|
15527
|
-
if (c === "\\") {
|
|
15528
|
-
const n = s[j + 1];
|
|
15529
|
-
if (n === void 0) return null;
|
|
15530
|
-
switch (n) {
|
|
15531
|
-
case "n":
|
|
15532
|
-
out2 += "\n";
|
|
15533
|
-
j++;
|
|
15534
|
-
continue;
|
|
15535
|
-
case "r":
|
|
15536
|
-
out2 += "\r";
|
|
15537
|
-
j++;
|
|
15538
|
-
continue;
|
|
15539
|
-
case "t":
|
|
15540
|
-
out2 += " ";
|
|
15541
|
-
j++;
|
|
15542
|
-
continue;
|
|
15543
|
-
case "0":
|
|
15544
|
-
out2 += "\0";
|
|
15545
|
-
j++;
|
|
15546
|
-
continue;
|
|
15547
|
-
case "x": {
|
|
15548
|
-
const h = /^[0-9a-fA-F]{2}/.exec(s.slice(j + 2, j + 4));
|
|
15549
|
-
if (!h) return null;
|
|
15550
|
-
out2 += String.fromCharCode(parseInt(h[0], 16));
|
|
15551
|
-
j += 3;
|
|
15552
|
-
continue;
|
|
15553
|
-
}
|
|
15554
|
-
case "u": {
|
|
15555
|
-
const h = /^[0-9a-fA-F]{4}/.exec(s.slice(j + 2, j + 6));
|
|
15556
|
-
if (!h) return null;
|
|
15557
|
-
out2 += String.fromCharCode(parseInt(h[0], 16));
|
|
15558
|
-
j += 5;
|
|
15559
|
-
continue;
|
|
15560
|
-
}
|
|
15561
|
-
default:
|
|
15562
|
-
out2 += n;
|
|
15563
|
-
j++;
|
|
15564
|
-
continue;
|
|
15565
|
-
}
|
|
15566
|
-
}
|
|
15567
|
-
if (c === "\n" && q !== "`") return null;
|
|
15568
|
-
out2 += c;
|
|
15569
|
-
}
|
|
15570
|
-
return null;
|
|
15571
|
-
}
|
|
15572
|
-
function readRegexLiteral(s, i2) {
|
|
15573
|
-
if (s[i2] !== "/") return null;
|
|
15574
|
-
let inClass = false;
|
|
15575
|
-
for (let j = i2 + 1; j < s.length; j++) {
|
|
15576
|
-
const c = s[j];
|
|
15577
|
-
if (c === "\n") return null;
|
|
15578
|
-
if (c === "\\") {
|
|
15579
|
-
j++;
|
|
15580
|
-
continue;
|
|
15581
|
-
}
|
|
15582
|
-
if (inClass) {
|
|
15583
|
-
if (c === "]") inClass = false;
|
|
15584
|
-
continue;
|
|
15585
|
-
}
|
|
15586
|
-
if (c === "[") {
|
|
15587
|
-
inClass = true;
|
|
15588
|
-
continue;
|
|
15589
|
-
}
|
|
15590
|
-
if (c === "/") {
|
|
15591
|
-
const flags2 = /^[dgimsuvy]*/.exec(s.slice(j + 1))[0];
|
|
15592
|
-
return { source: s.slice(i2 + 1, j), flags: flags2, end: j + 1 + flags2.length };
|
|
15593
|
-
}
|
|
15594
|
-
}
|
|
15595
|
-
return null;
|
|
15596
|
-
}
|
|
15597
|
-
var skipWs = (s, i2) => {
|
|
15598
|
-
while (i2 < s.length && /\s/.test(s[i2])) i2++;
|
|
15599
|
-
return i2;
|
|
15600
|
-
};
|
|
15601
|
-
function parseSteps(chainText) {
|
|
15602
|
-
const steps = [];
|
|
15603
|
-
const callRe = /\.\s*(replaceAll|replace)\s*\(/g;
|
|
15604
|
-
let m;
|
|
15605
|
-
while ((m = callRe.exec(chainText)) !== null) {
|
|
15606
|
-
const kind = m[1];
|
|
15607
|
-
let i2 = skipWs(chainText, m.index + m[0].length);
|
|
15608
|
-
let pattern;
|
|
15609
|
-
const rx = readRegexLiteral(chainText, i2);
|
|
15610
|
-
if (rx) {
|
|
15611
|
-
if (rx.source.length > MAX_PATTERN_LENGTH) return null;
|
|
15612
|
-
try {
|
|
15613
|
-
pattern = new RegExp(rx.source, rx.flags);
|
|
15614
|
-
} catch {
|
|
15615
|
-
return null;
|
|
15616
|
-
}
|
|
15617
|
-
i2 = rx.end;
|
|
15618
|
-
} else {
|
|
15619
|
-
const str = readQuoted(chainText, i2);
|
|
15620
|
-
if (!str) return null;
|
|
15621
|
-
pattern = str.value;
|
|
15622
|
-
i2 = str.end;
|
|
15623
|
-
}
|
|
15624
|
-
i2 = skipWs(chainText, i2);
|
|
15625
|
-
if (chainText[i2] !== ",") return null;
|
|
15626
|
-
i2 = skipWs(chainText, i2 + 1);
|
|
15627
|
-
const rep = readQuoted(chainText, i2);
|
|
15628
|
-
if (!rep) return null;
|
|
15629
|
-
i2 = skipWs(chainText, rep.end);
|
|
15630
|
-
if (chainText[i2] !== ")") return null;
|
|
15631
|
-
steps.push({ kind, pattern, replacement: rep.value });
|
|
15632
|
-
callRe.lastIndex = i2 + 1;
|
|
15633
|
-
}
|
|
15634
|
-
return steps;
|
|
15635
|
-
}
|
|
15636
|
-
function run2(steps, input) {
|
|
15637
|
-
let s = input;
|
|
15638
|
-
try {
|
|
15639
|
-
for (const st of steps) {
|
|
15640
|
-
if (st.kind === "replaceAll" && st.pattern instanceof RegExp && !st.pattern.global) return null;
|
|
15641
|
-
s = st.kind === "replaceAll" ? s.replaceAll(st.pattern, st.replacement) : s.replace(st.pattern, st.replacement);
|
|
15642
|
-
}
|
|
15643
|
-
} catch {
|
|
15644
|
-
return null;
|
|
15645
|
-
}
|
|
15646
|
-
return s;
|
|
15647
|
-
}
|
|
15648
|
-
function jsReplaceChainVerdict(chainText) {
|
|
15649
|
-
const steps = parseSteps(chainText);
|
|
15650
|
-
if (!steps || steps.length === 0) return "undecided";
|
|
15651
|
-
const first = chainText.search(/\.\s*(?:replaceAll|replace)\s*\(/);
|
|
15652
|
-
for (const seg of chainText.slice(first + 1).matchAll(/\.\s*([A-Za-z_$][\w$]*)\s*\(/g)) {
|
|
15653
|
-
if (!NEUTRAL_AFTER_REPLACE.has(seg[1])) return "undecided";
|
|
15654
|
-
}
|
|
15655
|
-
const out2 = run2(steps, PROBE);
|
|
15656
|
-
if (out2 === null) return "undecided";
|
|
15657
|
-
return out2.includes("\r") || out2.includes("\n") ? "leaves-crlf" : "strips-crlf";
|
|
15658
|
-
}
|
|
15659
|
-
function jsChainTextAt(sourceLines, line, column) {
|
|
15660
|
-
const text = [(sourceLines[line - 1] ?? "").slice(column), ...sourceLines.slice(line, line + 7)].join("\n");
|
|
15661
|
-
let depth = 0;
|
|
15662
|
-
let prevSig = "";
|
|
15663
|
-
let i2 = 0;
|
|
15664
|
-
for (; i2 < text.length; i2++) {
|
|
15665
|
-
const c = text[i2];
|
|
15666
|
-
if (c === '"' || c === "'" || c === "`") {
|
|
15667
|
-
for (i2++; i2 < text.length && text[i2] !== c; i2++) if (text[i2] === "\\") i2++;
|
|
15668
|
-
prevSig = c;
|
|
15669
|
-
continue;
|
|
15670
|
-
}
|
|
15671
|
-
if (c === "/" && depth > 0 && (prevSig === "(" || prevSig === ",")) {
|
|
15672
|
-
const rx = readRegexLiteral(text, i2);
|
|
15673
|
-
if (rx) {
|
|
15674
|
-
i2 = rx.end - 1;
|
|
15675
|
-
prevSig = "/";
|
|
15676
|
-
continue;
|
|
15677
|
-
}
|
|
15678
|
-
}
|
|
15679
|
-
if (c === "(" || c === "[" || c === "{") {
|
|
15680
|
-
depth++;
|
|
15681
|
-
prevSig = c;
|
|
15682
|
-
continue;
|
|
15683
|
-
}
|
|
15684
|
-
if (c === ")" || c === "]" || c === "}") {
|
|
15685
|
-
if (depth === 0) break;
|
|
15686
|
-
depth--;
|
|
15687
|
-
prevSig = c;
|
|
15688
|
-
continue;
|
|
15689
|
-
}
|
|
15690
|
-
if (depth === 0 && !/[\w$.\s]/.test(c)) break;
|
|
15691
|
-
if (!/\s/.test(c)) prevSig = c;
|
|
15692
|
-
}
|
|
15693
|
-
return text.slice(0, i2);
|
|
15694
|
-
}
|
|
15695
|
-
|
|
15696
15936
|
// src/analysis/constant-propagation/patterns.ts
|
|
15697
15937
|
var TAINT_PATTERNS = [
|
|
15698
15938
|
// HTTP Sources (Servlet API)
|
|
@@ -16082,13 +16322,35 @@ function dropCSharpObjectCarriedWaypoints(sinks, calls) {
|
|
|
16082
16322
|
function analyzeTaint(calls, types, config = getDefaultConfig(), typeHierarchy, language, code, imports) {
|
|
16083
16323
|
const sourceLines = code !== void 0 ? code.split("\n") : void 0;
|
|
16084
16324
|
const sources = findSources(calls, types, config.sources, sourceLines, language, imports);
|
|
16085
|
-
let sinkPatterns =
|
|
16325
|
+
let sinkPatterns = config.sinks.filter((p) => sinkPatternImportApplies(p, imports));
|
|
16326
|
+
sinkPatterns = expandPromisifyAliases(sinkPatterns, sourceLines, language);
|
|
16086
16327
|
sinkPatterns = expandIndirectEvalAliases(sinkPatterns, sourceLines, language);
|
|
16087
16328
|
const sinks = findSinks(calls, sinkPatterns, typeHierarchy, language, sourceLines, types);
|
|
16088
16329
|
const gatedSinks = language === "csharp" ? dropCSharpObjectCarriedWaypoints(sinks, calls) : sinks;
|
|
16089
16330
|
const sanitizers = findSanitizers(calls, types, config.sanitizers, sourceLines, language);
|
|
16090
16331
|
return { sources, sinks: gatedSinks, sanitizers };
|
|
16091
16332
|
}
|
|
16333
|
+
function isBoundParameterCall(call, pattern, sourceLines) {
|
|
16334
|
+
if (!pattern.requires_import || pattern.type !== "sql_injection") return false;
|
|
16335
|
+
const first = call.arguments[0]?.expression?.trim() ?? "";
|
|
16336
|
+
if (first.startsWith("[") || first.startsWith("{")) return true;
|
|
16337
|
+
const receiver = call.receiver;
|
|
16338
|
+
if (!receiver || !sourceLines || !/^[A-Za-z_$][\w$]*$/.test(receiver)) return false;
|
|
16339
|
+
const assignedFromPrepare = new RegExp(
|
|
16340
|
+
`\\b${receiver.replace(/\$/g, "\\$")}\\s*(?::[^=]+)?=\\s*(?:await\\s+)?[\\w$.]+\\.prepare\\s*\\(`
|
|
16341
|
+
);
|
|
16342
|
+
return sourceLines.some((line) => assignedFromPrepare.test(line));
|
|
16343
|
+
}
|
|
16344
|
+
function sinkPatternImportApplies(pattern, imports) {
|
|
16345
|
+
const required = pattern.requires_import;
|
|
16346
|
+
if (!required || required.length === 0) return true;
|
|
16347
|
+
if (!imports || imports.length === 0) return false;
|
|
16348
|
+
return imports.some((imp) => {
|
|
16349
|
+
const from = imp.from_package;
|
|
16350
|
+
if (!from) return false;
|
|
16351
|
+
return required.some((m) => from === m || from.startsWith(m + "/"));
|
|
16352
|
+
});
|
|
16353
|
+
}
|
|
16092
16354
|
function sinkPatternAppliesTo(pattern, language) {
|
|
16093
16355
|
if (language === void 0) return true;
|
|
16094
16356
|
if (pattern.languages && pattern.languages.length > 0 && !pattern.languages.includes(language)) {
|
|
@@ -16490,6 +16752,9 @@ function isInterproceduralTaintableType(typeName, language) {
|
|
|
16490
16752
|
"IEnumerable",
|
|
16491
16753
|
"ICollection",
|
|
16492
16754
|
"IList",
|
|
16755
|
+
"List",
|
|
16756
|
+
"Hashtable",
|
|
16757
|
+
"ArrayList",
|
|
16493
16758
|
"Dictionary",
|
|
16494
16759
|
"IDictionary",
|
|
16495
16760
|
"IReadOnlyList",
|
|
@@ -16503,6 +16768,46 @@ function isInterproceduralTaintableType(typeName, language) {
|
|
|
16503
16768
|
const leaf = baseType.split(".").pop() ?? baseType;
|
|
16504
16769
|
if (csharpTaintable.includes(baseType) || csharpTaintable.includes(leaf)) return true;
|
|
16505
16770
|
}
|
|
16771
|
+
if (language === "rust") {
|
|
16772
|
+
const trimmed = typeName.trim();
|
|
16773
|
+
const unref = trimmed.replace(/^&mut\s+/, "").replace(/^&\s*/, "");
|
|
16774
|
+
const rustBase = unref.split("<")[0].trim();
|
|
16775
|
+
const rustScalars = [
|
|
16776
|
+
"String",
|
|
16777
|
+
"str",
|
|
16778
|
+
"bool",
|
|
16779
|
+
"char",
|
|
16780
|
+
"i8",
|
|
16781
|
+
"i16",
|
|
16782
|
+
"i32",
|
|
16783
|
+
"i64",
|
|
16784
|
+
"i128",
|
|
16785
|
+
"isize",
|
|
16786
|
+
"u8",
|
|
16787
|
+
"u16",
|
|
16788
|
+
"u32",
|
|
16789
|
+
"u64",
|
|
16790
|
+
"u128",
|
|
16791
|
+
"usize",
|
|
16792
|
+
"f32",
|
|
16793
|
+
"f64"
|
|
16794
|
+
];
|
|
16795
|
+
if (rustScalars.includes(rustBase)) return true;
|
|
16796
|
+
if (rustBase === "()" || rustBase === "!") return false;
|
|
16797
|
+
if (unref.startsWith("Vec<") || unref.startsWith("Option<") || unref.startsWith("Result<")) {
|
|
16798
|
+
return true;
|
|
16799
|
+
}
|
|
16800
|
+
if (/^[A-Z][A-Za-z0-9_]*$/.test(rustBase)) return true;
|
|
16801
|
+
}
|
|
16802
|
+
if (language === "javascript" || language === "typescript") {
|
|
16803
|
+
const trimmed = typeName.trim();
|
|
16804
|
+
const jsScalars = ["string", "number", "boolean", "bigint", "any", "unknown", "object"];
|
|
16805
|
+
if (jsScalars.includes(baseType)) return true;
|
|
16806
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.startsWith("(")) return true;
|
|
16807
|
+
if (/\bRecord\s*</.test(trimmed)) return true;
|
|
16808
|
+
if (["void", "undefined", "null", "never"].includes(baseType)) return false;
|
|
16809
|
+
if (/^[A-Za-z_][\w.$]*$/.test(baseType)) return true;
|
|
16810
|
+
}
|
|
16506
16811
|
if (typeName.endsWith("[]")) {
|
|
16507
16812
|
const elementType = typeName.slice(0, -2);
|
|
16508
16813
|
if (elementType === "String" || elementType === "Object" || elementType === "byte") {
|
|
@@ -16617,6 +16922,31 @@ function isSafeGoExecCommandCall(call, pattern, language) {
|
|
|
16617
16922
|
return false;
|
|
16618
16923
|
}
|
|
16619
16924
|
}
|
|
16925
|
+
if (program === "git") {
|
|
16926
|
+
const subcommandArgPos = pattern.method === "CommandContext" ? 2 : 1;
|
|
16927
|
+
const subcommandArg = call.arguments.find((a) => a.position === subcommandArgPos);
|
|
16928
|
+
let subcommand;
|
|
16929
|
+
if (subcommandArg?.literal !== null && subcommandArg?.literal !== void 0) {
|
|
16930
|
+
subcommand = String(subcommandArg.literal);
|
|
16931
|
+
} else {
|
|
16932
|
+
const expr = (subcommandArg?.expression ?? "").trim();
|
|
16933
|
+
if (expr.startsWith('"') || expr.startsWith("`") || expr.startsWith("'")) {
|
|
16934
|
+
subcommand = expr.slice(1, -1);
|
|
16935
|
+
}
|
|
16936
|
+
}
|
|
16937
|
+
if (subcommand === "ls-remote") {
|
|
16938
|
+
const hasEndOfOptions = call.arguments.some((a) => {
|
|
16939
|
+
if (a.literal !== null && a.literal !== void 0) {
|
|
16940
|
+
return String(a.literal) === "--";
|
|
16941
|
+
}
|
|
16942
|
+
const expr = (a.expression ?? "").trim();
|
|
16943
|
+
return expr === '"--"' || expr === "'--'" || expr === "`--`";
|
|
16944
|
+
});
|
|
16945
|
+
if (!hasEndOfOptions) {
|
|
16946
|
+
return false;
|
|
16947
|
+
}
|
|
16948
|
+
}
|
|
16949
|
+
}
|
|
16620
16950
|
return true;
|
|
16621
16951
|
}
|
|
16622
16952
|
function isSafeJSChildProcessCall(call, pattern, language) {
|
|
@@ -17020,6 +17350,29 @@ function isRegexReceiver(receiver, sourceLines) {
|
|
|
17020
17350
|
const decl = new RegExp(`(?:const|let|var)\\s+${escapeRe(r)}\\s*=\\s*(?:/|(?:new\\s+)?RegExp\\s*\\()`);
|
|
17021
17351
|
return sourceLines.some((l) => decl.test(l));
|
|
17022
17352
|
}
|
|
17353
|
+
function isNavigationReceiverName(receiver) {
|
|
17354
|
+
const r = (receiver ?? "").trim();
|
|
17355
|
+
if (!r) return false;
|
|
17356
|
+
const last = r.slice(r.lastIndexOf(".") + 1).replace(/\(\)$/, "");
|
|
17357
|
+
return /router|history|navigat|^nav$/i.test(last);
|
|
17358
|
+
}
|
|
17359
|
+
function isInMemoryMapGetReceiver(receiver, sourceLines) {
|
|
17360
|
+
const raw = (receiver ?? "").trim();
|
|
17361
|
+
if (!raw || !sourceLines) return false;
|
|
17362
|
+
if (/\.properties$/i.test(raw)) return true;
|
|
17363
|
+
const lastDot = raw.lastIndexOf(".");
|
|
17364
|
+
const r = (lastDot >= 0 ? raw.slice(lastDot + 1) : raw).trim();
|
|
17365
|
+
if (/^(properties|memoryCache)$/i.test(r)) return true;
|
|
17366
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(r)) return false;
|
|
17367
|
+
const n = escapeRe(r);
|
|
17368
|
+
const mapCtor = new RegExp(
|
|
17369
|
+
`\\b${n}\\s*(?::[^=;]*)?=\\s*new\\s+(?:Weak)?Map\\s*(?:<.*>)?\\s*\\(`
|
|
17370
|
+
);
|
|
17371
|
+
const mapType = new RegExp(
|
|
17372
|
+
`\\b${n}\\s*\\??\\s*:\\s*(?:Readonly)?(?:Weak)?Map\\s*<`
|
|
17373
|
+
);
|
|
17374
|
+
return sourceLines.some((l) => mapCtor.test(l) || mapType.test(l));
|
|
17375
|
+
}
|
|
17023
17376
|
function isDeclaredFunctionReference(arg, sourceLines) {
|
|
17024
17377
|
if (arg.literal !== null && arg.literal !== void 0) return false;
|
|
17025
17378
|
if (!sourceLines) return false;
|
|
@@ -17224,6 +17577,12 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
17224
17577
|
if (isParameterizedQueryCall(call, pattern)) {
|
|
17225
17578
|
continue;
|
|
17226
17579
|
}
|
|
17580
|
+
if ((language === "javascript" || language === "typescript") && (pattern.type === "sql_injection" || pattern.type === "nosql_injection") && call.arguments.length === 0 && pattern.arg_positions.every((p) => p >= 0)) {
|
|
17581
|
+
continue;
|
|
17582
|
+
}
|
|
17583
|
+
if (isBoundParameterCall(call, pattern, sourceLines)) {
|
|
17584
|
+
continue;
|
|
17585
|
+
}
|
|
17227
17586
|
if (isSafePythonSubprocessCall(call, pattern, language)) {
|
|
17228
17587
|
continue;
|
|
17229
17588
|
}
|
|
@@ -17281,9 +17640,15 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
17281
17640
|
if (isArgumentlessJsResponseWriter(call, pattern, language)) {
|
|
17282
17641
|
continue;
|
|
17283
17642
|
}
|
|
17643
|
+
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)) {
|
|
17644
|
+
continue;
|
|
17645
|
+
}
|
|
17284
17646
|
if ((pattern.type === "command_injection" || pattern.type === "sql_injection") && call.method_name === "exec" && (language === "javascript" || language === "typescript") && isRegexReceiver(call.receiver, sourceLines)) {
|
|
17285
17647
|
continue;
|
|
17286
17648
|
}
|
|
17649
|
+
if (pattern.type === "sql_injection" && call.method_name === "get" && pattern.class === "Connection" && (language === "javascript" || language === "typescript") && isInMemoryMapGetReceiver(call.receiver, sourceLines)) {
|
|
17650
|
+
continue;
|
|
17651
|
+
}
|
|
17287
17652
|
if (pattern.type === "code_injection" && language === "python" && call.method_name === "compile" && call.receiver && call.receiver !== "builtins") {
|
|
17288
17653
|
continue;
|
|
17289
17654
|
}
|
|
@@ -17300,7 +17665,7 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
17300
17665
|
continue;
|
|
17301
17666
|
}
|
|
17302
17667
|
const location = formatCallLocation(call);
|
|
17303
|
-
const key = `${location}:${call.location.line}:${pattern.cwe}`;
|
|
17668
|
+
const key = `${location}:${call.location.line}:${call.location.column}:${pattern.cwe}`;
|
|
17304
17669
|
const confidence = calculateSinkConfidence(call, pattern);
|
|
17305
17670
|
const existing = sinkMap.get(key);
|
|
17306
17671
|
if (!existing || confidence > existing.confidence) {
|
|
@@ -17311,6 +17676,7 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
17311
17676
|
cwe: pattern.cwe,
|
|
17312
17677
|
location,
|
|
17313
17678
|
line: call.location.line,
|
|
17679
|
+
callColumn: call.location.column,
|
|
17314
17680
|
confidence,
|
|
17315
17681
|
method: call.method_name,
|
|
17316
17682
|
argPositions: pattern.arg_positions,
|
|
@@ -17348,6 +17714,15 @@ function matchesSourcePattern(call, pattern, language, types, imports) {
|
|
|
17348
17714
|
return false;
|
|
17349
17715
|
}
|
|
17350
17716
|
}
|
|
17717
|
+
if (!pattern.class && pattern.return_tainted && pattern.method && (language === "javascript" || language === "typescript")) {
|
|
17718
|
+
const expressRequestAccessors = /* @__PURE__ */ new Set(["query", "body", "params", "headers", "cookies"]);
|
|
17719
|
+
if (expressRequestAccessors.has(pattern.method)) {
|
|
17720
|
+
const recv = call.receiver?.toLowerCase() ?? "";
|
|
17721
|
+
if (recv !== "req" && recv !== "request" && recv !== "ctx") {
|
|
17722
|
+
return false;
|
|
17723
|
+
}
|
|
17724
|
+
}
|
|
17725
|
+
}
|
|
17351
17726
|
return pattern.return_tainted === true;
|
|
17352
17727
|
}
|
|
17353
17728
|
return false;
|
|
@@ -17441,6 +17816,7 @@ var SAFE_RECEIVERS_BY_METHOD = {
|
|
|
17441
17816
|
"database",
|
|
17442
17817
|
"sqlite",
|
|
17443
17818
|
"conn",
|
|
17819
|
+
"con",
|
|
17444
17820
|
"connection",
|
|
17445
17821
|
"client",
|
|
17446
17822
|
"pool",
|
|
@@ -17717,7 +18093,7 @@ function matchesSinkPattern(call, pattern, typeHierarchy, language) {
|
|
|
17717
18093
|
}
|
|
17718
18094
|
}
|
|
17719
18095
|
}
|
|
17720
|
-
if (!pattern.class && call.receiver) {
|
|
18096
|
+
if (!pattern.class && call.receiver && !pattern.requires_import) {
|
|
17721
18097
|
if (isKnownSafeReceiverForMethod(call.receiver, call.method_name, pattern.type)) {
|
|
17722
18098
|
return false;
|
|
17723
18099
|
}
|
|
@@ -18450,6 +18826,8 @@ function javaReplaceChainCredit(chainText) {
|
|
|
18450
18826
|
let lf = false;
|
|
18451
18827
|
let lt = false;
|
|
18452
18828
|
let gt = false;
|
|
18829
|
+
let openBrace = false;
|
|
18830
|
+
let closeBrace = false;
|
|
18453
18831
|
for (const m of chainText.matchAll(JAVA_REPLACE_ARGS_RE)) {
|
|
18454
18832
|
const kind = m[1];
|
|
18455
18833
|
const rawFrom = m[2] ?? m[3] ?? "";
|
|
@@ -18460,10 +18838,13 @@ function javaReplaceChainCredit(chainText) {
|
|
|
18460
18838
|
if (cov.lf) lf = true;
|
|
18461
18839
|
if (from === "<" && to.startsWith("&")) lt = true;
|
|
18462
18840
|
if (from === ">" && to.startsWith("&")) gt = true;
|
|
18841
|
+
if (from === "{") openBrace = true;
|
|
18842
|
+
if (from === "}") closeBrace = true;
|
|
18463
18843
|
}
|
|
18464
18844
|
const out2 = [];
|
|
18465
18845
|
if (cr && lf) out2.push("log_injection");
|
|
18466
18846
|
if (lt && gt) out2.push("xss");
|
|
18847
|
+
if (openBrace && closeBrace) out2.push("code_injection");
|
|
18467
18848
|
return out2;
|
|
18468
18849
|
}
|
|
18469
18850
|
function matchesSanitizerPattern(call, pattern) {
|
|
@@ -19793,6 +20174,11 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19793
20174
|
taintedArrayElements = /* @__PURE__ */ new Map();
|
|
19794
20175
|
// Track current method name for scoping local variables
|
|
19795
20176
|
currentMethod = null;
|
|
20177
|
+
/** Start lines of nested `{ ... }` blocks — disambiguate same-named locals (#476 Collections10). */
|
|
20178
|
+
blockScopeStack = [];
|
|
20179
|
+
lineBlockScopes = /* @__PURE__ */ new Map();
|
|
20180
|
+
/** Skip block-scope push on the method/constructor body `{ ... }` (#476). */
|
|
20181
|
+
skipMethodBodyBlockScope = false;
|
|
19796
20182
|
// Track conditional taints: which variables were tainted under which conditions
|
|
19797
20183
|
// Maps condition expression string → set of variables tainted under that condition
|
|
19798
20184
|
conditionalTaints = /* @__PURE__ */ new Map();
|
|
@@ -19806,6 +20192,8 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19806
20192
|
inSynchronizedBlock = false;
|
|
19807
20193
|
// Track iterator sources: iterator variable name → collection name it was created from
|
|
19808
20194
|
iteratorSources = /* @__PURE__ */ new Map();
|
|
20195
|
+
/** Vars assigned from `iter.next()` — do not retro-clear on later iterator rebind (#476). */
|
|
20196
|
+
taintedFromIteratorNext = /* @__PURE__ */ new Set();
|
|
19809
20197
|
// Track class field names (declared at class level, not local variables)
|
|
19810
20198
|
classFields = /* @__PURE__ */ new Set();
|
|
19811
20199
|
// Track tainted method parameters for inter-procedural analysis
|
|
@@ -19863,12 +20251,16 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19863
20251
|
this.sanitizedVars.clear();
|
|
19864
20252
|
this.sanitizedVarUncovered.clear();
|
|
19865
20253
|
this.currentMethod = null;
|
|
20254
|
+
this.blockScopeStack = [];
|
|
20255
|
+
this.lineBlockScopes.clear();
|
|
20256
|
+
this.skipMethodBodyBlockScope = false;
|
|
19866
20257
|
this.conditionalTaints.clear();
|
|
19867
20258
|
this.conditionStack = [];
|
|
19868
20259
|
this.lineConditions.clear();
|
|
19869
20260
|
this.synchronizedLines.clear();
|
|
19870
20261
|
this.inSynchronizedBlock = false;
|
|
19871
20262
|
this.iteratorSources.clear();
|
|
20263
|
+
this.taintedFromIteratorNext.clear();
|
|
19872
20264
|
this.classFields.clear();
|
|
19873
20265
|
this.instanceFieldTaint.clear();
|
|
19874
20266
|
this.currentClassName = null;
|
|
@@ -19893,6 +20285,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19893
20285
|
const resultSymbols = new Map(this.symbols);
|
|
19894
20286
|
for (const name2 of this.tainted) {
|
|
19895
20287
|
if (name2.includes(":")) {
|
|
20288
|
+
if (/^[^:]+:b\d+:/.test(name2)) {
|
|
20289
|
+
continue;
|
|
20290
|
+
}
|
|
19896
20291
|
const unscoped = name2.substring(name2.indexOf(":") + 1);
|
|
19897
20292
|
resultTainted.add(unscoped);
|
|
19898
20293
|
}
|
|
@@ -19907,6 +20302,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19907
20302
|
for (const [name2, value] of this.symbols) {
|
|
19908
20303
|
if (this.goScopedNames.has(name2)) continue;
|
|
19909
20304
|
if (name2.includes(":")) {
|
|
20305
|
+
if (/^[^:]+:b\d+:/.test(name2)) {
|
|
20306
|
+
continue;
|
|
20307
|
+
}
|
|
19910
20308
|
const unscoped = name2.substring(name2.indexOf(":") + 1);
|
|
19911
20309
|
if (!resultSymbols.has(unscoped)) {
|
|
19912
20310
|
resultSymbols.set(unscoped, value);
|
|
@@ -19916,6 +20314,7 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19916
20314
|
return {
|
|
19917
20315
|
symbols: resultSymbols,
|
|
19918
20316
|
tainted: resultTainted,
|
|
20317
|
+
lineBlockScopes: new Map(this.lineBlockScopes),
|
|
19919
20318
|
unreachableLines: new Set(this.unreachableLines),
|
|
19920
20319
|
taintedCollections: new Map(this.taintedCollections),
|
|
19921
20320
|
taintedArrayElements: new Map(this.taintedArrayElements),
|
|
@@ -20459,6 +20858,18 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20459
20858
|
// ===========================================================================
|
|
20460
20859
|
// Taint Refinement
|
|
20461
20860
|
// ===========================================================================
|
|
20861
|
+
/** True when `node` is `iter.next()` (possibly under a cast). */
|
|
20862
|
+
isIteratorNextExpression(node) {
|
|
20863
|
+
const text = getNodeText2(node, this.source);
|
|
20864
|
+
if (/\.\s*next\s*\(/.test(text)) return true;
|
|
20865
|
+
let current = node;
|
|
20866
|
+
if (current.type === "cast_expression" || current.type === "parenthesized_expression") {
|
|
20867
|
+
current = current.childForFieldName("operand") ?? current.childForFieldName("expression");
|
|
20868
|
+
}
|
|
20869
|
+
if (!current || current.type !== "method_invocation") return false;
|
|
20870
|
+
const nameNode = current.childForFieldName("name");
|
|
20871
|
+
return nameNode ? getNodeText2(nameNode, this.source) === "next" : false;
|
|
20872
|
+
}
|
|
20462
20873
|
refineTaintFromConstants() {
|
|
20463
20874
|
let changed = true;
|
|
20464
20875
|
let iterations = 0;
|
|
@@ -20468,6 +20879,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20468
20879
|
iterations++;
|
|
20469
20880
|
const toRemove = [];
|
|
20470
20881
|
for (const varName of this.tainted) {
|
|
20882
|
+
if (this.taintedFromIteratorNext.has(varName)) {
|
|
20883
|
+
continue;
|
|
20884
|
+
}
|
|
20471
20885
|
const symbol = this.symbols.get(varName);
|
|
20472
20886
|
if (symbol && symbol.type !== "unknown") {
|
|
20473
20887
|
toRemove.push(varName);
|
|
@@ -20516,6 +20930,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20516
20930
|
if (this.unreachableLines.has(line)) {
|
|
20517
20931
|
return true;
|
|
20518
20932
|
}
|
|
20933
|
+
if (this.blockScopeStack.length > 0) {
|
|
20934
|
+
this.lineBlockScopes.set(line, this.blockScopeStack[this.blockScopeStack.length - 1]);
|
|
20935
|
+
}
|
|
20519
20936
|
if (this.conditionStack.length > 0 && !this.lineConditions.has(line)) {
|
|
20520
20937
|
this.lineConditions.set(line, this.conditionStack[this.conditionStack.length - 1]);
|
|
20521
20938
|
}
|
|
@@ -20541,12 +20958,24 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20541
20958
|
case "assignment_statement":
|
|
20542
20959
|
this.handleGoAssignment(node);
|
|
20543
20960
|
return false;
|
|
20961
|
+
case "var_declaration":
|
|
20962
|
+
this.handleGoVarDeclaration(node);
|
|
20963
|
+
return false;
|
|
20544
20964
|
case "update_expression":
|
|
20545
20965
|
this.handleUpdateExpression(node);
|
|
20546
20966
|
return false;
|
|
20547
20967
|
case "if_statement":
|
|
20548
20968
|
this.handleIfStatement(node);
|
|
20549
20969
|
return true;
|
|
20970
|
+
case "block": {
|
|
20971
|
+
if (this.skipMethodBodyBlockScope) {
|
|
20972
|
+
this.skipMethodBodyBlockScope = false;
|
|
20973
|
+
}
|
|
20974
|
+
for (const child of node.children) {
|
|
20975
|
+
this.visit(child);
|
|
20976
|
+
}
|
|
20977
|
+
return true;
|
|
20978
|
+
}
|
|
20550
20979
|
case "switch_expression":
|
|
20551
20980
|
case "switch_statement":
|
|
20552
20981
|
this.handleSwitch(node);
|
|
@@ -20632,7 +21061,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20632
21061
|
}
|
|
20633
21062
|
const body2 = node.childForFieldName("body");
|
|
20634
21063
|
if (body2) {
|
|
21064
|
+
this.skipMethodBodyBlockScope = true;
|
|
20635
21065
|
this.visit(body2);
|
|
21066
|
+
this.skipMethodBodyBlockScope = false;
|
|
20636
21067
|
}
|
|
20637
21068
|
this.currentMethod = prevMethod;
|
|
20638
21069
|
this.inConstructor = prevInConstructor;
|
|
@@ -20646,10 +21077,31 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20646
21077
|
if (varName.includes(".") || varName.includes(":")) {
|
|
20647
21078
|
return varName;
|
|
20648
21079
|
}
|
|
21080
|
+
const blockId = this.blockScopeStack.length > 0 ? this.blockScopeStack[this.blockScopeStack.length - 1] : void 0;
|
|
20649
21081
|
if (this.currentMethod) {
|
|
20650
|
-
return `${this.currentMethod}:${varName}`;
|
|
21082
|
+
return blockId !== void 0 ? `${this.currentMethod}:b${blockId}:${varName}` : `${this.currentMethod}:${varName}`;
|
|
20651
21083
|
}
|
|
20652
|
-
return varName;
|
|
21084
|
+
return blockId !== void 0 ? `b${blockId}:${varName}` : varName;
|
|
21085
|
+
}
|
|
21086
|
+
/** Innermost block → method → unscoped, for reads that may bind to an outer declaration. */
|
|
21087
|
+
getScopedNameCandidates(varName) {
|
|
21088
|
+
if (varName.includes(".") || varName.includes(":")) {
|
|
21089
|
+
return [varName];
|
|
21090
|
+
}
|
|
21091
|
+
const out2 = [];
|
|
21092
|
+
for (let i2 = this.blockScopeStack.length - 1; i2 >= 0; i2--) {
|
|
21093
|
+
const blockId = this.blockScopeStack[i2];
|
|
21094
|
+
if (this.currentMethod) {
|
|
21095
|
+
out2.push(`${this.currentMethod}:b${blockId}:${varName}`);
|
|
21096
|
+
} else {
|
|
21097
|
+
out2.push(`b${blockId}:${varName}`);
|
|
21098
|
+
}
|
|
21099
|
+
}
|
|
21100
|
+
if (this.currentMethod) {
|
|
21101
|
+
out2.push(`${this.currentMethod}:${varName}`);
|
|
21102
|
+
}
|
|
21103
|
+
out2.push(varName);
|
|
21104
|
+
return out2;
|
|
20653
21105
|
}
|
|
20654
21106
|
/**
|
|
20655
21107
|
* Look up a variable value, checking both scoped and unscoped names.
|
|
@@ -20657,11 +21109,12 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20657
21109
|
* either local (scoped) or global (unscoped, like class fields).
|
|
20658
21110
|
*/
|
|
20659
21111
|
lookupSymbol(varName) {
|
|
20660
|
-
if (
|
|
20661
|
-
const scopedName
|
|
20662
|
-
|
|
20663
|
-
|
|
20664
|
-
|
|
21112
|
+
if (!varName.includes(".") && !varName.includes(":")) {
|
|
21113
|
+
for (const scopedName of this.getScopedNameCandidates(varName)) {
|
|
21114
|
+
const scopedValue = this.symbols.get(scopedName);
|
|
21115
|
+
if (scopedValue) {
|
|
21116
|
+
return scopedValue;
|
|
21117
|
+
}
|
|
20665
21118
|
}
|
|
20666
21119
|
}
|
|
20667
21120
|
return this.symbols.get(varName);
|
|
@@ -20713,8 +21166,22 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20713
21166
|
this.trackIteratorsInNode(initNode);
|
|
20714
21167
|
}
|
|
20715
21168
|
}
|
|
20716
|
-
|
|
20717
|
-
|
|
21169
|
+
if (node.type === "while_statement" || node.type === "do_statement") {
|
|
21170
|
+
const body2 = node.childForFieldName("body");
|
|
21171
|
+
for (const child of node.children) {
|
|
21172
|
+
if (child !== body2) this.visit(child);
|
|
21173
|
+
}
|
|
21174
|
+
if (body2?.type === "block") {
|
|
21175
|
+
this.blockScopeStack.push(getNodeLine(body2));
|
|
21176
|
+
this.visit(body2);
|
|
21177
|
+
this.blockScopeStack.pop();
|
|
21178
|
+
} else if (body2) {
|
|
21179
|
+
this.visit(body2);
|
|
21180
|
+
}
|
|
21181
|
+
} else {
|
|
21182
|
+
for (const child of node.children) {
|
|
21183
|
+
this.visit(child);
|
|
21184
|
+
}
|
|
20718
21185
|
}
|
|
20719
21186
|
for (const varName of loopVarNames) {
|
|
20720
21187
|
this.symbols.set(varName, createUnknown(getNodeLine(node)));
|
|
@@ -20856,6 +21323,10 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20856
21323
|
this.sanitizedVars.delete(scopedName);
|
|
20857
21324
|
this.sanitizedVarUncovered.delete(scopedName);
|
|
20858
21325
|
this.definitionNodes.set(scopedName, valueNode);
|
|
21326
|
+
if (this.isIteratorNextExpression(valueNode)) {
|
|
21327
|
+
this.taintedFromIteratorNext.add(scopedName);
|
|
21328
|
+
this.taintedFromIteratorNext.add(varName);
|
|
21329
|
+
}
|
|
20859
21330
|
this.symbols.set(scopedName, createUnknown(line));
|
|
20860
21331
|
} else {
|
|
20861
21332
|
if (this.inConditionalBranch) {
|
|
@@ -20897,6 +21368,64 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20897
21368
|
* helper that always returns a constant — clears the variable. Anything else stops being tracked here, which is what Go
|
|
20898
21369
|
* assignments got before this handler existed: the DFG decides.
|
|
20899
21370
|
*/
|
|
21371
|
+
/** `var x = expr` / `var x T = expr` — same clean/taint rules as `:=` (#783). */
|
|
21372
|
+
handleGoVarDeclaration(node) {
|
|
21373
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
21374
|
+
const spec = node.child(i2);
|
|
21375
|
+
if (!spec || spec.type !== "var_spec") continue;
|
|
21376
|
+
const nameNode = spec.childForFieldName("name");
|
|
21377
|
+
let valueNode = spec.childForFieldName("value");
|
|
21378
|
+
if (!nameNode || !valueNode) continue;
|
|
21379
|
+
const names = [];
|
|
21380
|
+
if (nameNode.type === "identifier") {
|
|
21381
|
+
names.push(getNodeText2(nameNode, this.source));
|
|
21382
|
+
} else if (nameNode.type === "identifier_list") {
|
|
21383
|
+
for (const c of nameNode.children) {
|
|
21384
|
+
if (c.type === "identifier") names.push(getNodeText2(c, this.source));
|
|
21385
|
+
}
|
|
21386
|
+
}
|
|
21387
|
+
if (valueNode.type === "expression_list") {
|
|
21388
|
+
valueNode = valueNode.children.find((c) => c.type !== "," && c.type !== "comment") ?? valueNode;
|
|
21389
|
+
}
|
|
21390
|
+
if (names.length === 0) continue;
|
|
21391
|
+
const line = getNodeLine(node);
|
|
21392
|
+
for (const varName of names) {
|
|
21393
|
+
if (varName === "_") continue;
|
|
21394
|
+
const scopedName = this.getScopedName(varName);
|
|
21395
|
+
this.goScopedNames.add(scopedName);
|
|
21396
|
+
if (this.isTaintedExpression(valueNode)) {
|
|
21397
|
+
this.goUnknownVars.add(scopedName);
|
|
21398
|
+
this.tainted.add(scopedName);
|
|
21399
|
+
this.sanitizedVars.delete(scopedName);
|
|
21400
|
+
this.sanitizedVarUncovered.delete(scopedName);
|
|
21401
|
+
this.definitionNodes.set(scopedName, valueNode);
|
|
21402
|
+
this.symbols.set(scopedName, createUnknown(line));
|
|
21403
|
+
continue;
|
|
21404
|
+
}
|
|
21405
|
+
if (!this.isGoKnownCleanRhs(valueNode) || this.goUnknownVars.has(scopedName)) {
|
|
21406
|
+
this.goUnknownVars.add(scopedName);
|
|
21407
|
+
this.symbols.delete(scopedName);
|
|
21408
|
+
this.sanitizedVars.delete(scopedName);
|
|
21409
|
+
this.sanitizedVarUncovered.delete(scopedName);
|
|
21410
|
+
if (!this.inConditionalBranch) {
|
|
21411
|
+
this.tainted.delete(scopedName);
|
|
21412
|
+
this.definitionNodes.delete(scopedName);
|
|
21413
|
+
}
|
|
21414
|
+
continue;
|
|
21415
|
+
}
|
|
21416
|
+
if (this.inConditionalBranch) {
|
|
21417
|
+
this.goUnknownVars.add(scopedName);
|
|
21418
|
+
this.symbols.delete(scopedName);
|
|
21419
|
+
continue;
|
|
21420
|
+
}
|
|
21421
|
+
this.tainted.delete(scopedName);
|
|
21422
|
+
this.definitionNodes.delete(scopedName);
|
|
21423
|
+
this.symbols.set(scopedName, this.evaluateExpression(valueNode));
|
|
21424
|
+
this.sanitizedVars.delete(scopedName);
|
|
21425
|
+
this.sanitizedVarUncovered.delete(scopedName);
|
|
21426
|
+
}
|
|
21427
|
+
}
|
|
21428
|
+
}
|
|
20900
21429
|
handleGoAssignment(node) {
|
|
20901
21430
|
const left = node.childForFieldName("left");
|
|
20902
21431
|
const right = node.childForFieldName("right");
|
|
@@ -20983,6 +21512,12 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
20983
21512
|
this.sanitizedVars.delete(scopedName);
|
|
20984
21513
|
this.sanitizedVarUncovered.delete(scopedName);
|
|
20985
21514
|
this.definitionNodes.set(scopedName, right);
|
|
21515
|
+
if (this.isIteratorNextExpression(right)) {
|
|
21516
|
+
this.taintedFromIteratorNext.add(scopedName);
|
|
21517
|
+
if (!varName.includes(".")) {
|
|
21518
|
+
this.taintedFromIteratorNext.add(varName);
|
|
21519
|
+
}
|
|
21520
|
+
}
|
|
20986
21521
|
this.symbols.set(scopedName, createUnknown(line));
|
|
20987
21522
|
if (this.inConstructor && varName.startsWith("this.")) {
|
|
20988
21523
|
const fieldName = varName.substring(5);
|
|
@@ -21707,6 +22242,24 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
21707
22242
|
cache.set(rootId, false);
|
|
21708
22243
|
return false;
|
|
21709
22244
|
}
|
|
22245
|
+
/** Is an argument of `call` an inline sanitizer call over tainted input (itself possibly nested)? */
|
|
22246
|
+
argIsSanitizedTaint(call) {
|
|
22247
|
+
const argsNode = call.childForFieldName("arguments");
|
|
22248
|
+
if (!argsNode) return false;
|
|
22249
|
+
for (const arg of argsNode.children) {
|
|
22250
|
+
if (arg.type !== "method_invocation") continue;
|
|
22251
|
+
const name2 = arg.childForFieldName("name");
|
|
22252
|
+
if (!name2 || !SANITIZER_METHODS.has(getNodeText2(name2, this.source))) continue;
|
|
22253
|
+
const inner = arg.childForFieldName("arguments");
|
|
22254
|
+
if (!inner) continue;
|
|
22255
|
+
for (const a of inner.children) {
|
|
22256
|
+
if (a.type === "(" || a.type === ")" || a.type === ",") continue;
|
|
22257
|
+
if (this.isTaintedExpression(a)) return true;
|
|
22258
|
+
}
|
|
22259
|
+
if (this.argIsSanitizedTaint(arg)) return true;
|
|
22260
|
+
}
|
|
22261
|
+
return false;
|
|
22262
|
+
}
|
|
21710
22263
|
isTaintedExpressionStep(node) {
|
|
21711
22264
|
const text = getNodeText2(node, this.source);
|
|
21712
22265
|
if (node.type === "method_invocation") {
|
|
@@ -21714,6 +22267,9 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
21714
22267
|
const objectNode = node.childForFieldName("object");
|
|
21715
22268
|
if (nameNode) {
|
|
21716
22269
|
const methodName = getNodeText2(nameNode, this.source);
|
|
22270
|
+
if (ANTI_SANITIZER_METHODS.has(methodName) && this.argIsSanitizedTaint(node)) {
|
|
22271
|
+
return true;
|
|
22272
|
+
}
|
|
21717
22273
|
if (SANITIZER_METHODS.has(methodName) && !this.localNonSanitizing.has(methodName)) {
|
|
21718
22274
|
return false;
|
|
21719
22275
|
}
|
|
@@ -21973,10 +22529,17 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
21973
22529
|
}
|
|
21974
22530
|
}
|
|
21975
22531
|
if (node.type === "identifier") {
|
|
21976
|
-
|
|
21977
|
-
|
|
21978
|
-
|
|
21979
|
-
if (this.
|
|
22532
|
+
let isTainted = false;
|
|
22533
|
+
let boundName = null;
|
|
22534
|
+
for (const candidate of this.getScopedNameCandidates(text)) {
|
|
22535
|
+
if (this.tainted.has(candidate)) {
|
|
22536
|
+
isTainted = true;
|
|
22537
|
+
boundName = candidate;
|
|
22538
|
+
break;
|
|
22539
|
+
}
|
|
22540
|
+
}
|
|
22541
|
+
if (isTainted && boundName) {
|
|
22542
|
+
if (this.isExcludedByCondition(boundName) || this.isExcludedByCondition(text)) {
|
|
21980
22543
|
return false;
|
|
21981
22544
|
}
|
|
21982
22545
|
}
|