circle-ir 3.58.0 → 3.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +58 -17
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/html/html-merge.d.ts.map +1 -1
- package/dist/analysis/html/html-merge.js +10 -0
- package/dist/analysis/html/html-merge.js.map +1 -1
- package/dist/analysis/interprocedural.d.ts.map +1 -1
- package/dist/analysis/interprocedural.js +44 -11
- package/dist/analysis/interprocedural.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts +7 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +283 -15
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/missing-public-doc-pass.d.ts.map +1 -1
- package/dist/analysis/passes/missing-public-doc-pass.js +2 -1
- package/dist/analysis/passes/missing-public-doc-pass.js.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.js +4 -1
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.js +2 -1
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- package/dist/analysis/passes/weak-random-pass.d.ts.map +1 -1
- package/dist/analysis/passes/weak-random-pass.js +2 -1
- package/dist/analysis/passes/weak-random-pass.js.map +1 -1
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +29 -7
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analysis/taint-propagation.d.ts.map +1 -1
- package/dist/analysis/taint-propagation.js +20 -0
- package/dist/analysis/taint-propagation.js.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +19 -2
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +512 -51
- package/dist/core/circle-ir-core.cjs +243 -26
- package/dist/core/circle-ir-core.js +243 -26
- package/dist/core/extractors/calls.js +181 -1
- package/dist/core/extractors/calls.js.map +1 -1
- package/dist/core/extractors/cfg.js +1 -1
- package/dist/core/extractors/cfg.js.map +1 -1
- package/dist/core/extractors/dfg.js +29 -3
- package/dist/core/extractors/dfg.js.map +1 -1
- package/dist/core/extractors/imports.js +1 -1
- package/dist/core/extractors/imports.js.map +1 -1
- package/dist/core/extractors/runtime-registrations.js +1 -1
- package/dist/core/extractors/runtime-registrations.js.map +1 -1
- package/dist/core/extractors/types.js +1 -1
- package/dist/core/extractors/types.js.map +1 -1
- package/dist/core/parser.d.ts +1 -1
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/graph/scope-graph.d.ts.map +1 -1
- package/dist/graph/scope-graph.js +1 -0
- package/dist/graph/scope-graph.js.map +1 -1
- package/dist/languages/plugins/bash.d.ts.map +1 -1
- package/dist/languages/plugins/bash.js +17 -0
- package/dist/languages/plugins/bash.js.map +1 -1
- package/dist/languages/registry.d.ts.map +1 -1
- package/dist/languages/registry.js +6 -0
- package/dist/languages/registry.js.map +1 -1
- package/dist/languages/types.d.ts +1 -1
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/wasm/tree-sitter-tsx.wasm +0 -0
- package/package.json +2 -1
|
@@ -4387,7 +4387,7 @@ function detectLanguage(tree) {
|
|
|
4387
4387
|
}
|
|
4388
4388
|
function extractTypes(tree, cache, language) {
|
|
4389
4389
|
const effectiveLanguage = language ?? detectLanguage(tree);
|
|
4390
|
-
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
4390
|
+
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
|
|
4391
4391
|
const isPython = effectiveLanguage === "python";
|
|
4392
4392
|
const isRust = effectiveLanguage === "rust";
|
|
4393
4393
|
if (effectiveLanguage === "go") {
|
|
@@ -5773,7 +5773,7 @@ function detectLanguageFromTree(tree, cache) {
|
|
|
5773
5773
|
function extractCalls(tree, cache, language) {
|
|
5774
5774
|
const calls = [];
|
|
5775
5775
|
const detectedLanguage = language ?? detectLanguageFromTree(tree, cache);
|
|
5776
|
-
const isJavaScript = detectedLanguage === "javascript" || detectedLanguage === "typescript";
|
|
5776
|
+
const isJavaScript = detectedLanguage === "javascript" || detectedLanguage === "typescript" || detectedLanguage === "tsx";
|
|
5777
5777
|
const isPython = detectedLanguage === "python";
|
|
5778
5778
|
const isRust = detectedLanguage === "rust";
|
|
5779
5779
|
if (detectedLanguage === "go") {
|
|
@@ -5822,8 +5822,137 @@ function extractJavaScriptCalls(tree, cache) {
|
|
|
5822
5822
|
calls.push(callInfo);
|
|
5823
5823
|
}
|
|
5824
5824
|
}
|
|
5825
|
+
const jsxAttributes = getNodesFromCache(tree.rootNode, "jsx_attribute", cache);
|
|
5826
|
+
for (const attr of jsxAttributes) {
|
|
5827
|
+
const callInfo = extractJSXAttributeSink(attr);
|
|
5828
|
+
if (callInfo) {
|
|
5829
|
+
calls.push(callInfo);
|
|
5830
|
+
}
|
|
5831
|
+
}
|
|
5832
|
+
const assignments = getNodesFromCache(tree.rootNode, "assignment_expression", cache);
|
|
5833
|
+
for (const assign of assignments) {
|
|
5834
|
+
const callInfo = extractDomPropertyAssignmentSink(assign);
|
|
5835
|
+
if (callInfo) {
|
|
5836
|
+
calls.push(callInfo);
|
|
5837
|
+
}
|
|
5838
|
+
}
|
|
5825
5839
|
return calls;
|
|
5826
5840
|
}
|
|
5841
|
+
var DOM_XSS_ASSIGNMENT_PROPERTIES = /* @__PURE__ */ new Set([
|
|
5842
|
+
"innerHTML",
|
|
5843
|
+
"outerHTML"
|
|
5844
|
+
]);
|
|
5845
|
+
function extractDomPropertyAssignmentSink(node) {
|
|
5846
|
+
const leftNode = node.childForFieldName("left");
|
|
5847
|
+
const rightNode = node.childForFieldName("right");
|
|
5848
|
+
if (!leftNode || !rightNode) return null;
|
|
5849
|
+
if (leftNode.type !== "member_expression") return null;
|
|
5850
|
+
const propertyNode = leftNode.childForFieldName("property");
|
|
5851
|
+
const objectNode = leftNode.childForFieldName("object");
|
|
5852
|
+
if (!propertyNode) return null;
|
|
5853
|
+
const propertyName = getNodeText(propertyNode);
|
|
5854
|
+
if (!DOM_XSS_ASSIGNMENT_PROPERTIES.has(propertyName)) return null;
|
|
5855
|
+
const receiver = objectNode ? getNodeText(objectNode) : null;
|
|
5856
|
+
const expression = getNodeText(rightNode);
|
|
5857
|
+
const { variable, literal } = analyzeJSArgument(rightNode);
|
|
5858
|
+
const enclosingFunc = findJSEnclosingFunction(node);
|
|
5859
|
+
return {
|
|
5860
|
+
method_name: propertyName,
|
|
5861
|
+
receiver,
|
|
5862
|
+
arguments: [
|
|
5863
|
+
{
|
|
5864
|
+
position: 0,
|
|
5865
|
+
expression,
|
|
5866
|
+
variable,
|
|
5867
|
+
literal
|
|
5868
|
+
}
|
|
5869
|
+
],
|
|
5870
|
+
location: {
|
|
5871
|
+
line: node.startPosition.row + 1,
|
|
5872
|
+
column: node.startPosition.column
|
|
5873
|
+
},
|
|
5874
|
+
in_method: enclosingFunc,
|
|
5875
|
+
resolved: true,
|
|
5876
|
+
resolution: {
|
|
5877
|
+
status: "resolved",
|
|
5878
|
+
target: `DOM.${propertyName}`
|
|
5879
|
+
}
|
|
5880
|
+
};
|
|
5881
|
+
}
|
|
5882
|
+
function extractJSXAttributeSink(attr) {
|
|
5883
|
+
let nameNode = null;
|
|
5884
|
+
for (let i2 = 0; i2 < attr.childCount; i2++) {
|
|
5885
|
+
const child = attr.child(i2);
|
|
5886
|
+
if (child && child.type === "property_identifier") {
|
|
5887
|
+
nameNode = child;
|
|
5888
|
+
break;
|
|
5889
|
+
}
|
|
5890
|
+
}
|
|
5891
|
+
if (!nameNode) return null;
|
|
5892
|
+
const attrName = getNodeText(nameNode);
|
|
5893
|
+
if (attrName !== "dangerouslySetInnerHTML") return null;
|
|
5894
|
+
let valueExpr = null;
|
|
5895
|
+
for (let i2 = 0; i2 < attr.childCount; i2++) {
|
|
5896
|
+
const child = attr.child(i2);
|
|
5897
|
+
if (child && child.type === "jsx_expression") {
|
|
5898
|
+
valueExpr = child;
|
|
5899
|
+
break;
|
|
5900
|
+
}
|
|
5901
|
+
}
|
|
5902
|
+
if (!valueExpr) return null;
|
|
5903
|
+
let htmlValue = null;
|
|
5904
|
+
for (let i2 = 0; i2 < valueExpr.childCount; i2++) {
|
|
5905
|
+
const inner = valueExpr.child(i2);
|
|
5906
|
+
if (!inner || inner.type !== "object") continue;
|
|
5907
|
+
for (let j = 0; j < inner.childCount; j++) {
|
|
5908
|
+
const pair = inner.child(j);
|
|
5909
|
+
if (!pair || pair.type !== "pair") continue;
|
|
5910
|
+
const keyNode = pair.childForFieldName("key");
|
|
5911
|
+
if (!keyNode) continue;
|
|
5912
|
+
const keyText = getNodeText(keyNode).replace(/^["']|["']$/g, "");
|
|
5913
|
+
if (keyText === "__html") {
|
|
5914
|
+
htmlValue = pair.childForFieldName("value");
|
|
5915
|
+
break;
|
|
5916
|
+
}
|
|
5917
|
+
}
|
|
5918
|
+
if (htmlValue) break;
|
|
5919
|
+
}
|
|
5920
|
+
if (!htmlValue) {
|
|
5921
|
+
for (let i2 = 0; i2 < valueExpr.childCount; i2++) {
|
|
5922
|
+
const inner = valueExpr.child(i2);
|
|
5923
|
+
if (inner && inner.type !== "{" && inner.type !== "}") {
|
|
5924
|
+
htmlValue = inner;
|
|
5925
|
+
break;
|
|
5926
|
+
}
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5929
|
+
if (!htmlValue) return null;
|
|
5930
|
+
const expression = getNodeText(htmlValue);
|
|
5931
|
+
const { variable, literal } = analyzeJSArgument(htmlValue);
|
|
5932
|
+
const enclosingFunc = findJSEnclosingFunction(attr);
|
|
5933
|
+
return {
|
|
5934
|
+
method_name: "dangerouslySetInnerHTML",
|
|
5935
|
+
receiver: null,
|
|
5936
|
+
arguments: [
|
|
5937
|
+
{
|
|
5938
|
+
position: 0,
|
|
5939
|
+
expression,
|
|
5940
|
+
variable,
|
|
5941
|
+
literal
|
|
5942
|
+
}
|
|
5943
|
+
],
|
|
5944
|
+
location: {
|
|
5945
|
+
line: attr.startPosition.row + 1,
|
|
5946
|
+
column: attr.startPosition.column
|
|
5947
|
+
},
|
|
5948
|
+
in_method: enclosingFunc,
|
|
5949
|
+
resolved: true,
|
|
5950
|
+
resolution: {
|
|
5951
|
+
status: "resolved",
|
|
5952
|
+
target: "react.dangerouslySetInnerHTML"
|
|
5953
|
+
}
|
|
5954
|
+
};
|
|
5955
|
+
}
|
|
5827
5956
|
function buildJSResolutionContext(tree, cache) {
|
|
5828
5957
|
const context = {
|
|
5829
5958
|
functionNames: /* @__PURE__ */ new Set(),
|
|
@@ -7331,7 +7460,7 @@ function detectLanguage2(tree) {
|
|
|
7331
7460
|
}
|
|
7332
7461
|
function extractImports(tree, language) {
|
|
7333
7462
|
const effectiveLanguage = language ?? detectLanguage2(tree);
|
|
7334
|
-
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
7463
|
+
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
|
|
7335
7464
|
const isPython = effectiveLanguage === "python";
|
|
7336
7465
|
const isRust = effectiveLanguage === "rust";
|
|
7337
7466
|
if (effectiveLanguage === "go") {
|
|
@@ -8023,7 +8152,7 @@ function detectLanguage3(tree) {
|
|
|
8023
8152
|
}
|
|
8024
8153
|
function buildCFG(tree, language) {
|
|
8025
8154
|
const effectiveLanguage = language ?? detectLanguage3(tree);
|
|
8026
|
-
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
8155
|
+
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
|
|
8027
8156
|
const allBlocks = [];
|
|
8028
8157
|
const allEdges = [];
|
|
8029
8158
|
let blockIdCounter = 0;
|
|
@@ -8598,7 +8727,7 @@ function detectLanguage4(tree) {
|
|
|
8598
8727
|
}
|
|
8599
8728
|
function buildDFG(tree, cache, language) {
|
|
8600
8729
|
const effectiveLanguage = language ?? detectLanguage4(tree);
|
|
8601
|
-
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
8730
|
+
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
|
|
8602
8731
|
if (isJavaScript) {
|
|
8603
8732
|
return buildJavaScriptDFG(tree, cache);
|
|
8604
8733
|
}
|
|
@@ -9406,7 +9535,18 @@ function buildBashDFG(tree) {
|
|
|
9406
9535
|
if (varNameNode) {
|
|
9407
9536
|
const varName = getNodeText(varNameNode);
|
|
9408
9537
|
if (varName && !varName.startsWith("?") && !varName.startsWith("#")) {
|
|
9409
|
-
|
|
9538
|
+
let reachingDef = findReachingDef(varName, scopeStack);
|
|
9539
|
+
if (reachingDef === null && !positionalParams.includes(varName)) {
|
|
9540
|
+
const def = {
|
|
9541
|
+
id: defIdCounter++,
|
|
9542
|
+
variable: varName,
|
|
9543
|
+
line: 0,
|
|
9544
|
+
kind: "param"
|
|
9545
|
+
};
|
|
9546
|
+
defs.push(def);
|
|
9547
|
+
scopeStack[0].set(varName, def.id);
|
|
9548
|
+
reachingDef = def.id;
|
|
9549
|
+
}
|
|
9410
9550
|
uses.push({
|
|
9411
9551
|
id: useIdCounter++,
|
|
9412
9552
|
variable: varName,
|
|
@@ -9419,7 +9559,18 @@ function buildBashDFG(tree) {
|
|
|
9419
9559
|
const varNameNode = node.namedChildCount > 0 ? node.namedChild(0) : null;
|
|
9420
9560
|
if (varNameNode && varNameNode.type === "variable_name") {
|
|
9421
9561
|
const varName = getNodeText(varNameNode);
|
|
9422
|
-
|
|
9562
|
+
let reachingDef = findReachingDef(varName, scopeStack);
|
|
9563
|
+
if (reachingDef === null && !positionalParams.includes(varName)) {
|
|
9564
|
+
const def = {
|
|
9565
|
+
id: defIdCounter++,
|
|
9566
|
+
variable: varName,
|
|
9567
|
+
line: 0,
|
|
9568
|
+
kind: "param"
|
|
9569
|
+
};
|
|
9570
|
+
defs.push(def);
|
|
9571
|
+
scopeStack[0].set(varName, def.id);
|
|
9572
|
+
reachingDef = def.id;
|
|
9573
|
+
}
|
|
9423
9574
|
uses.push({
|
|
9424
9575
|
id: useIdCounter++,
|
|
9425
9576
|
variable: varName,
|
|
@@ -10168,8 +10319,12 @@ var DEFAULT_SOURCES = [
|
|
|
10168
10319
|
{ method: "get", class: "cookies", type: "http_cookie", severity: "high", return_tainted: true },
|
|
10169
10320
|
{ property: "json", object: "request", type: "http_body", severity: "high", property_tainted: true },
|
|
10170
10321
|
{ property: "data", object: "request", type: "http_body", severity: "high", property_tainted: true },
|
|
10322
|
+
{ property: "stream", object: "request", type: "http_body", severity: "high", property_tainted: true },
|
|
10171
10323
|
{ property: "path", object: "request", type: "http_path", severity: "medium", property_tainted: true },
|
|
10172
10324
|
{ property: "query_string", object: "request", type: "http_query", severity: "high", property_tainted: true },
|
|
10325
|
+
// Flask request.get_data() — raw request bytes (method form, parallel to request.data property)
|
|
10326
|
+
{ method: "get_data", class: "request", type: "http_body", severity: "high", return_tainted: true },
|
|
10327
|
+
{ method: "get_json", class: "request", type: "http_body", severity: "high", return_tainted: true },
|
|
10173
10328
|
// Django request object
|
|
10174
10329
|
{ method: "get", class: "GET", type: "http_param", severity: "high", return_tainted: true },
|
|
10175
10330
|
{ method: "get", class: "POST", type: "http_param", severity: "high", return_tainted: true },
|
|
@@ -10381,14 +10536,19 @@ var DEFAULT_SINKS = [
|
|
|
10381
10536
|
{ method: "setExecutable", class: "ExecTask", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10382
10537
|
{ method: "setCommand", class: "ExecTask", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10383
10538
|
{ method: "execute", class: "Java", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10384
|
-
// Shell/Bash utilities
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10389
|
-
{
|
|
10390
|
-
{ method: "
|
|
10391
|
-
{ method: "
|
|
10539
|
+
// Shell/Bash utilities — these are method-call sinks in host languages
|
|
10540
|
+
// (Java Runtime/ProcessBuilder, JS child_process spawn/exec, Python subprocess, etc.).
|
|
10541
|
+
// When the analyzed file IS a bash/shell script, the bash plugin's per-flag entries
|
|
10542
|
+
// (argPositions: [1] for `bash -c <cmd>`) MUST win. Restrict these generic entries
|
|
10543
|
+
// to non-shell languages so they don't collide on the dedup key
|
|
10544
|
+
// `${location}:${call.location.line}:${pattern.cwe}`.
|
|
10545
|
+
{ method: "bash", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10546
|
+
{ method: "shell", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10547
|
+
{ method: "sh", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10548
|
+
{ method: "spawn", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10549
|
+
{ method: "fork", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10550
|
+
{ method: "popen", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10551
|
+
{ method: "system", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10392
10552
|
// Apache Commons Exec
|
|
10393
10553
|
// Note: bare class 'Executor' removed (see comment above) — DefaultExecutor matched explicitly.
|
|
10394
10554
|
{ method: "setCommandline", class: "DefaultExecutor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
@@ -10478,6 +10638,12 @@ var DEFAULT_SINKS = [
|
|
|
10478
10638
|
{ method: "unzip", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
|
|
10479
10639
|
{ method: "extract", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
|
|
10480
10640
|
{ method: "extractAll", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
|
|
10641
|
+
// Python zipfile/tarfile use lowercase extractall (PEP 8 naming)
|
|
10642
|
+
{ method: "extractall", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
10643
|
+
// Python zipfile.ZipFile(path) — tainted archive path enables Zip-Slip via malicious archive
|
|
10644
|
+
{ method: "ZipFile", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
10645
|
+
// Flask send_from_directory: untrusted filename can escape directory via ../
|
|
10646
|
+
{ method: "send_from_directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [1], languages: ["python"] },
|
|
10481
10647
|
{ method: "unjar", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
|
|
10482
10648
|
// Additional file constructors — BufferedReader(Reader) is NOT a path traversal sink; it wraps a Reader, not a file path
|
|
10483
10649
|
{ method: "PrintWriter", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
@@ -11562,16 +11728,42 @@ var DEFAULT_SINKS = [
|
|
|
11562
11728
|
// value position so a tainted variable is detected.
|
|
11563
11729
|
{ method: "Set", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11564
11730
|
{ method: "Add", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11565
|
-
// Mass-assignment (CWE-915) — Sprint 6, #86.
|
|
11566
|
-
// JS Object.assign(target, ...sources)
|
|
11567
|
-
//
|
|
11568
|
-
//
|
|
11569
|
-
|
|
11570
|
-
//
|
|
11571
|
-
|
|
11572
|
-
|
|
11731
|
+
// Mass-assignment (CWE-915 / CWE-1321) — Sprint 6, #86; cognium-dev #68 Sprint 10.
|
|
11732
|
+
// JS Object.assign(target, ...sources), `_.merge`, `_.extend`, `$.extend`,
|
|
11733
|
+
// `Object.defineProperty` — when fed an attacker-controlled bag, they write
|
|
11734
|
+
// arbitrary keys onto the target (or, for `__proto__`/`constructor.prototype`,
|
|
11735
|
+
// pollute the prototype chain). The CWE is CWE-1321 (Prototype Pollution),
|
|
11736
|
+
// which subsumes mass assignment for JS sinks operating on plain Objects.
|
|
11737
|
+
// We keep the existing `mass_assignment` SinkType so consumers route the
|
|
11738
|
+
// findings the same way; only the CWE shifts to flag prototype-pollution.
|
|
11739
|
+
{ method: "assign", class: "Object", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11740
|
+
{ method: "defineProperty", class: "Object", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2], languages: ["javascript", "typescript"] },
|
|
11741
|
+
{ method: "defineProperties", class: "Object", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1], languages: ["javascript", "typescript"] },
|
|
11742
|
+
// Lodash bulk-merge helpers behave identically. `_.merge` and `lodash.merge`
|
|
11743
|
+
// are aliases — match both receivers.
|
|
11744
|
+
{ method: "merge", class: "_", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11745
|
+
{ method: "merge", class: "lodash", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11746
|
+
{ method: "extend", class: "_", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11747
|
+
{ method: "extend", class: "lodash", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11748
|
+
{ method: "defaultsDeep", class: "_", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11749
|
+
{ method: "defaultsDeep", class: "lodash", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11573
11750
|
// jQuery $.extend(target, source) (legacy).
|
|
11574
|
-
{ method: "extend", class: "$", type: "mass_assignment", cwe: "CWE-
|
|
11751
|
+
{ method: "extend", class: "$", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11752
|
+
{ method: "extend", class: "jQuery", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11753
|
+
// DOM-XSS via property assignment (CWE-79) — cognium-dev #68 Sprint 10.
|
|
11754
|
+
// `el.innerHTML = tainted` / `el.outerHTML = tainted`. The JS call extractor
|
|
11755
|
+
// emits a synthetic CallInfo with method=`innerHTML`/`outerHTML` for each
|
|
11756
|
+
// matching assignment_expression. These classless entries catch them.
|
|
11757
|
+
{ method: "innerHTML", type: "xss", cwe: "CWE-79", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11758
|
+
{ method: "outerHTML", type: "xss", cwe: "CWE-79", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11759
|
+
// node-serialize.unserialize (CWE-502) — cognium-dev #68 Sprint 10.
|
|
11760
|
+
// The node-serialize package evaluates `_$$ND_FUNC$$_` IIFE payloads on
|
|
11761
|
+
// decode, turning untrusted input into RCE. Match both receiver-bound
|
|
11762
|
+
// calls (`serialize.unserialize(x)`) and destructured imports
|
|
11763
|
+
// (`const { unserialize } = require('node-serialize')`).
|
|
11764
|
+
{ method: "unserialize", class: "serialize", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11765
|
+
{ method: "unserialize", class: "node-serialize", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11766
|
+
{ method: "unserialize", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] }
|
|
11575
11767
|
];
|
|
11576
11768
|
var DEFAULT_SANITIZERS = [
|
|
11577
11769
|
// SQL Injection - proper parameter binding sanitizes input
|
|
@@ -12178,7 +12370,12 @@ function matchesSourcePattern(call, pattern) {
|
|
|
12178
12370
|
if (call.receiver_type && call.receiver_type === pattern.class) {
|
|
12179
12371
|
} else if (call.receiver_type_fqn && call.receiver_type_fqn.endsWith("." + pattern.class)) {
|
|
12180
12372
|
} else if (!call.receiver) {
|
|
12181
|
-
|
|
12373
|
+
const target = call.resolution?.target;
|
|
12374
|
+
const expectedTail = `${pattern.class}.${pattern.method}`;
|
|
12375
|
+
if (target && (target === expectedTail || target.endsWith("." + expectedTail))) {
|
|
12376
|
+
} else {
|
|
12377
|
+
return false;
|
|
12378
|
+
}
|
|
12182
12379
|
} else if (!receiverMightBeClass(call.receiver, pattern.class)) {
|
|
12183
12380
|
return false;
|
|
12184
12381
|
}
|
|
@@ -12445,7 +12642,12 @@ function matchesSinkPattern(call, pattern, typeHierarchy, language) {
|
|
|
12445
12642
|
}
|
|
12446
12643
|
return false;
|
|
12447
12644
|
} else if (!call.receiver && !call.receiver_type) {
|
|
12448
|
-
|
|
12645
|
+
const target = call.resolution?.target;
|
|
12646
|
+
const expectedTail = `${pattern.class}.${pattern.method}`;
|
|
12647
|
+
if (target && (target === expectedTail || target.endsWith("." + expectedTail))) {
|
|
12648
|
+
} else {
|
|
12649
|
+
return false;
|
|
12650
|
+
}
|
|
12449
12651
|
}
|
|
12450
12652
|
}
|
|
12451
12653
|
if (!pattern.class && call.receiver) {
|
|
@@ -13287,6 +13489,21 @@ function findInitialTaint(sources, callsByLine, defsByLine) {
|
|
|
13287
13489
|
});
|
|
13288
13490
|
}
|
|
13289
13491
|
}
|
|
13492
|
+
if (source.variable) {
|
|
13493
|
+
const paramDefs = defsByLine.get(0) ?? [];
|
|
13494
|
+
for (const def of paramDefs) {
|
|
13495
|
+
if (def.kind === "param" && def.variable === source.variable) {
|
|
13496
|
+
tainted.push({
|
|
13497
|
+
variable: def.variable,
|
|
13498
|
+
defId: def.id,
|
|
13499
|
+
line: def.line,
|
|
13500
|
+
sourceType: source.type,
|
|
13501
|
+
sourceLine: source.line,
|
|
13502
|
+
confidence: source.confidence
|
|
13503
|
+
});
|
|
13504
|
+
}
|
|
13505
|
+
}
|
|
13506
|
+
}
|
|
13290
13507
|
}
|
|
13291
13508
|
return tainted;
|
|
13292
13509
|
}
|