circle-ir 3.21.0 → 3.22.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/README.md +5 -1
- package/configs/sinks/golang.json +144 -0
- package/configs/sources/golang.json +150 -0
- package/dist/analysis/config-loader.js +6 -6
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/taint-matcher.js +115 -22
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analyzer.js +9 -0
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +1564 -203
- package/dist/core/circle-ir-core.cjs +787 -20
- package/dist/core/circle-ir-core.js +787 -20
- package/dist/core/extractors/calls.js +134 -1
- package/dist/core/extractors/calls.js.map +1 -1
- package/dist/core/extractors/cfg.js +68 -0
- package/dist/core/extractors/cfg.js.map +1 -1
- package/dist/core/extractors/dfg.js +270 -0
- package/dist/core/extractors/dfg.js.map +1 -1
- package/dist/core/extractors/imports.js +78 -0
- package/dist/core/extractors/imports.js.map +1 -1
- package/dist/core/extractors/types.js +160 -0
- package/dist/core/extractors/types.js.map +1 -1
- package/dist/core/parser.d.ts +1 -1
- package/dist/languages/plugins/go.d.ts +61 -0
- package/dist/languages/plugins/go.js +615 -0
- package/dist/languages/plugins/go.js.map +1 -0
- package/dist/languages/plugins/index.d.ts +1 -0
- package/dist/languages/plugins/index.js +3 -0
- package/dist/languages/plugins/index.js.map +1 -1
- package/dist/languages/types.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/wasm/tree-sitter-go.wasm +0 -0
- package/docs/SPEC.md +1 -1
- package/package.json +3 -1
|
@@ -4373,6 +4373,9 @@ function extractTypes(tree, cache, language) {
|
|
|
4373
4373
|
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
4374
4374
|
const isPython = effectiveLanguage === "python";
|
|
4375
4375
|
const isRust = effectiveLanguage === "rust";
|
|
4376
|
+
if (effectiveLanguage === "go") {
|
|
4377
|
+
return extractGoTypes(tree, cache);
|
|
4378
|
+
}
|
|
4376
4379
|
if (isRust) {
|
|
4377
4380
|
return extractRustTypes(tree, cache);
|
|
4378
4381
|
}
|
|
@@ -5508,6 +5511,143 @@ function extractRustDerives(node) {
|
|
|
5508
5511
|
}
|
|
5509
5512
|
return derives;
|
|
5510
5513
|
}
|
|
5514
|
+
function extractGoTypes(tree, cache) {
|
|
5515
|
+
const types = [];
|
|
5516
|
+
const root = tree.rootNode;
|
|
5517
|
+
const typeDecls = getNodesFromCache(root, "type_declaration", cache);
|
|
5518
|
+
for (const decl of typeDecls) {
|
|
5519
|
+
for (let i2 = 0; i2 < decl.childCount; i2++) {
|
|
5520
|
+
const spec = decl.child(i2);
|
|
5521
|
+
if (!spec || spec.type !== "type_spec") continue;
|
|
5522
|
+
const nameNode = spec.childForFieldName("name");
|
|
5523
|
+
const typeNode = spec.childForFieldName("type");
|
|
5524
|
+
if (!nameNode || !typeNode) continue;
|
|
5525
|
+
const name2 = getNodeText(nameNode);
|
|
5526
|
+
const isInterface = typeNode.type === "interface_type";
|
|
5527
|
+
const isStruct = typeNode.type === "struct_type";
|
|
5528
|
+
if (!isStruct && !isInterface) continue;
|
|
5529
|
+
const fields = [];
|
|
5530
|
+
const methods = [];
|
|
5531
|
+
if (isStruct) {
|
|
5532
|
+
const fieldList = findChildByType(typeNode, "field_declaration_list");
|
|
5533
|
+
if (fieldList) {
|
|
5534
|
+
for (let j = 0; j < fieldList.childCount; j++) {
|
|
5535
|
+
const field = fieldList.child(j);
|
|
5536
|
+
if (!field || field.type !== "field_declaration") continue;
|
|
5537
|
+
const fieldName = field.childForFieldName("name");
|
|
5538
|
+
const fieldType = field.childForFieldName("type");
|
|
5539
|
+
if (fieldName) {
|
|
5540
|
+
fields.push({
|
|
5541
|
+
name: getNodeText(fieldName),
|
|
5542
|
+
type: fieldType ? getNodeText(fieldType) : null,
|
|
5543
|
+
modifiers: [],
|
|
5544
|
+
annotations: []
|
|
5545
|
+
});
|
|
5546
|
+
}
|
|
5547
|
+
}
|
|
5548
|
+
}
|
|
5549
|
+
}
|
|
5550
|
+
const methodDecls = getNodesFromCache(root, "method_declaration", cache);
|
|
5551
|
+
for (const md of methodDecls) {
|
|
5552
|
+
const receiver = md.childForFieldName("receiver");
|
|
5553
|
+
if (!receiver) continue;
|
|
5554
|
+
const receiverText = getNodeText(receiver);
|
|
5555
|
+
if (receiverText.includes(name2)) {
|
|
5556
|
+
const methodNameNode = md.childForFieldName("name");
|
|
5557
|
+
const params = md.childForFieldName("parameters");
|
|
5558
|
+
const result = md.childForFieldName("result");
|
|
5559
|
+
if (methodNameNode) {
|
|
5560
|
+
methods.push({
|
|
5561
|
+
name: getNodeText(methodNameNode),
|
|
5562
|
+
return_type: result ? getNodeText(result) : null,
|
|
5563
|
+
parameters: params ? extractGoParameters(params) : [],
|
|
5564
|
+
annotations: [],
|
|
5565
|
+
modifiers: [],
|
|
5566
|
+
start_line: md.startPosition.row + 1,
|
|
5567
|
+
end_line: md.endPosition.row + 1
|
|
5568
|
+
});
|
|
5569
|
+
}
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
let pkg = null;
|
|
5573
|
+
const pkgClause = findChildByType(root, "package_clause");
|
|
5574
|
+
if (pkgClause) {
|
|
5575
|
+
for (let j = 0; j < pkgClause.childCount; j++) {
|
|
5576
|
+
const child = pkgClause.child(j);
|
|
5577
|
+
if (child && child.type === "package_identifier") {
|
|
5578
|
+
pkg = getNodeText(child);
|
|
5579
|
+
break;
|
|
5580
|
+
}
|
|
5581
|
+
}
|
|
5582
|
+
}
|
|
5583
|
+
types.push({
|
|
5584
|
+
name: name2,
|
|
5585
|
+
kind: isInterface ? "interface" : "class",
|
|
5586
|
+
package: pkg,
|
|
5587
|
+
extends: null,
|
|
5588
|
+
implements: [],
|
|
5589
|
+
annotations: [],
|
|
5590
|
+
methods,
|
|
5591
|
+
fields,
|
|
5592
|
+
start_line: decl.startPosition.row + 1,
|
|
5593
|
+
end_line: decl.endPosition.row + 1
|
|
5594
|
+
});
|
|
5595
|
+
}
|
|
5596
|
+
}
|
|
5597
|
+
const functions = getNodesFromCache(root, "function_declaration", cache);
|
|
5598
|
+
if (functions.length > 0) {
|
|
5599
|
+
const moduleFunctions = [];
|
|
5600
|
+
for (const func2 of functions) {
|
|
5601
|
+
const nameNode = func2.childForFieldName("name");
|
|
5602
|
+
const params = func2.childForFieldName("parameters");
|
|
5603
|
+
const result = func2.childForFieldName("result");
|
|
5604
|
+
if (nameNode) {
|
|
5605
|
+
moduleFunctions.push({
|
|
5606
|
+
name: getNodeText(nameNode),
|
|
5607
|
+
return_type: result ? getNodeText(result) : null,
|
|
5608
|
+
parameters: params ? extractGoParameters(params) : [],
|
|
5609
|
+
annotations: [],
|
|
5610
|
+
modifiers: [],
|
|
5611
|
+
start_line: func2.startPosition.row + 1,
|
|
5612
|
+
end_line: func2.endPosition.row + 1
|
|
5613
|
+
});
|
|
5614
|
+
}
|
|
5615
|
+
}
|
|
5616
|
+
if (moduleFunctions.length > 0) {
|
|
5617
|
+
types.push({
|
|
5618
|
+
name: "<module>",
|
|
5619
|
+
kind: "class",
|
|
5620
|
+
package: null,
|
|
5621
|
+
extends: null,
|
|
5622
|
+
implements: [],
|
|
5623
|
+
annotations: [],
|
|
5624
|
+
methods: moduleFunctions,
|
|
5625
|
+
fields: [],
|
|
5626
|
+
start_line: 1,
|
|
5627
|
+
end_line: root.endPosition.row + 1
|
|
5628
|
+
});
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5631
|
+
return types;
|
|
5632
|
+
}
|
|
5633
|
+
function extractGoParameters(params) {
|
|
5634
|
+
const parameters = [];
|
|
5635
|
+
for (let i2 = 0; i2 < params.childCount; i2++) {
|
|
5636
|
+
const child = params.child(i2);
|
|
5637
|
+
if (!child || child.type !== "parameter_declaration") continue;
|
|
5638
|
+
const nameNode = child.childForFieldName("name");
|
|
5639
|
+
const typeNode = child.childForFieldName("type");
|
|
5640
|
+
if (nameNode) {
|
|
5641
|
+
parameters.push({
|
|
5642
|
+
name: getNodeText(nameNode),
|
|
5643
|
+
type: typeNode ? getNodeText(typeNode) : null,
|
|
5644
|
+
annotations: [],
|
|
5645
|
+
line: child.startPosition.row + 1
|
|
5646
|
+
});
|
|
5647
|
+
}
|
|
5648
|
+
}
|
|
5649
|
+
return parameters;
|
|
5650
|
+
}
|
|
5511
5651
|
function findChildByType(node, type) {
|
|
5512
5652
|
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
5513
5653
|
const child = node.child(i2);
|
|
@@ -5555,6 +5695,9 @@ function extractCalls(tree, cache, language) {
|
|
|
5555
5695
|
const isJavaScript = detectedLanguage === "javascript" || detectedLanguage === "typescript";
|
|
5556
5696
|
const isPython = detectedLanguage === "python";
|
|
5557
5697
|
const isRust = detectedLanguage === "rust";
|
|
5698
|
+
if (detectedLanguage === "go") {
|
|
5699
|
+
return extractGoCalls(tree, cache);
|
|
5700
|
+
}
|
|
5558
5701
|
if (detectedLanguage === "bash") {
|
|
5559
5702
|
return extractBashCalls(tree, cache);
|
|
5560
5703
|
}
|
|
@@ -6281,7 +6424,7 @@ function extractBashCommandInfo(node) {
|
|
|
6281
6424
|
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
6282
6425
|
const child = node.child(i2);
|
|
6283
6426
|
if (!child) continue;
|
|
6284
|
-
if (child === nameNode) continue;
|
|
6427
|
+
if (child === nameNode || child.id === nameNode.id) continue;
|
|
6285
6428
|
if (child.type.includes("redirect") || child.type === "heredoc_body" || child.type === "file_descriptor") {
|
|
6286
6429
|
continue;
|
|
6287
6430
|
}
|
|
@@ -6837,6 +6980,104 @@ function resolveRustCall(methodName, receiver, context) {
|
|
|
6837
6980
|
status: "external_method"
|
|
6838
6981
|
};
|
|
6839
6982
|
}
|
|
6983
|
+
function extractGoCalls(tree, cache) {
|
|
6984
|
+
const calls = [];
|
|
6985
|
+
const callExpressions = getNodesFromCache(tree.rootNode, "call_expression", cache);
|
|
6986
|
+
for (const call of callExpressions) {
|
|
6987
|
+
const callInfo = extractGoCallInfo(call);
|
|
6988
|
+
if (callInfo) {
|
|
6989
|
+
calls.push(callInfo);
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
return calls;
|
|
6993
|
+
}
|
|
6994
|
+
function extractGoCallInfo(node) {
|
|
6995
|
+
const funcNode = node.childForFieldName("function");
|
|
6996
|
+
if (!funcNode) return null;
|
|
6997
|
+
let methodName;
|
|
6998
|
+
let receiver = null;
|
|
6999
|
+
if (funcNode.type === "selector_expression") {
|
|
7000
|
+
const operand = funcNode.childForFieldName("operand");
|
|
7001
|
+
const field = funcNode.childForFieldName("field");
|
|
7002
|
+
receiver = operand ? getNodeText(operand) : null;
|
|
7003
|
+
methodName = field ? getNodeText(field) : getNodeText(funcNode);
|
|
7004
|
+
} else if (funcNode.type === "identifier") {
|
|
7005
|
+
methodName = getNodeText(funcNode);
|
|
7006
|
+
} else {
|
|
7007
|
+
methodName = getNodeText(funcNode);
|
|
7008
|
+
}
|
|
7009
|
+
const argsNode = node.childForFieldName("arguments");
|
|
7010
|
+
const args2 = argsNode ? extractGoArguments(argsNode) : [];
|
|
7011
|
+
const inMethod = findGoEnclosingFunction(node);
|
|
7012
|
+
return {
|
|
7013
|
+
method_name: methodName,
|
|
7014
|
+
receiver,
|
|
7015
|
+
arguments: args2,
|
|
7016
|
+
location: {
|
|
7017
|
+
line: node.startPosition.row + 1,
|
|
7018
|
+
column: node.startPosition.column
|
|
7019
|
+
},
|
|
7020
|
+
in_method: inMethod,
|
|
7021
|
+
resolved: false,
|
|
7022
|
+
resolution: { status: "external_method" }
|
|
7023
|
+
};
|
|
7024
|
+
}
|
|
7025
|
+
function extractGoArguments(argsNode) {
|
|
7026
|
+
const args2 = [];
|
|
7027
|
+
let position = 0;
|
|
7028
|
+
for (let i2 = 0; i2 < argsNode.childCount; i2++) {
|
|
7029
|
+
const child = argsNode.child(i2);
|
|
7030
|
+
if (!child) continue;
|
|
7031
|
+
if (child.type === "(" || child.type === ")" || child.type === ",") continue;
|
|
7032
|
+
const expression = getNodeText(child);
|
|
7033
|
+
const variable = child.type === "identifier" ? expression : null;
|
|
7034
|
+
const literal = isGoLiteral(child) ? extractGoLiteralValue(child) : null;
|
|
7035
|
+
args2.push({
|
|
7036
|
+
position: position++,
|
|
7037
|
+
expression,
|
|
7038
|
+
variable,
|
|
7039
|
+
literal
|
|
7040
|
+
});
|
|
7041
|
+
}
|
|
7042
|
+
return args2;
|
|
7043
|
+
}
|
|
7044
|
+
function isGoLiteral(node) {
|
|
7045
|
+
const literalTypes = /* @__PURE__ */ new Set([
|
|
7046
|
+
"interpreted_string_literal",
|
|
7047
|
+
"raw_string_literal",
|
|
7048
|
+
"int_literal",
|
|
7049
|
+
"float_literal",
|
|
7050
|
+
"true",
|
|
7051
|
+
"false",
|
|
7052
|
+
"nil"
|
|
7053
|
+
]);
|
|
7054
|
+
return literalTypes.has(node.type);
|
|
7055
|
+
}
|
|
7056
|
+
function extractGoLiteralValue(node) {
|
|
7057
|
+
const text = getNodeText(node);
|
|
7058
|
+
if (node.type === "interpreted_string_literal") {
|
|
7059
|
+
return text.slice(1, -1);
|
|
7060
|
+
}
|
|
7061
|
+
if (node.type === "raw_string_literal") {
|
|
7062
|
+
return text.slice(1, -1);
|
|
7063
|
+
}
|
|
7064
|
+
return text;
|
|
7065
|
+
}
|
|
7066
|
+
function findGoEnclosingFunction(node) {
|
|
7067
|
+
let current = node.parent;
|
|
7068
|
+
while (current) {
|
|
7069
|
+
if (current.type === "function_declaration") {
|
|
7070
|
+
const nameNode = current.childForFieldName("name");
|
|
7071
|
+
return nameNode ? getNodeText(nameNode) : null;
|
|
7072
|
+
}
|
|
7073
|
+
if (current.type === "method_declaration") {
|
|
7074
|
+
const nameNode = current.childForFieldName("name");
|
|
7075
|
+
return nameNode ? getNodeText(nameNode) : null;
|
|
7076
|
+
}
|
|
7077
|
+
current = current.parent;
|
|
7078
|
+
}
|
|
7079
|
+
return null;
|
|
7080
|
+
}
|
|
6840
7081
|
|
|
6841
7082
|
// src/core/extractors/imports.ts
|
|
6842
7083
|
function detectLanguage2(tree) {
|
|
@@ -6891,6 +7132,9 @@ function extractImports(tree, language) {
|
|
|
6891
7132
|
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
6892
7133
|
const isPython = effectiveLanguage === "python";
|
|
6893
7134
|
const isRust = effectiveLanguage === "rust";
|
|
7135
|
+
if (effectiveLanguage === "go") {
|
|
7136
|
+
return extractGoImports(tree);
|
|
7137
|
+
}
|
|
6894
7138
|
if (isRust) {
|
|
6895
7139
|
return extractRustImports(tree);
|
|
6896
7140
|
}
|
|
@@ -7438,6 +7682,61 @@ function extractRustScopedUseList(node, lineNumber) {
|
|
|
7438
7682
|
}
|
|
7439
7683
|
return imports;
|
|
7440
7684
|
}
|
|
7685
|
+
function extractGoImports(tree) {
|
|
7686
|
+
const imports = [];
|
|
7687
|
+
const importDecls = findNodes(tree.rootNode, "import_declaration");
|
|
7688
|
+
for (const decl of importDecls) {
|
|
7689
|
+
const singleSpec = findGoChildByType(decl, "import_spec");
|
|
7690
|
+
if (singleSpec) {
|
|
7691
|
+
const parsed = parseGoImportSpec(singleSpec);
|
|
7692
|
+
if (parsed) imports.push(parsed);
|
|
7693
|
+
continue;
|
|
7694
|
+
}
|
|
7695
|
+
const specList = findGoChildByType(decl, "import_spec_list");
|
|
7696
|
+
if (specList) {
|
|
7697
|
+
for (let i2 = 0; i2 < specList.childCount; i2++) {
|
|
7698
|
+
const spec = specList.child(i2);
|
|
7699
|
+
if (!spec || spec.type !== "import_spec") continue;
|
|
7700
|
+
const parsed = parseGoImportSpec(spec);
|
|
7701
|
+
if (parsed) imports.push(parsed);
|
|
7702
|
+
}
|
|
7703
|
+
}
|
|
7704
|
+
}
|
|
7705
|
+
return imports;
|
|
7706
|
+
}
|
|
7707
|
+
function parseGoImportSpec(spec) {
|
|
7708
|
+
let alias = null;
|
|
7709
|
+
let path;
|
|
7710
|
+
for (let i2 = 0; i2 < spec.childCount; i2++) {
|
|
7711
|
+
const child = spec.child(i2);
|
|
7712
|
+
if (!child) continue;
|
|
7713
|
+
if (child.type === "package_identifier" || child.type === "blank_identifier" || child.type === "dot") {
|
|
7714
|
+
alias = getNodeText(child);
|
|
7715
|
+
}
|
|
7716
|
+
if (child.type === "interpreted_string_literal") {
|
|
7717
|
+
const text = getNodeText(child);
|
|
7718
|
+
path = text.slice(1, -1);
|
|
7719
|
+
}
|
|
7720
|
+
}
|
|
7721
|
+
if (!path) return null;
|
|
7722
|
+
const shortName = alias || path.split("/").pop() || path;
|
|
7723
|
+
return {
|
|
7724
|
+
imported_name: shortName,
|
|
7725
|
+
from_package: path,
|
|
7726
|
+
alias,
|
|
7727
|
+
is_wildcard: alias === ".",
|
|
7728
|
+
line_number: spec.startPosition.row + 1
|
|
7729
|
+
};
|
|
7730
|
+
}
|
|
7731
|
+
function findGoChildByType(node, type) {
|
|
7732
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
7733
|
+
const child = node.child(i2);
|
|
7734
|
+
if (child?.type === type) {
|
|
7735
|
+
return child;
|
|
7736
|
+
}
|
|
7737
|
+
}
|
|
7738
|
+
return null;
|
|
7739
|
+
}
|
|
7441
7740
|
|
|
7442
7741
|
// src/core/extractors/exports.ts
|
|
7443
7742
|
function extractExports(types) {
|
|
@@ -7529,6 +7828,9 @@ function buildCFG(tree, language) {
|
|
|
7529
7828
|
if (effectiveLanguage === "bash") {
|
|
7530
7829
|
return buildBashCFG(tree, blockIdCounter);
|
|
7531
7830
|
}
|
|
7831
|
+
if (effectiveLanguage === "go") {
|
|
7832
|
+
return buildGoCFG(tree, blockIdCounter);
|
|
7833
|
+
}
|
|
7532
7834
|
if (isJavaScript) {
|
|
7533
7835
|
const functions = [
|
|
7534
7836
|
...findNodes(tree.rootNode, "function_declaration"),
|
|
@@ -8005,6 +8307,58 @@ function isStatement(node, isJavaScript) {
|
|
|
8005
8307
|
]);
|
|
8006
8308
|
return isJavaScript ? jsStatementTypes.has(node.type) : javaStatementTypes.has(node.type);
|
|
8007
8309
|
}
|
|
8310
|
+
function buildGoCFG(tree, blockIdCounter) {
|
|
8311
|
+
const allBlocks = [];
|
|
8312
|
+
const allEdges = [];
|
|
8313
|
+
const functions = [
|
|
8314
|
+
...findNodes(tree.rootNode, "function_declaration"),
|
|
8315
|
+
...findNodes(tree.rootNode, "method_declaration")
|
|
8316
|
+
];
|
|
8317
|
+
for (const func2 of functions) {
|
|
8318
|
+
const body2 = func2.childForFieldName("body");
|
|
8319
|
+
if (!body2 || body2.type !== "block") continue;
|
|
8320
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, false);
|
|
8321
|
+
allBlocks.push(...blocks);
|
|
8322
|
+
allEdges.push(...edges);
|
|
8323
|
+
blockIdCounter = nextId;
|
|
8324
|
+
}
|
|
8325
|
+
const hasTopLevelDecls = tree.rootNode.children.some(
|
|
8326
|
+
(c) => c !== null && isGoStatement(c)
|
|
8327
|
+
);
|
|
8328
|
+
if (hasTopLevelDecls) {
|
|
8329
|
+
const block = {
|
|
8330
|
+
id: blockIdCounter++,
|
|
8331
|
+
type: "normal",
|
|
8332
|
+
start_line: 1,
|
|
8333
|
+
end_line: tree.rootNode.endPosition.row + 1
|
|
8334
|
+
};
|
|
8335
|
+
allBlocks.push(block);
|
|
8336
|
+
}
|
|
8337
|
+
return { blocks: allBlocks, edges: allEdges };
|
|
8338
|
+
}
|
|
8339
|
+
function isGoStatement(node) {
|
|
8340
|
+
const goStatementTypes = /* @__PURE__ */ new Set([
|
|
8341
|
+
"short_var_declaration",
|
|
8342
|
+
"var_declaration",
|
|
8343
|
+
"assignment_statement",
|
|
8344
|
+
"expression_statement",
|
|
8345
|
+
"if_statement",
|
|
8346
|
+
"for_statement",
|
|
8347
|
+
"switch_statement",
|
|
8348
|
+
"type_switch_statement",
|
|
8349
|
+
"select_statement",
|
|
8350
|
+
"return_statement",
|
|
8351
|
+
"go_statement",
|
|
8352
|
+
"defer_statement",
|
|
8353
|
+
"send_statement",
|
|
8354
|
+
"inc_statement",
|
|
8355
|
+
"dec_statement",
|
|
8356
|
+
"block",
|
|
8357
|
+
"type_declaration",
|
|
8358
|
+
"const_declaration"
|
|
8359
|
+
]);
|
|
8360
|
+
return goStatementTypes.has(node.type);
|
|
8361
|
+
}
|
|
8008
8362
|
|
|
8009
8363
|
// src/core/extractors/dfg.ts
|
|
8010
8364
|
function detectLanguage4(tree) {
|
|
@@ -8052,6 +8406,9 @@ function buildDFG(tree, cache, language) {
|
|
|
8052
8406
|
if (effectiveLanguage === "bash") {
|
|
8053
8407
|
return buildBashDFG(tree);
|
|
8054
8408
|
}
|
|
8409
|
+
if (effectiveLanguage === "go") {
|
|
8410
|
+
return buildGoDFG(tree);
|
|
8411
|
+
}
|
|
8055
8412
|
return buildJavaDFG(tree, cache);
|
|
8056
8413
|
}
|
|
8057
8414
|
function buildJavaDFG(tree, cache) {
|
|
@@ -9035,6 +9392,259 @@ function isRustKeyword(name2) {
|
|
|
9035
9392
|
]);
|
|
9036
9393
|
return keywords.has(name2);
|
|
9037
9394
|
}
|
|
9395
|
+
var GO_KEYWORDS = /* @__PURE__ */ new Set([
|
|
9396
|
+
"break",
|
|
9397
|
+
"case",
|
|
9398
|
+
"chan",
|
|
9399
|
+
"const",
|
|
9400
|
+
"continue",
|
|
9401
|
+
"default",
|
|
9402
|
+
"defer",
|
|
9403
|
+
"else",
|
|
9404
|
+
"fallthrough",
|
|
9405
|
+
"for",
|
|
9406
|
+
"func",
|
|
9407
|
+
"go",
|
|
9408
|
+
"goto",
|
|
9409
|
+
"if",
|
|
9410
|
+
"import",
|
|
9411
|
+
"interface",
|
|
9412
|
+
"map",
|
|
9413
|
+
"package",
|
|
9414
|
+
"range",
|
|
9415
|
+
"return",
|
|
9416
|
+
"select",
|
|
9417
|
+
"struct",
|
|
9418
|
+
"switch",
|
|
9419
|
+
"type",
|
|
9420
|
+
"var",
|
|
9421
|
+
"true",
|
|
9422
|
+
"false",
|
|
9423
|
+
"nil",
|
|
9424
|
+
"iota",
|
|
9425
|
+
"append",
|
|
9426
|
+
"cap",
|
|
9427
|
+
"close",
|
|
9428
|
+
"complex",
|
|
9429
|
+
"copy",
|
|
9430
|
+
"delete",
|
|
9431
|
+
"imag",
|
|
9432
|
+
"len",
|
|
9433
|
+
"make",
|
|
9434
|
+
"new",
|
|
9435
|
+
"panic",
|
|
9436
|
+
"print",
|
|
9437
|
+
"println",
|
|
9438
|
+
"real",
|
|
9439
|
+
"recover",
|
|
9440
|
+
"string",
|
|
9441
|
+
"int",
|
|
9442
|
+
"int8",
|
|
9443
|
+
"int16",
|
|
9444
|
+
"int32",
|
|
9445
|
+
"int64",
|
|
9446
|
+
"uint",
|
|
9447
|
+
"uint8",
|
|
9448
|
+
"uint16",
|
|
9449
|
+
"uint32",
|
|
9450
|
+
"uint64",
|
|
9451
|
+
"float32",
|
|
9452
|
+
"float64",
|
|
9453
|
+
"complex64",
|
|
9454
|
+
"complex128",
|
|
9455
|
+
"byte",
|
|
9456
|
+
"rune",
|
|
9457
|
+
"bool",
|
|
9458
|
+
"error",
|
|
9459
|
+
"any"
|
|
9460
|
+
]);
|
|
9461
|
+
function buildGoDFG(tree) {
|
|
9462
|
+
const defs = [];
|
|
9463
|
+
const uses = [];
|
|
9464
|
+
let defIdCounter = 1;
|
|
9465
|
+
let useIdCounter = 1;
|
|
9466
|
+
const scopeStack = [/* @__PURE__ */ new Map()];
|
|
9467
|
+
const functions = [
|
|
9468
|
+
...findNodes(tree.rootNode, "function_declaration"),
|
|
9469
|
+
...findNodes(tree.rootNode, "method_declaration")
|
|
9470
|
+
];
|
|
9471
|
+
for (const func2 of functions) {
|
|
9472
|
+
scopeStack.push(/* @__PURE__ */ new Map());
|
|
9473
|
+
const params = func2.childForFieldName("parameters");
|
|
9474
|
+
if (params) {
|
|
9475
|
+
extractGoParamDefs(params, defs, defIdCounter, scopeStack);
|
|
9476
|
+
defIdCounter = defs.length + 1;
|
|
9477
|
+
}
|
|
9478
|
+
const receiver = func2.childForFieldName("receiver");
|
|
9479
|
+
if (receiver) {
|
|
9480
|
+
extractGoParamDefs(receiver, defs, defIdCounter, scopeStack);
|
|
9481
|
+
defIdCounter = defs.length + 1;
|
|
9482
|
+
}
|
|
9483
|
+
const body2 = func2.childForFieldName("body");
|
|
9484
|
+
if (body2) {
|
|
9485
|
+
processGoBlock(body2, defs, uses, scopeStack, { defId: defIdCounter, useId: useIdCounter });
|
|
9486
|
+
defIdCounter = defs.length + 1;
|
|
9487
|
+
useIdCounter = uses.length + 1;
|
|
9488
|
+
}
|
|
9489
|
+
scopeStack.pop();
|
|
9490
|
+
}
|
|
9491
|
+
for (let i2 = 0; i2 < tree.rootNode.childCount; i2++) {
|
|
9492
|
+
const child = tree.rootNode.child(i2);
|
|
9493
|
+
if (!child) continue;
|
|
9494
|
+
if (child.type === "var_declaration") {
|
|
9495
|
+
processGoVarDecl(child, defs, scopeStack, { defId: defIdCounter });
|
|
9496
|
+
defIdCounter = defs.length + 1;
|
|
9497
|
+
}
|
|
9498
|
+
}
|
|
9499
|
+
const chains = computeChains(defs, uses);
|
|
9500
|
+
return { defs, uses, chains };
|
|
9501
|
+
}
|
|
9502
|
+
function extractGoParamDefs(params, defs, _startId, scopeStack) {
|
|
9503
|
+
for (let i2 = 0; i2 < params.childCount; i2++) {
|
|
9504
|
+
const param = params.child(i2);
|
|
9505
|
+
if (!param || param.type !== "parameter_declaration") continue;
|
|
9506
|
+
const typeNode = param.childForFieldName("type");
|
|
9507
|
+
for (let j = 0; j < param.childCount; j++) {
|
|
9508
|
+
const nameNode = param.child(j);
|
|
9509
|
+
if (!nameNode || nameNode.type !== "identifier") continue;
|
|
9510
|
+
if (nameNode === typeNode) continue;
|
|
9511
|
+
const varName = getNodeText(nameNode);
|
|
9512
|
+
if (varName === "_") continue;
|
|
9513
|
+
const def = {
|
|
9514
|
+
id: defs.length + 1,
|
|
9515
|
+
variable: varName,
|
|
9516
|
+
kind: "param",
|
|
9517
|
+
line: param.startPosition.row + 1
|
|
9518
|
+
};
|
|
9519
|
+
defs.push(def);
|
|
9520
|
+
currentScope(scopeStack).set(varName, def.id);
|
|
9521
|
+
}
|
|
9522
|
+
}
|
|
9523
|
+
}
|
|
9524
|
+
function processGoBlock(node, defs, uses, scopeStack, counters) {
|
|
9525
|
+
walkTree(node, (child) => {
|
|
9526
|
+
if (child.type === "short_var_declaration") {
|
|
9527
|
+
const left = child.childForFieldName("left");
|
|
9528
|
+
const right = child.childForFieldName("right");
|
|
9529
|
+
if (right) {
|
|
9530
|
+
extractGoUses(right, uses, scopeStack);
|
|
9531
|
+
}
|
|
9532
|
+
if (left) {
|
|
9533
|
+
extractGoLhsDefs(left, defs, scopeStack, child.startPosition.row + 1);
|
|
9534
|
+
}
|
|
9535
|
+
} else if (child.type === "var_declaration") {
|
|
9536
|
+
processGoVarDecl(child, defs, scopeStack, counters);
|
|
9537
|
+
} else if (child.type === "assignment_statement") {
|
|
9538
|
+
const left = child.childForFieldName("left");
|
|
9539
|
+
const right = child.childForFieldName("right");
|
|
9540
|
+
if (right) {
|
|
9541
|
+
extractGoUses(right, uses, scopeStack);
|
|
9542
|
+
}
|
|
9543
|
+
if (left) {
|
|
9544
|
+
extractGoLhsDefs(left, defs, scopeStack, child.startPosition.row + 1);
|
|
9545
|
+
}
|
|
9546
|
+
} else if (child.type === "for_statement") {
|
|
9547
|
+
const rangeClause = findChildByTypeGo(child, "range_clause");
|
|
9548
|
+
if (rangeClause) {
|
|
9549
|
+
const left = rangeClause.childForFieldName("left");
|
|
9550
|
+
if (left) {
|
|
9551
|
+
extractGoLhsDefs(left, defs, scopeStack, child.startPosition.row + 1);
|
|
9552
|
+
}
|
|
9553
|
+
}
|
|
9554
|
+
} else if (child.type === "call_expression") {
|
|
9555
|
+
extractGoUses(child, uses, scopeStack);
|
|
9556
|
+
} else if (child.type === "return_statement") {
|
|
9557
|
+
for (let i2 = 0; i2 < child.childCount; i2++) {
|
|
9558
|
+
const expr = child.child(i2);
|
|
9559
|
+
if (expr && expr.type !== "return") {
|
|
9560
|
+
extractGoUses(expr, uses, scopeStack);
|
|
9561
|
+
}
|
|
9562
|
+
}
|
|
9563
|
+
}
|
|
9564
|
+
});
|
|
9565
|
+
}
|
|
9566
|
+
function processGoVarDecl(node, defs, scopeStack, _counters) {
|
|
9567
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
9568
|
+
const spec = node.child(i2);
|
|
9569
|
+
if (!spec || spec.type !== "var_spec") continue;
|
|
9570
|
+
for (let j = 0; j < spec.childCount; j++) {
|
|
9571
|
+
const nameNode = spec.child(j);
|
|
9572
|
+
if (!nameNode || nameNode.type !== "identifier") continue;
|
|
9573
|
+
const varName = getNodeText(nameNode);
|
|
9574
|
+
if (varName === "_") continue;
|
|
9575
|
+
const def = {
|
|
9576
|
+
id: defs.length + 1,
|
|
9577
|
+
variable: varName,
|
|
9578
|
+
kind: "local",
|
|
9579
|
+
line: spec.startPosition.row + 1
|
|
9580
|
+
};
|
|
9581
|
+
defs.push(def);
|
|
9582
|
+
currentScope(scopeStack).set(varName, def.id);
|
|
9583
|
+
}
|
|
9584
|
+
}
|
|
9585
|
+
}
|
|
9586
|
+
function extractGoLhsDefs(left, defs, scopeStack, line) {
|
|
9587
|
+
if (left.type === "identifier") {
|
|
9588
|
+
const varName = getNodeText(left);
|
|
9589
|
+
if (varName === "_") return;
|
|
9590
|
+
const def = {
|
|
9591
|
+
id: defs.length + 1,
|
|
9592
|
+
variable: varName,
|
|
9593
|
+
kind: "local",
|
|
9594
|
+
line
|
|
9595
|
+
};
|
|
9596
|
+
defs.push(def);
|
|
9597
|
+
currentScope(scopeStack).set(varName, def.id);
|
|
9598
|
+
} else if (left.type === "expression_list") {
|
|
9599
|
+
for (let i2 = 0; i2 < left.childCount; i2++) {
|
|
9600
|
+
const item = left.child(i2);
|
|
9601
|
+
if (item && item.type === "identifier") {
|
|
9602
|
+
const varName = getNodeText(item);
|
|
9603
|
+
if (varName === "_") continue;
|
|
9604
|
+
const def = {
|
|
9605
|
+
id: defs.length + 1,
|
|
9606
|
+
variable: varName,
|
|
9607
|
+
kind: "local",
|
|
9608
|
+
line
|
|
9609
|
+
};
|
|
9610
|
+
defs.push(def);
|
|
9611
|
+
currentScope(scopeStack).set(varName, def.id);
|
|
9612
|
+
}
|
|
9613
|
+
}
|
|
9614
|
+
}
|
|
9615
|
+
}
|
|
9616
|
+
function extractGoUses(node, uses, scopeStack) {
|
|
9617
|
+
walkTree(node, (child) => {
|
|
9618
|
+
if (child.type === "identifier") {
|
|
9619
|
+
const varName = getNodeText(child);
|
|
9620
|
+
if (varName === "_" || GO_KEYWORDS.has(varName)) return;
|
|
9621
|
+
const parent = child.parent;
|
|
9622
|
+
if (parent?.type === "selector_expression" && parent.childForFieldName("field") === child) {
|
|
9623
|
+
return;
|
|
9624
|
+
}
|
|
9625
|
+
if (parent?.type === "parameter_declaration" && parent.childForFieldName("type") === child) {
|
|
9626
|
+
return;
|
|
9627
|
+
}
|
|
9628
|
+
if (parent?.type === "type_identifier") {
|
|
9629
|
+
return;
|
|
9630
|
+
}
|
|
9631
|
+
const defId = findReachingDef(varName, scopeStack);
|
|
9632
|
+
uses.push({
|
|
9633
|
+
id: uses.length + 1,
|
|
9634
|
+
variable: varName,
|
|
9635
|
+
line: child.startPosition.row + 1,
|
|
9636
|
+
def_id: defId
|
|
9637
|
+
});
|
|
9638
|
+
}
|
|
9639
|
+
});
|
|
9640
|
+
}
|
|
9641
|
+
function findChildByTypeGo(node, type) {
|
|
9642
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
9643
|
+
const child = node.child(i2);
|
|
9644
|
+
if (child?.type === type) return child;
|
|
9645
|
+
}
|
|
9646
|
+
return null;
|
|
9647
|
+
}
|
|
9038
9648
|
|
|
9039
9649
|
// src/analysis/config-loader.ts
|
|
9040
9650
|
function parseConfig(content) {
|
|
@@ -9486,9 +10096,8 @@ var DEFAULT_SINKS = [
|
|
|
9486
10096
|
{ method: "FlowExecution", class: "constructor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
9487
10097
|
// ActiveMQ control commands
|
|
9488
10098
|
{ method: "processControlCommand", class: "TransportConnection", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
9489
|
-
// XStream deserialization (
|
|
9490
|
-
|
|
9491
|
-
{ method: "unmarshal", class: "XStream", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10099
|
+
// XStream deserialization — classified as CWE-502 (deserialization), not CWE-78 (command injection).
|
|
10100
|
+
// The deserialization sink entries at lines ~1059 handle this correctly.
|
|
9492
10101
|
{ method: "fromString", class: "FileConverter", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
9493
10102
|
// Plexus command line
|
|
9494
10103
|
{ method: "getPosition", class: "Commandline", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
@@ -10172,7 +10781,8 @@ var DEFAULT_SINKS = [
|
|
|
10172
10781
|
{ method: "query", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10173
10782
|
{ method: "query", class: "Pool", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10174
10783
|
{ method: "query", class: "Client", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10175
|
-
{ method:
|
|
10784
|
+
// Note: classless { method: 'query' } removed — too many FPs (UriComponentsBuilder.query(), etc.)
|
|
10785
|
+
// SQL query calls are covered by class-specific patterns above (Connection, Pool, Client, JdbcTemplate)
|
|
10176
10786
|
{ method: "raw", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0] },
|
|
10177
10787
|
// Browser DOM XSS sinks
|
|
10178
10788
|
{ method: "setAttribute", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
@@ -10369,8 +10979,8 @@ var DEFAULT_SINKS = [
|
|
|
10369
10979
|
{ method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10370
10980
|
{ method: "query_row", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10371
10981
|
{ method: "prepare", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10372
|
-
// sqlx::query macro
|
|
10373
|
-
{ method: "query", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10982
|
+
// sqlx::query macro — use class-specific pattern
|
|
10983
|
+
{ method: "query", class: "sqlx", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10374
10984
|
// rusqlite specific
|
|
10375
10985
|
{ method: "prepare", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10376
10986
|
{ method: "execute", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
@@ -10885,15 +11495,24 @@ function isInterproceduralTaintableType(typeName) {
|
|
|
10885
11495
|
}
|
|
10886
11496
|
function isParameterizedQueryCall(call, pattern) {
|
|
10887
11497
|
if (pattern.type !== "sql_injection") return false;
|
|
10888
|
-
|
|
10889
|
-
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
const
|
|
10893
|
-
if (
|
|
11498
|
+
const queryArg = call.arguments.find((a) => a.position === 0);
|
|
11499
|
+
if (queryArg) {
|
|
11500
|
+
const queryText = queryArg.literal ?? queryArg.expression ?? "";
|
|
11501
|
+
const hasPlaceholders = /(\?(?:\s|,|$|\))|\$\d+|:\w+|%s)/.test(queryText);
|
|
11502
|
+
const hasConcatenation = /\+\s*[^+]/.test(queryText) || queryText.includes("${");
|
|
11503
|
+
if (hasPlaceholders && !hasConcatenation && call.arguments.length >= 2) {
|
|
10894
11504
|
return true;
|
|
10895
11505
|
}
|
|
10896
11506
|
}
|
|
11507
|
+
if (call.arguments.length >= 2) {
|
|
11508
|
+
const secondArg = call.arguments.find((a) => a.position === 1);
|
|
11509
|
+
if (secondArg?.expression) {
|
|
11510
|
+
const expr = secondArg.expression.trim();
|
|
11511
|
+
if (expr.startsWith("[")) {
|
|
11512
|
+
return true;
|
|
11513
|
+
}
|
|
11514
|
+
}
|
|
11515
|
+
}
|
|
10897
11516
|
return false;
|
|
10898
11517
|
}
|
|
10899
11518
|
function findSinks(calls, patterns, typeHierarchy) {
|
|
@@ -11019,9 +11638,130 @@ var SAFE_RECEIVERS_BY_METHOD = {
|
|
|
11019
11638
|
"stmt",
|
|
11020
11639
|
"statement",
|
|
11021
11640
|
"cursor"
|
|
11641
|
+
]),
|
|
11642
|
+
// query() is only a SQL sink when receiver is a database handle — not URL builders,
|
|
11643
|
+
// DOM selectors, GraphQL clients, DNS resolvers, etc.
|
|
11644
|
+
query: /* @__PURE__ */ new Set([
|
|
11645
|
+
"uri",
|
|
11646
|
+
"url",
|
|
11647
|
+
"builder",
|
|
11648
|
+
"uribuilder",
|
|
11649
|
+
"uricomponents",
|
|
11650
|
+
"uricomponentsbuilder",
|
|
11651
|
+
"servleturicomponentsbuilder",
|
|
11652
|
+
"httpurl",
|
|
11653
|
+
"urlbuilder",
|
|
11654
|
+
"webclient",
|
|
11655
|
+
"request",
|
|
11656
|
+
"req",
|
|
11657
|
+
"router",
|
|
11658
|
+
"route",
|
|
11659
|
+
"app",
|
|
11660
|
+
"express",
|
|
11661
|
+
"parser",
|
|
11662
|
+
"selector",
|
|
11663
|
+
"jquery",
|
|
11664
|
+
"dom",
|
|
11665
|
+
"document",
|
|
11666
|
+
"element",
|
|
11667
|
+
"xmlpath",
|
|
11668
|
+
"xpath",
|
|
11669
|
+
"dns",
|
|
11670
|
+
"resolver",
|
|
11671
|
+
"graphql",
|
|
11672
|
+
"apollo",
|
|
11673
|
+
"querybuilder",
|
|
11674
|
+
"criteria"
|
|
11675
|
+
]),
|
|
11676
|
+
// authenticate() — safe on auth framework objects (token verification, not code exec)
|
|
11677
|
+
authenticate: /* @__PURE__ */ new Set([
|
|
11678
|
+
"auth",
|
|
11679
|
+
"authenticator",
|
|
11680
|
+
"authmanager",
|
|
11681
|
+
"authprovider",
|
|
11682
|
+
"authenticationmanager",
|
|
11683
|
+
"authservice",
|
|
11684
|
+
"oauth",
|
|
11685
|
+
"token",
|
|
11686
|
+
"jwt",
|
|
11687
|
+
"passport",
|
|
11688
|
+
"session",
|
|
11689
|
+
"security",
|
|
11690
|
+
"credentials",
|
|
11691
|
+
"identityprovider",
|
|
11692
|
+
"ldap",
|
|
11693
|
+
"saml",
|
|
11694
|
+
"oidc"
|
|
11695
|
+
]),
|
|
11696
|
+
// add() is extremely generic — safe on collections, UI containers, builders, etc.
|
|
11697
|
+
add: /* @__PURE__ */ new Set([
|
|
11698
|
+
"list",
|
|
11699
|
+
"set",
|
|
11700
|
+
"map",
|
|
11701
|
+
"collection",
|
|
11702
|
+
"array",
|
|
11703
|
+
"queue",
|
|
11704
|
+
"deque",
|
|
11705
|
+
"stack",
|
|
11706
|
+
"vector",
|
|
11707
|
+
"builder",
|
|
11708
|
+
"panel",
|
|
11709
|
+
"container",
|
|
11710
|
+
"group",
|
|
11711
|
+
"layout",
|
|
11712
|
+
"menu",
|
|
11713
|
+
"toolbar",
|
|
11714
|
+
"model",
|
|
11715
|
+
"registry",
|
|
11716
|
+
"context",
|
|
11717
|
+
"config",
|
|
11718
|
+
"options",
|
|
11719
|
+
"params",
|
|
11720
|
+
"headers",
|
|
11721
|
+
"attributes",
|
|
11722
|
+
"listeners",
|
|
11723
|
+
"handlers",
|
|
11724
|
+
"filters",
|
|
11725
|
+
"interceptors",
|
|
11726
|
+
"validators",
|
|
11727
|
+
"extensions",
|
|
11728
|
+
"plugins",
|
|
11729
|
+
"modules",
|
|
11730
|
+
"components",
|
|
11731
|
+
"children",
|
|
11732
|
+
"items",
|
|
11733
|
+
"elements",
|
|
11734
|
+
"entries",
|
|
11735
|
+
"rows",
|
|
11736
|
+
"columns",
|
|
11737
|
+
"fields",
|
|
11738
|
+
"properties",
|
|
11739
|
+
"descriptors",
|
|
11740
|
+
"nodes",
|
|
11741
|
+
"actions",
|
|
11742
|
+
"results",
|
|
11743
|
+
"errors",
|
|
11744
|
+
"warnings",
|
|
11745
|
+
"messages",
|
|
11746
|
+
"notifications",
|
|
11747
|
+
"events",
|
|
11748
|
+
"subscribers",
|
|
11749
|
+
"observers",
|
|
11750
|
+
"providers",
|
|
11751
|
+
"services",
|
|
11752
|
+
"beans",
|
|
11753
|
+
"tasks",
|
|
11754
|
+
"jobs",
|
|
11755
|
+
"workers",
|
|
11756
|
+
"threads",
|
|
11757
|
+
"schedulers"
|
|
11022
11758
|
])
|
|
11023
11759
|
};
|
|
11024
|
-
function isKnownSafeReceiverForMethod(receiver, method,
|
|
11760
|
+
function isKnownSafeReceiverForMethod(receiver, method, sinkType) {
|
|
11761
|
+
const lowerMethod = method.toLowerCase();
|
|
11762
|
+
if ((lowerMethod === "fromxml" || lowerMethod === "unmarshal") && sinkType === "command_injection") {
|
|
11763
|
+
return true;
|
|
11764
|
+
}
|
|
11025
11765
|
const safeReceivers = SAFE_RECEIVERS_BY_METHOD[method];
|
|
11026
11766
|
if (!safeReceivers) return false;
|
|
11027
11767
|
const lowerReceiver = receiver.toLowerCase();
|
|
@@ -11127,11 +11867,23 @@ function receiverMightBeClass(receiver, className) {
|
|
|
11127
11867
|
}
|
|
11128
11868
|
}
|
|
11129
11869
|
}
|
|
11130
|
-
if (lowerClass.includes(lowerReceiver)) {
|
|
11131
|
-
|
|
11870
|
+
if (lowerReceiver.length >= 3 && lowerClass.includes(lowerReceiver)) {
|
|
11871
|
+
if (lowerReceiver.length >= 5 || lowerReceiver.length / lowerClass.length >= 0.4) {
|
|
11872
|
+
return true;
|
|
11873
|
+
}
|
|
11132
11874
|
}
|
|
11133
|
-
if (
|
|
11134
|
-
|
|
11875
|
+
if (lowerReceiver.length >= 2) {
|
|
11876
|
+
if (lowerClass.startsWith(lowerReceiver) || lowerClass.endsWith(lowerReceiver)) {
|
|
11877
|
+
return true;
|
|
11878
|
+
}
|
|
11879
|
+
}
|
|
11880
|
+
if (lowerReceiver.length >= 3) {
|
|
11881
|
+
const words = className.replace(/([a-z])([A-Z])/g, "$1\0$2").toLowerCase().split("\0");
|
|
11882
|
+
for (const word of words) {
|
|
11883
|
+
if (word.startsWith(lowerReceiver) && lowerReceiver.length / word.length >= 0.4) {
|
|
11884
|
+
return true;
|
|
11885
|
+
}
|
|
11886
|
+
}
|
|
11135
11887
|
}
|
|
11136
11888
|
const commonMappings = {
|
|
11137
11889
|
// HTTP/Servlet
|
|
@@ -11152,8 +11904,10 @@ function receiverMightBeClass(receiver, className) {
|
|
|
11152
11904
|
// Process/Runtime
|
|
11153
11905
|
runtime: ["Runtime"],
|
|
11154
11906
|
pb: ["ProcessBuilder"],
|
|
11155
|
-
// Scripting
|
|
11907
|
+
// Scripting / Expression evaluation
|
|
11156
11908
|
engine: ["ScriptEngine"],
|
|
11909
|
+
ev: ["ExpressionEvaluator", "ScriptEvaluator", "ClassBodyEvaluator"],
|
|
11910
|
+
evaluator: ["ExpressionEvaluator", "ScriptEvaluator", "ClassBodyEvaluator"],
|
|
11157
11911
|
// LDAP
|
|
11158
11912
|
ctx: ["Context", "InitialContext", "DirContext", "InitialDirContext", "LdapContext"],
|
|
11159
11913
|
context: ["Context", "InitialContext", "DirContext", "InitialDirContext", "LdapContext"],
|
|
@@ -11243,12 +11997,25 @@ function receiverMightBeClass(receiver, className) {
|
|
|
11243
11997
|
knex: ["knex"],
|
|
11244
11998
|
prisma: ["prisma"],
|
|
11245
11999
|
axios: ["axios"],
|
|
11246
|
-
fetch: ["fetch"]
|
|
12000
|
+
fetch: ["fetch"],
|
|
12001
|
+
// Go idioms (single-letter receivers)
|
|
12002
|
+
r: ["Request"],
|
|
12003
|
+
w: ["ResponseWriter"]
|
|
11247
12004
|
};
|
|
11248
12005
|
const mappings = commonMappings[lowerReceiver];
|
|
11249
12006
|
if (mappings && Array.isArray(mappings) && mappings.includes(className)) {
|
|
11250
12007
|
return true;
|
|
11251
12008
|
}
|
|
12009
|
+
const strippedReceiver = lowerReceiver.replace(/\d+$/, "");
|
|
12010
|
+
if (strippedReceiver !== lowerReceiver && strippedReceiver.length >= 2) {
|
|
12011
|
+
const strippedMappings = commonMappings[strippedReceiver];
|
|
12012
|
+
if (strippedMappings && Array.isArray(strippedMappings) && strippedMappings.includes(className)) {
|
|
12013
|
+
return true;
|
|
12014
|
+
}
|
|
12015
|
+
if (lowerClass.startsWith(strippedReceiver) || strippedReceiver.startsWith(lowerClass)) {
|
|
12016
|
+
return true;
|
|
12017
|
+
}
|
|
12018
|
+
}
|
|
11252
12019
|
return false;
|
|
11253
12020
|
}
|
|
11254
12021
|
function formatCallLocation(call) {
|