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
|
@@ -4308,6 +4308,9 @@ function extractTypes(tree, cache, language) {
|
|
|
4308
4308
|
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
4309
4309
|
const isPython = effectiveLanguage === "python";
|
|
4310
4310
|
const isRust = effectiveLanguage === "rust";
|
|
4311
|
+
if (effectiveLanguage === "go") {
|
|
4312
|
+
return extractGoTypes(tree, cache);
|
|
4313
|
+
}
|
|
4311
4314
|
if (isRust) {
|
|
4312
4315
|
return extractRustTypes(tree, cache);
|
|
4313
4316
|
}
|
|
@@ -5443,6 +5446,143 @@ function extractRustDerives(node) {
|
|
|
5443
5446
|
}
|
|
5444
5447
|
return derives;
|
|
5445
5448
|
}
|
|
5449
|
+
function extractGoTypes(tree, cache) {
|
|
5450
|
+
const types = [];
|
|
5451
|
+
const root = tree.rootNode;
|
|
5452
|
+
const typeDecls = getNodesFromCache(root, "type_declaration", cache);
|
|
5453
|
+
for (const decl of typeDecls) {
|
|
5454
|
+
for (let i2 = 0; i2 < decl.childCount; i2++) {
|
|
5455
|
+
const spec = decl.child(i2);
|
|
5456
|
+
if (!spec || spec.type !== "type_spec") continue;
|
|
5457
|
+
const nameNode = spec.childForFieldName("name");
|
|
5458
|
+
const typeNode = spec.childForFieldName("type");
|
|
5459
|
+
if (!nameNode || !typeNode) continue;
|
|
5460
|
+
const name2 = getNodeText(nameNode);
|
|
5461
|
+
const isInterface = typeNode.type === "interface_type";
|
|
5462
|
+
const isStruct = typeNode.type === "struct_type";
|
|
5463
|
+
if (!isStruct && !isInterface) continue;
|
|
5464
|
+
const fields = [];
|
|
5465
|
+
const methods = [];
|
|
5466
|
+
if (isStruct) {
|
|
5467
|
+
const fieldList = findChildByType(typeNode, "field_declaration_list");
|
|
5468
|
+
if (fieldList) {
|
|
5469
|
+
for (let j = 0; j < fieldList.childCount; j++) {
|
|
5470
|
+
const field = fieldList.child(j);
|
|
5471
|
+
if (!field || field.type !== "field_declaration") continue;
|
|
5472
|
+
const fieldName = field.childForFieldName("name");
|
|
5473
|
+
const fieldType = field.childForFieldName("type");
|
|
5474
|
+
if (fieldName) {
|
|
5475
|
+
fields.push({
|
|
5476
|
+
name: getNodeText(fieldName),
|
|
5477
|
+
type: fieldType ? getNodeText(fieldType) : null,
|
|
5478
|
+
modifiers: [],
|
|
5479
|
+
annotations: []
|
|
5480
|
+
});
|
|
5481
|
+
}
|
|
5482
|
+
}
|
|
5483
|
+
}
|
|
5484
|
+
}
|
|
5485
|
+
const methodDecls = getNodesFromCache(root, "method_declaration", cache);
|
|
5486
|
+
for (const md of methodDecls) {
|
|
5487
|
+
const receiver = md.childForFieldName("receiver");
|
|
5488
|
+
if (!receiver) continue;
|
|
5489
|
+
const receiverText = getNodeText(receiver);
|
|
5490
|
+
if (receiverText.includes(name2)) {
|
|
5491
|
+
const methodNameNode = md.childForFieldName("name");
|
|
5492
|
+
const params = md.childForFieldName("parameters");
|
|
5493
|
+
const result = md.childForFieldName("result");
|
|
5494
|
+
if (methodNameNode) {
|
|
5495
|
+
methods.push({
|
|
5496
|
+
name: getNodeText(methodNameNode),
|
|
5497
|
+
return_type: result ? getNodeText(result) : null,
|
|
5498
|
+
parameters: params ? extractGoParameters(params) : [],
|
|
5499
|
+
annotations: [],
|
|
5500
|
+
modifiers: [],
|
|
5501
|
+
start_line: md.startPosition.row + 1,
|
|
5502
|
+
end_line: md.endPosition.row + 1
|
|
5503
|
+
});
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
}
|
|
5507
|
+
let pkg = null;
|
|
5508
|
+
const pkgClause = findChildByType(root, "package_clause");
|
|
5509
|
+
if (pkgClause) {
|
|
5510
|
+
for (let j = 0; j < pkgClause.childCount; j++) {
|
|
5511
|
+
const child = pkgClause.child(j);
|
|
5512
|
+
if (child && child.type === "package_identifier") {
|
|
5513
|
+
pkg = getNodeText(child);
|
|
5514
|
+
break;
|
|
5515
|
+
}
|
|
5516
|
+
}
|
|
5517
|
+
}
|
|
5518
|
+
types.push({
|
|
5519
|
+
name: name2,
|
|
5520
|
+
kind: isInterface ? "interface" : "class",
|
|
5521
|
+
package: pkg,
|
|
5522
|
+
extends: null,
|
|
5523
|
+
implements: [],
|
|
5524
|
+
annotations: [],
|
|
5525
|
+
methods,
|
|
5526
|
+
fields,
|
|
5527
|
+
start_line: decl.startPosition.row + 1,
|
|
5528
|
+
end_line: decl.endPosition.row + 1
|
|
5529
|
+
});
|
|
5530
|
+
}
|
|
5531
|
+
}
|
|
5532
|
+
const functions = getNodesFromCache(root, "function_declaration", cache);
|
|
5533
|
+
if (functions.length > 0) {
|
|
5534
|
+
const moduleFunctions = [];
|
|
5535
|
+
for (const func2 of functions) {
|
|
5536
|
+
const nameNode = func2.childForFieldName("name");
|
|
5537
|
+
const params = func2.childForFieldName("parameters");
|
|
5538
|
+
const result = func2.childForFieldName("result");
|
|
5539
|
+
if (nameNode) {
|
|
5540
|
+
moduleFunctions.push({
|
|
5541
|
+
name: getNodeText(nameNode),
|
|
5542
|
+
return_type: result ? getNodeText(result) : null,
|
|
5543
|
+
parameters: params ? extractGoParameters(params) : [],
|
|
5544
|
+
annotations: [],
|
|
5545
|
+
modifiers: [],
|
|
5546
|
+
start_line: func2.startPosition.row + 1,
|
|
5547
|
+
end_line: func2.endPosition.row + 1
|
|
5548
|
+
});
|
|
5549
|
+
}
|
|
5550
|
+
}
|
|
5551
|
+
if (moduleFunctions.length > 0) {
|
|
5552
|
+
types.push({
|
|
5553
|
+
name: "<module>",
|
|
5554
|
+
kind: "class",
|
|
5555
|
+
package: null,
|
|
5556
|
+
extends: null,
|
|
5557
|
+
implements: [],
|
|
5558
|
+
annotations: [],
|
|
5559
|
+
methods: moduleFunctions,
|
|
5560
|
+
fields: [],
|
|
5561
|
+
start_line: 1,
|
|
5562
|
+
end_line: root.endPosition.row + 1
|
|
5563
|
+
});
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
5566
|
+
return types;
|
|
5567
|
+
}
|
|
5568
|
+
function extractGoParameters(params) {
|
|
5569
|
+
const parameters = [];
|
|
5570
|
+
for (let i2 = 0; i2 < params.childCount; i2++) {
|
|
5571
|
+
const child = params.child(i2);
|
|
5572
|
+
if (!child || child.type !== "parameter_declaration") continue;
|
|
5573
|
+
const nameNode = child.childForFieldName("name");
|
|
5574
|
+
const typeNode = child.childForFieldName("type");
|
|
5575
|
+
if (nameNode) {
|
|
5576
|
+
parameters.push({
|
|
5577
|
+
name: getNodeText(nameNode),
|
|
5578
|
+
type: typeNode ? getNodeText(typeNode) : null,
|
|
5579
|
+
annotations: [],
|
|
5580
|
+
line: child.startPosition.row + 1
|
|
5581
|
+
});
|
|
5582
|
+
}
|
|
5583
|
+
}
|
|
5584
|
+
return parameters;
|
|
5585
|
+
}
|
|
5446
5586
|
function findChildByType(node, type) {
|
|
5447
5587
|
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
5448
5588
|
const child = node.child(i2);
|
|
@@ -5490,6 +5630,9 @@ function extractCalls(tree, cache, language) {
|
|
|
5490
5630
|
const isJavaScript = detectedLanguage === "javascript" || detectedLanguage === "typescript";
|
|
5491
5631
|
const isPython = detectedLanguage === "python";
|
|
5492
5632
|
const isRust = detectedLanguage === "rust";
|
|
5633
|
+
if (detectedLanguage === "go") {
|
|
5634
|
+
return extractGoCalls(tree, cache);
|
|
5635
|
+
}
|
|
5493
5636
|
if (detectedLanguage === "bash") {
|
|
5494
5637
|
return extractBashCalls(tree, cache);
|
|
5495
5638
|
}
|
|
@@ -6216,7 +6359,7 @@ function extractBashCommandInfo(node) {
|
|
|
6216
6359
|
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
6217
6360
|
const child = node.child(i2);
|
|
6218
6361
|
if (!child) continue;
|
|
6219
|
-
if (child === nameNode) continue;
|
|
6362
|
+
if (child === nameNode || child.id === nameNode.id) continue;
|
|
6220
6363
|
if (child.type.includes("redirect") || child.type === "heredoc_body" || child.type === "file_descriptor") {
|
|
6221
6364
|
continue;
|
|
6222
6365
|
}
|
|
@@ -6772,6 +6915,104 @@ function resolveRustCall(methodName, receiver, context) {
|
|
|
6772
6915
|
status: "external_method"
|
|
6773
6916
|
};
|
|
6774
6917
|
}
|
|
6918
|
+
function extractGoCalls(tree, cache) {
|
|
6919
|
+
const calls = [];
|
|
6920
|
+
const callExpressions = getNodesFromCache(tree.rootNode, "call_expression", cache);
|
|
6921
|
+
for (const call of callExpressions) {
|
|
6922
|
+
const callInfo = extractGoCallInfo(call);
|
|
6923
|
+
if (callInfo) {
|
|
6924
|
+
calls.push(callInfo);
|
|
6925
|
+
}
|
|
6926
|
+
}
|
|
6927
|
+
return calls;
|
|
6928
|
+
}
|
|
6929
|
+
function extractGoCallInfo(node) {
|
|
6930
|
+
const funcNode = node.childForFieldName("function");
|
|
6931
|
+
if (!funcNode) return null;
|
|
6932
|
+
let methodName;
|
|
6933
|
+
let receiver = null;
|
|
6934
|
+
if (funcNode.type === "selector_expression") {
|
|
6935
|
+
const operand = funcNode.childForFieldName("operand");
|
|
6936
|
+
const field = funcNode.childForFieldName("field");
|
|
6937
|
+
receiver = operand ? getNodeText(operand) : null;
|
|
6938
|
+
methodName = field ? getNodeText(field) : getNodeText(funcNode);
|
|
6939
|
+
} else if (funcNode.type === "identifier") {
|
|
6940
|
+
methodName = getNodeText(funcNode);
|
|
6941
|
+
} else {
|
|
6942
|
+
methodName = getNodeText(funcNode);
|
|
6943
|
+
}
|
|
6944
|
+
const argsNode = node.childForFieldName("arguments");
|
|
6945
|
+
const args2 = argsNode ? extractGoArguments(argsNode) : [];
|
|
6946
|
+
const inMethod = findGoEnclosingFunction(node);
|
|
6947
|
+
return {
|
|
6948
|
+
method_name: methodName,
|
|
6949
|
+
receiver,
|
|
6950
|
+
arguments: args2,
|
|
6951
|
+
location: {
|
|
6952
|
+
line: node.startPosition.row + 1,
|
|
6953
|
+
column: node.startPosition.column
|
|
6954
|
+
},
|
|
6955
|
+
in_method: inMethod,
|
|
6956
|
+
resolved: false,
|
|
6957
|
+
resolution: { status: "external_method" }
|
|
6958
|
+
};
|
|
6959
|
+
}
|
|
6960
|
+
function extractGoArguments(argsNode) {
|
|
6961
|
+
const args2 = [];
|
|
6962
|
+
let position = 0;
|
|
6963
|
+
for (let i2 = 0; i2 < argsNode.childCount; i2++) {
|
|
6964
|
+
const child = argsNode.child(i2);
|
|
6965
|
+
if (!child) continue;
|
|
6966
|
+
if (child.type === "(" || child.type === ")" || child.type === ",") continue;
|
|
6967
|
+
const expression = getNodeText(child);
|
|
6968
|
+
const variable = child.type === "identifier" ? expression : null;
|
|
6969
|
+
const literal = isGoLiteral(child) ? extractGoLiteralValue(child) : null;
|
|
6970
|
+
args2.push({
|
|
6971
|
+
position: position++,
|
|
6972
|
+
expression,
|
|
6973
|
+
variable,
|
|
6974
|
+
literal
|
|
6975
|
+
});
|
|
6976
|
+
}
|
|
6977
|
+
return args2;
|
|
6978
|
+
}
|
|
6979
|
+
function isGoLiteral(node) {
|
|
6980
|
+
const literalTypes = /* @__PURE__ */ new Set([
|
|
6981
|
+
"interpreted_string_literal",
|
|
6982
|
+
"raw_string_literal",
|
|
6983
|
+
"int_literal",
|
|
6984
|
+
"float_literal",
|
|
6985
|
+
"true",
|
|
6986
|
+
"false",
|
|
6987
|
+
"nil"
|
|
6988
|
+
]);
|
|
6989
|
+
return literalTypes.has(node.type);
|
|
6990
|
+
}
|
|
6991
|
+
function extractGoLiteralValue(node) {
|
|
6992
|
+
const text = getNodeText(node);
|
|
6993
|
+
if (node.type === "interpreted_string_literal") {
|
|
6994
|
+
return text.slice(1, -1);
|
|
6995
|
+
}
|
|
6996
|
+
if (node.type === "raw_string_literal") {
|
|
6997
|
+
return text.slice(1, -1);
|
|
6998
|
+
}
|
|
6999
|
+
return text;
|
|
7000
|
+
}
|
|
7001
|
+
function findGoEnclosingFunction(node) {
|
|
7002
|
+
let current = node.parent;
|
|
7003
|
+
while (current) {
|
|
7004
|
+
if (current.type === "function_declaration") {
|
|
7005
|
+
const nameNode = current.childForFieldName("name");
|
|
7006
|
+
return nameNode ? getNodeText(nameNode) : null;
|
|
7007
|
+
}
|
|
7008
|
+
if (current.type === "method_declaration") {
|
|
7009
|
+
const nameNode = current.childForFieldName("name");
|
|
7010
|
+
return nameNode ? getNodeText(nameNode) : null;
|
|
7011
|
+
}
|
|
7012
|
+
current = current.parent;
|
|
7013
|
+
}
|
|
7014
|
+
return null;
|
|
7015
|
+
}
|
|
6775
7016
|
|
|
6776
7017
|
// src/core/extractors/imports.ts
|
|
6777
7018
|
function detectLanguage2(tree) {
|
|
@@ -6826,6 +7067,9 @@ function extractImports(tree, language) {
|
|
|
6826
7067
|
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
6827
7068
|
const isPython = effectiveLanguage === "python";
|
|
6828
7069
|
const isRust = effectiveLanguage === "rust";
|
|
7070
|
+
if (effectiveLanguage === "go") {
|
|
7071
|
+
return extractGoImports(tree);
|
|
7072
|
+
}
|
|
6829
7073
|
if (isRust) {
|
|
6830
7074
|
return extractRustImports(tree);
|
|
6831
7075
|
}
|
|
@@ -7373,6 +7617,61 @@ function extractRustScopedUseList(node, lineNumber) {
|
|
|
7373
7617
|
}
|
|
7374
7618
|
return imports;
|
|
7375
7619
|
}
|
|
7620
|
+
function extractGoImports(tree) {
|
|
7621
|
+
const imports = [];
|
|
7622
|
+
const importDecls = findNodes(tree.rootNode, "import_declaration");
|
|
7623
|
+
for (const decl of importDecls) {
|
|
7624
|
+
const singleSpec = findGoChildByType(decl, "import_spec");
|
|
7625
|
+
if (singleSpec) {
|
|
7626
|
+
const parsed = parseGoImportSpec(singleSpec);
|
|
7627
|
+
if (parsed) imports.push(parsed);
|
|
7628
|
+
continue;
|
|
7629
|
+
}
|
|
7630
|
+
const specList = findGoChildByType(decl, "import_spec_list");
|
|
7631
|
+
if (specList) {
|
|
7632
|
+
for (let i2 = 0; i2 < specList.childCount; i2++) {
|
|
7633
|
+
const spec = specList.child(i2);
|
|
7634
|
+
if (!spec || spec.type !== "import_spec") continue;
|
|
7635
|
+
const parsed = parseGoImportSpec(spec);
|
|
7636
|
+
if (parsed) imports.push(parsed);
|
|
7637
|
+
}
|
|
7638
|
+
}
|
|
7639
|
+
}
|
|
7640
|
+
return imports;
|
|
7641
|
+
}
|
|
7642
|
+
function parseGoImportSpec(spec) {
|
|
7643
|
+
let alias = null;
|
|
7644
|
+
let path;
|
|
7645
|
+
for (let i2 = 0; i2 < spec.childCount; i2++) {
|
|
7646
|
+
const child = spec.child(i2);
|
|
7647
|
+
if (!child) continue;
|
|
7648
|
+
if (child.type === "package_identifier" || child.type === "blank_identifier" || child.type === "dot") {
|
|
7649
|
+
alias = getNodeText(child);
|
|
7650
|
+
}
|
|
7651
|
+
if (child.type === "interpreted_string_literal") {
|
|
7652
|
+
const text = getNodeText(child);
|
|
7653
|
+
path = text.slice(1, -1);
|
|
7654
|
+
}
|
|
7655
|
+
}
|
|
7656
|
+
if (!path) return null;
|
|
7657
|
+
const shortName = alias || path.split("/").pop() || path;
|
|
7658
|
+
return {
|
|
7659
|
+
imported_name: shortName,
|
|
7660
|
+
from_package: path,
|
|
7661
|
+
alias,
|
|
7662
|
+
is_wildcard: alias === ".",
|
|
7663
|
+
line_number: spec.startPosition.row + 1
|
|
7664
|
+
};
|
|
7665
|
+
}
|
|
7666
|
+
function findGoChildByType(node, type) {
|
|
7667
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
7668
|
+
const child = node.child(i2);
|
|
7669
|
+
if (child?.type === type) {
|
|
7670
|
+
return child;
|
|
7671
|
+
}
|
|
7672
|
+
}
|
|
7673
|
+
return null;
|
|
7674
|
+
}
|
|
7376
7675
|
|
|
7377
7676
|
// src/core/extractors/exports.ts
|
|
7378
7677
|
function extractExports(types) {
|
|
@@ -7464,6 +7763,9 @@ function buildCFG(tree, language) {
|
|
|
7464
7763
|
if (effectiveLanguage === "bash") {
|
|
7465
7764
|
return buildBashCFG(tree, blockIdCounter);
|
|
7466
7765
|
}
|
|
7766
|
+
if (effectiveLanguage === "go") {
|
|
7767
|
+
return buildGoCFG(tree, blockIdCounter);
|
|
7768
|
+
}
|
|
7467
7769
|
if (isJavaScript) {
|
|
7468
7770
|
const functions = [
|
|
7469
7771
|
...findNodes(tree.rootNode, "function_declaration"),
|
|
@@ -7940,6 +8242,58 @@ function isStatement(node, isJavaScript) {
|
|
|
7940
8242
|
]);
|
|
7941
8243
|
return isJavaScript ? jsStatementTypes.has(node.type) : javaStatementTypes.has(node.type);
|
|
7942
8244
|
}
|
|
8245
|
+
function buildGoCFG(tree, blockIdCounter) {
|
|
8246
|
+
const allBlocks = [];
|
|
8247
|
+
const allEdges = [];
|
|
8248
|
+
const functions = [
|
|
8249
|
+
...findNodes(tree.rootNode, "function_declaration"),
|
|
8250
|
+
...findNodes(tree.rootNode, "method_declaration")
|
|
8251
|
+
];
|
|
8252
|
+
for (const func2 of functions) {
|
|
8253
|
+
const body2 = func2.childForFieldName("body");
|
|
8254
|
+
if (!body2 || body2.type !== "block") continue;
|
|
8255
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, false);
|
|
8256
|
+
allBlocks.push(...blocks);
|
|
8257
|
+
allEdges.push(...edges);
|
|
8258
|
+
blockIdCounter = nextId;
|
|
8259
|
+
}
|
|
8260
|
+
const hasTopLevelDecls = tree.rootNode.children.some(
|
|
8261
|
+
(c) => c !== null && isGoStatement(c)
|
|
8262
|
+
);
|
|
8263
|
+
if (hasTopLevelDecls) {
|
|
8264
|
+
const block = {
|
|
8265
|
+
id: blockIdCounter++,
|
|
8266
|
+
type: "normal",
|
|
8267
|
+
start_line: 1,
|
|
8268
|
+
end_line: tree.rootNode.endPosition.row + 1
|
|
8269
|
+
};
|
|
8270
|
+
allBlocks.push(block);
|
|
8271
|
+
}
|
|
8272
|
+
return { blocks: allBlocks, edges: allEdges };
|
|
8273
|
+
}
|
|
8274
|
+
function isGoStatement(node) {
|
|
8275
|
+
const goStatementTypes = /* @__PURE__ */ new Set([
|
|
8276
|
+
"short_var_declaration",
|
|
8277
|
+
"var_declaration",
|
|
8278
|
+
"assignment_statement",
|
|
8279
|
+
"expression_statement",
|
|
8280
|
+
"if_statement",
|
|
8281
|
+
"for_statement",
|
|
8282
|
+
"switch_statement",
|
|
8283
|
+
"type_switch_statement",
|
|
8284
|
+
"select_statement",
|
|
8285
|
+
"return_statement",
|
|
8286
|
+
"go_statement",
|
|
8287
|
+
"defer_statement",
|
|
8288
|
+
"send_statement",
|
|
8289
|
+
"inc_statement",
|
|
8290
|
+
"dec_statement",
|
|
8291
|
+
"block",
|
|
8292
|
+
"type_declaration",
|
|
8293
|
+
"const_declaration"
|
|
8294
|
+
]);
|
|
8295
|
+
return goStatementTypes.has(node.type);
|
|
8296
|
+
}
|
|
7943
8297
|
|
|
7944
8298
|
// src/core/extractors/dfg.ts
|
|
7945
8299
|
function detectLanguage4(tree) {
|
|
@@ -7987,6 +8341,9 @@ function buildDFG(tree, cache, language) {
|
|
|
7987
8341
|
if (effectiveLanguage === "bash") {
|
|
7988
8342
|
return buildBashDFG(tree);
|
|
7989
8343
|
}
|
|
8344
|
+
if (effectiveLanguage === "go") {
|
|
8345
|
+
return buildGoDFG(tree);
|
|
8346
|
+
}
|
|
7990
8347
|
return buildJavaDFG(tree, cache);
|
|
7991
8348
|
}
|
|
7992
8349
|
function buildJavaDFG(tree, cache) {
|
|
@@ -8970,6 +9327,259 @@ function isRustKeyword(name2) {
|
|
|
8970
9327
|
]);
|
|
8971
9328
|
return keywords.has(name2);
|
|
8972
9329
|
}
|
|
9330
|
+
var GO_KEYWORDS = /* @__PURE__ */ new Set([
|
|
9331
|
+
"break",
|
|
9332
|
+
"case",
|
|
9333
|
+
"chan",
|
|
9334
|
+
"const",
|
|
9335
|
+
"continue",
|
|
9336
|
+
"default",
|
|
9337
|
+
"defer",
|
|
9338
|
+
"else",
|
|
9339
|
+
"fallthrough",
|
|
9340
|
+
"for",
|
|
9341
|
+
"func",
|
|
9342
|
+
"go",
|
|
9343
|
+
"goto",
|
|
9344
|
+
"if",
|
|
9345
|
+
"import",
|
|
9346
|
+
"interface",
|
|
9347
|
+
"map",
|
|
9348
|
+
"package",
|
|
9349
|
+
"range",
|
|
9350
|
+
"return",
|
|
9351
|
+
"select",
|
|
9352
|
+
"struct",
|
|
9353
|
+
"switch",
|
|
9354
|
+
"type",
|
|
9355
|
+
"var",
|
|
9356
|
+
"true",
|
|
9357
|
+
"false",
|
|
9358
|
+
"nil",
|
|
9359
|
+
"iota",
|
|
9360
|
+
"append",
|
|
9361
|
+
"cap",
|
|
9362
|
+
"close",
|
|
9363
|
+
"complex",
|
|
9364
|
+
"copy",
|
|
9365
|
+
"delete",
|
|
9366
|
+
"imag",
|
|
9367
|
+
"len",
|
|
9368
|
+
"make",
|
|
9369
|
+
"new",
|
|
9370
|
+
"panic",
|
|
9371
|
+
"print",
|
|
9372
|
+
"println",
|
|
9373
|
+
"real",
|
|
9374
|
+
"recover",
|
|
9375
|
+
"string",
|
|
9376
|
+
"int",
|
|
9377
|
+
"int8",
|
|
9378
|
+
"int16",
|
|
9379
|
+
"int32",
|
|
9380
|
+
"int64",
|
|
9381
|
+
"uint",
|
|
9382
|
+
"uint8",
|
|
9383
|
+
"uint16",
|
|
9384
|
+
"uint32",
|
|
9385
|
+
"uint64",
|
|
9386
|
+
"float32",
|
|
9387
|
+
"float64",
|
|
9388
|
+
"complex64",
|
|
9389
|
+
"complex128",
|
|
9390
|
+
"byte",
|
|
9391
|
+
"rune",
|
|
9392
|
+
"bool",
|
|
9393
|
+
"error",
|
|
9394
|
+
"any"
|
|
9395
|
+
]);
|
|
9396
|
+
function buildGoDFG(tree) {
|
|
9397
|
+
const defs = [];
|
|
9398
|
+
const uses = [];
|
|
9399
|
+
let defIdCounter = 1;
|
|
9400
|
+
let useIdCounter = 1;
|
|
9401
|
+
const scopeStack = [/* @__PURE__ */ new Map()];
|
|
9402
|
+
const functions = [
|
|
9403
|
+
...findNodes(tree.rootNode, "function_declaration"),
|
|
9404
|
+
...findNodes(tree.rootNode, "method_declaration")
|
|
9405
|
+
];
|
|
9406
|
+
for (const func2 of functions) {
|
|
9407
|
+
scopeStack.push(/* @__PURE__ */ new Map());
|
|
9408
|
+
const params = func2.childForFieldName("parameters");
|
|
9409
|
+
if (params) {
|
|
9410
|
+
extractGoParamDefs(params, defs, defIdCounter, scopeStack);
|
|
9411
|
+
defIdCounter = defs.length + 1;
|
|
9412
|
+
}
|
|
9413
|
+
const receiver = func2.childForFieldName("receiver");
|
|
9414
|
+
if (receiver) {
|
|
9415
|
+
extractGoParamDefs(receiver, defs, defIdCounter, scopeStack);
|
|
9416
|
+
defIdCounter = defs.length + 1;
|
|
9417
|
+
}
|
|
9418
|
+
const body2 = func2.childForFieldName("body");
|
|
9419
|
+
if (body2) {
|
|
9420
|
+
processGoBlock(body2, defs, uses, scopeStack, { defId: defIdCounter, useId: useIdCounter });
|
|
9421
|
+
defIdCounter = defs.length + 1;
|
|
9422
|
+
useIdCounter = uses.length + 1;
|
|
9423
|
+
}
|
|
9424
|
+
scopeStack.pop();
|
|
9425
|
+
}
|
|
9426
|
+
for (let i2 = 0; i2 < tree.rootNode.childCount; i2++) {
|
|
9427
|
+
const child = tree.rootNode.child(i2);
|
|
9428
|
+
if (!child) continue;
|
|
9429
|
+
if (child.type === "var_declaration") {
|
|
9430
|
+
processGoVarDecl(child, defs, scopeStack, { defId: defIdCounter });
|
|
9431
|
+
defIdCounter = defs.length + 1;
|
|
9432
|
+
}
|
|
9433
|
+
}
|
|
9434
|
+
const chains = computeChains(defs, uses);
|
|
9435
|
+
return { defs, uses, chains };
|
|
9436
|
+
}
|
|
9437
|
+
function extractGoParamDefs(params, defs, _startId, scopeStack) {
|
|
9438
|
+
for (let i2 = 0; i2 < params.childCount; i2++) {
|
|
9439
|
+
const param = params.child(i2);
|
|
9440
|
+
if (!param || param.type !== "parameter_declaration") continue;
|
|
9441
|
+
const typeNode = param.childForFieldName("type");
|
|
9442
|
+
for (let j = 0; j < param.childCount; j++) {
|
|
9443
|
+
const nameNode = param.child(j);
|
|
9444
|
+
if (!nameNode || nameNode.type !== "identifier") continue;
|
|
9445
|
+
if (nameNode === typeNode) continue;
|
|
9446
|
+
const varName = getNodeText(nameNode);
|
|
9447
|
+
if (varName === "_") continue;
|
|
9448
|
+
const def = {
|
|
9449
|
+
id: defs.length + 1,
|
|
9450
|
+
variable: varName,
|
|
9451
|
+
kind: "param",
|
|
9452
|
+
line: param.startPosition.row + 1
|
|
9453
|
+
};
|
|
9454
|
+
defs.push(def);
|
|
9455
|
+
currentScope(scopeStack).set(varName, def.id);
|
|
9456
|
+
}
|
|
9457
|
+
}
|
|
9458
|
+
}
|
|
9459
|
+
function processGoBlock(node, defs, uses, scopeStack, counters) {
|
|
9460
|
+
walkTree(node, (child) => {
|
|
9461
|
+
if (child.type === "short_var_declaration") {
|
|
9462
|
+
const left = child.childForFieldName("left");
|
|
9463
|
+
const right = child.childForFieldName("right");
|
|
9464
|
+
if (right) {
|
|
9465
|
+
extractGoUses(right, uses, scopeStack);
|
|
9466
|
+
}
|
|
9467
|
+
if (left) {
|
|
9468
|
+
extractGoLhsDefs(left, defs, scopeStack, child.startPosition.row + 1);
|
|
9469
|
+
}
|
|
9470
|
+
} else if (child.type === "var_declaration") {
|
|
9471
|
+
processGoVarDecl(child, defs, scopeStack, counters);
|
|
9472
|
+
} else if (child.type === "assignment_statement") {
|
|
9473
|
+
const left = child.childForFieldName("left");
|
|
9474
|
+
const right = child.childForFieldName("right");
|
|
9475
|
+
if (right) {
|
|
9476
|
+
extractGoUses(right, uses, scopeStack);
|
|
9477
|
+
}
|
|
9478
|
+
if (left) {
|
|
9479
|
+
extractGoLhsDefs(left, defs, scopeStack, child.startPosition.row + 1);
|
|
9480
|
+
}
|
|
9481
|
+
} else if (child.type === "for_statement") {
|
|
9482
|
+
const rangeClause = findChildByTypeGo(child, "range_clause");
|
|
9483
|
+
if (rangeClause) {
|
|
9484
|
+
const left = rangeClause.childForFieldName("left");
|
|
9485
|
+
if (left) {
|
|
9486
|
+
extractGoLhsDefs(left, defs, scopeStack, child.startPosition.row + 1);
|
|
9487
|
+
}
|
|
9488
|
+
}
|
|
9489
|
+
} else if (child.type === "call_expression") {
|
|
9490
|
+
extractGoUses(child, uses, scopeStack);
|
|
9491
|
+
} else if (child.type === "return_statement") {
|
|
9492
|
+
for (let i2 = 0; i2 < child.childCount; i2++) {
|
|
9493
|
+
const expr = child.child(i2);
|
|
9494
|
+
if (expr && expr.type !== "return") {
|
|
9495
|
+
extractGoUses(expr, uses, scopeStack);
|
|
9496
|
+
}
|
|
9497
|
+
}
|
|
9498
|
+
}
|
|
9499
|
+
});
|
|
9500
|
+
}
|
|
9501
|
+
function processGoVarDecl(node, defs, scopeStack, _counters) {
|
|
9502
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
9503
|
+
const spec = node.child(i2);
|
|
9504
|
+
if (!spec || spec.type !== "var_spec") continue;
|
|
9505
|
+
for (let j = 0; j < spec.childCount; j++) {
|
|
9506
|
+
const nameNode = spec.child(j);
|
|
9507
|
+
if (!nameNode || nameNode.type !== "identifier") continue;
|
|
9508
|
+
const varName = getNodeText(nameNode);
|
|
9509
|
+
if (varName === "_") continue;
|
|
9510
|
+
const def = {
|
|
9511
|
+
id: defs.length + 1,
|
|
9512
|
+
variable: varName,
|
|
9513
|
+
kind: "local",
|
|
9514
|
+
line: spec.startPosition.row + 1
|
|
9515
|
+
};
|
|
9516
|
+
defs.push(def);
|
|
9517
|
+
currentScope(scopeStack).set(varName, def.id);
|
|
9518
|
+
}
|
|
9519
|
+
}
|
|
9520
|
+
}
|
|
9521
|
+
function extractGoLhsDefs(left, defs, scopeStack, line) {
|
|
9522
|
+
if (left.type === "identifier") {
|
|
9523
|
+
const varName = getNodeText(left);
|
|
9524
|
+
if (varName === "_") return;
|
|
9525
|
+
const def = {
|
|
9526
|
+
id: defs.length + 1,
|
|
9527
|
+
variable: varName,
|
|
9528
|
+
kind: "local",
|
|
9529
|
+
line
|
|
9530
|
+
};
|
|
9531
|
+
defs.push(def);
|
|
9532
|
+
currentScope(scopeStack).set(varName, def.id);
|
|
9533
|
+
} else if (left.type === "expression_list") {
|
|
9534
|
+
for (let i2 = 0; i2 < left.childCount; i2++) {
|
|
9535
|
+
const item = left.child(i2);
|
|
9536
|
+
if (item && item.type === "identifier") {
|
|
9537
|
+
const varName = getNodeText(item);
|
|
9538
|
+
if (varName === "_") continue;
|
|
9539
|
+
const def = {
|
|
9540
|
+
id: defs.length + 1,
|
|
9541
|
+
variable: varName,
|
|
9542
|
+
kind: "local",
|
|
9543
|
+
line
|
|
9544
|
+
};
|
|
9545
|
+
defs.push(def);
|
|
9546
|
+
currentScope(scopeStack).set(varName, def.id);
|
|
9547
|
+
}
|
|
9548
|
+
}
|
|
9549
|
+
}
|
|
9550
|
+
}
|
|
9551
|
+
function extractGoUses(node, uses, scopeStack) {
|
|
9552
|
+
walkTree(node, (child) => {
|
|
9553
|
+
if (child.type === "identifier") {
|
|
9554
|
+
const varName = getNodeText(child);
|
|
9555
|
+
if (varName === "_" || GO_KEYWORDS.has(varName)) return;
|
|
9556
|
+
const parent = child.parent;
|
|
9557
|
+
if (parent?.type === "selector_expression" && parent.childForFieldName("field") === child) {
|
|
9558
|
+
return;
|
|
9559
|
+
}
|
|
9560
|
+
if (parent?.type === "parameter_declaration" && parent.childForFieldName("type") === child) {
|
|
9561
|
+
return;
|
|
9562
|
+
}
|
|
9563
|
+
if (parent?.type === "type_identifier") {
|
|
9564
|
+
return;
|
|
9565
|
+
}
|
|
9566
|
+
const defId = findReachingDef(varName, scopeStack);
|
|
9567
|
+
uses.push({
|
|
9568
|
+
id: uses.length + 1,
|
|
9569
|
+
variable: varName,
|
|
9570
|
+
line: child.startPosition.row + 1,
|
|
9571
|
+
def_id: defId
|
|
9572
|
+
});
|
|
9573
|
+
}
|
|
9574
|
+
});
|
|
9575
|
+
}
|
|
9576
|
+
function findChildByTypeGo(node, type) {
|
|
9577
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
9578
|
+
const child = node.child(i2);
|
|
9579
|
+
if (child?.type === type) return child;
|
|
9580
|
+
}
|
|
9581
|
+
return null;
|
|
9582
|
+
}
|
|
8973
9583
|
|
|
8974
9584
|
// src/analysis/config-loader.ts
|
|
8975
9585
|
function parseConfig(content) {
|
|
@@ -9421,9 +10031,8 @@ var DEFAULT_SINKS = [
|
|
|
9421
10031
|
{ method: "FlowExecution", class: "constructor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
9422
10032
|
// ActiveMQ control commands
|
|
9423
10033
|
{ method: "processControlCommand", class: "TransportConnection", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
9424
|
-
// XStream deserialization (
|
|
9425
|
-
|
|
9426
|
-
{ method: "unmarshal", class: "XStream", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10034
|
+
// XStream deserialization — classified as CWE-502 (deserialization), not CWE-78 (command injection).
|
|
10035
|
+
// The deserialization sink entries at lines ~1059 handle this correctly.
|
|
9427
10036
|
{ method: "fromString", class: "FileConverter", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
9428
10037
|
// Plexus command line
|
|
9429
10038
|
{ method: "getPosition", class: "Commandline", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
@@ -10107,7 +10716,8 @@ var DEFAULT_SINKS = [
|
|
|
10107
10716
|
{ method: "query", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10108
10717
|
{ method: "query", class: "Pool", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10109
10718
|
{ method: "query", class: "Client", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10110
|
-
{ method:
|
|
10719
|
+
// Note: classless { method: 'query' } removed — too many FPs (UriComponentsBuilder.query(), etc.)
|
|
10720
|
+
// SQL query calls are covered by class-specific patterns above (Connection, Pool, Client, JdbcTemplate)
|
|
10111
10721
|
{ method: "raw", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0] },
|
|
10112
10722
|
// Browser DOM XSS sinks
|
|
10113
10723
|
{ method: "setAttribute", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
@@ -10304,8 +10914,8 @@ var DEFAULT_SINKS = [
|
|
|
10304
10914
|
{ method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10305
10915
|
{ method: "query_row", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10306
10916
|
{ method: "prepare", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10307
|
-
// sqlx::query macro
|
|
10308
|
-
{ method: "query", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10917
|
+
// sqlx::query macro — use class-specific pattern
|
|
10918
|
+
{ method: "query", class: "sqlx", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10309
10919
|
// rusqlite specific
|
|
10310
10920
|
{ method: "prepare", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10311
10921
|
{ method: "execute", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
@@ -10820,15 +11430,24 @@ function isInterproceduralTaintableType(typeName) {
|
|
|
10820
11430
|
}
|
|
10821
11431
|
function isParameterizedQueryCall(call, pattern) {
|
|
10822
11432
|
if (pattern.type !== "sql_injection") return false;
|
|
10823
|
-
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
const
|
|
10828
|
-
if (
|
|
11433
|
+
const queryArg = call.arguments.find((a) => a.position === 0);
|
|
11434
|
+
if (queryArg) {
|
|
11435
|
+
const queryText = queryArg.literal ?? queryArg.expression ?? "";
|
|
11436
|
+
const hasPlaceholders = /(\?(?:\s|,|$|\))|\$\d+|:\w+|%s)/.test(queryText);
|
|
11437
|
+
const hasConcatenation = /\+\s*[^+]/.test(queryText) || queryText.includes("${");
|
|
11438
|
+
if (hasPlaceholders && !hasConcatenation && call.arguments.length >= 2) {
|
|
10829
11439
|
return true;
|
|
10830
11440
|
}
|
|
10831
11441
|
}
|
|
11442
|
+
if (call.arguments.length >= 2) {
|
|
11443
|
+
const secondArg = call.arguments.find((a) => a.position === 1);
|
|
11444
|
+
if (secondArg?.expression) {
|
|
11445
|
+
const expr = secondArg.expression.trim();
|
|
11446
|
+
if (expr.startsWith("[")) {
|
|
11447
|
+
return true;
|
|
11448
|
+
}
|
|
11449
|
+
}
|
|
11450
|
+
}
|
|
10832
11451
|
return false;
|
|
10833
11452
|
}
|
|
10834
11453
|
function findSinks(calls, patterns, typeHierarchy) {
|
|
@@ -10954,9 +11573,130 @@ var SAFE_RECEIVERS_BY_METHOD = {
|
|
|
10954
11573
|
"stmt",
|
|
10955
11574
|
"statement",
|
|
10956
11575
|
"cursor"
|
|
11576
|
+
]),
|
|
11577
|
+
// query() is only a SQL sink when receiver is a database handle — not URL builders,
|
|
11578
|
+
// DOM selectors, GraphQL clients, DNS resolvers, etc.
|
|
11579
|
+
query: /* @__PURE__ */ new Set([
|
|
11580
|
+
"uri",
|
|
11581
|
+
"url",
|
|
11582
|
+
"builder",
|
|
11583
|
+
"uribuilder",
|
|
11584
|
+
"uricomponents",
|
|
11585
|
+
"uricomponentsbuilder",
|
|
11586
|
+
"servleturicomponentsbuilder",
|
|
11587
|
+
"httpurl",
|
|
11588
|
+
"urlbuilder",
|
|
11589
|
+
"webclient",
|
|
11590
|
+
"request",
|
|
11591
|
+
"req",
|
|
11592
|
+
"router",
|
|
11593
|
+
"route",
|
|
11594
|
+
"app",
|
|
11595
|
+
"express",
|
|
11596
|
+
"parser",
|
|
11597
|
+
"selector",
|
|
11598
|
+
"jquery",
|
|
11599
|
+
"dom",
|
|
11600
|
+
"document",
|
|
11601
|
+
"element",
|
|
11602
|
+
"xmlpath",
|
|
11603
|
+
"xpath",
|
|
11604
|
+
"dns",
|
|
11605
|
+
"resolver",
|
|
11606
|
+
"graphql",
|
|
11607
|
+
"apollo",
|
|
11608
|
+
"querybuilder",
|
|
11609
|
+
"criteria"
|
|
11610
|
+
]),
|
|
11611
|
+
// authenticate() — safe on auth framework objects (token verification, not code exec)
|
|
11612
|
+
authenticate: /* @__PURE__ */ new Set([
|
|
11613
|
+
"auth",
|
|
11614
|
+
"authenticator",
|
|
11615
|
+
"authmanager",
|
|
11616
|
+
"authprovider",
|
|
11617
|
+
"authenticationmanager",
|
|
11618
|
+
"authservice",
|
|
11619
|
+
"oauth",
|
|
11620
|
+
"token",
|
|
11621
|
+
"jwt",
|
|
11622
|
+
"passport",
|
|
11623
|
+
"session",
|
|
11624
|
+
"security",
|
|
11625
|
+
"credentials",
|
|
11626
|
+
"identityprovider",
|
|
11627
|
+
"ldap",
|
|
11628
|
+
"saml",
|
|
11629
|
+
"oidc"
|
|
11630
|
+
]),
|
|
11631
|
+
// add() is extremely generic — safe on collections, UI containers, builders, etc.
|
|
11632
|
+
add: /* @__PURE__ */ new Set([
|
|
11633
|
+
"list",
|
|
11634
|
+
"set",
|
|
11635
|
+
"map",
|
|
11636
|
+
"collection",
|
|
11637
|
+
"array",
|
|
11638
|
+
"queue",
|
|
11639
|
+
"deque",
|
|
11640
|
+
"stack",
|
|
11641
|
+
"vector",
|
|
11642
|
+
"builder",
|
|
11643
|
+
"panel",
|
|
11644
|
+
"container",
|
|
11645
|
+
"group",
|
|
11646
|
+
"layout",
|
|
11647
|
+
"menu",
|
|
11648
|
+
"toolbar",
|
|
11649
|
+
"model",
|
|
11650
|
+
"registry",
|
|
11651
|
+
"context",
|
|
11652
|
+
"config",
|
|
11653
|
+
"options",
|
|
11654
|
+
"params",
|
|
11655
|
+
"headers",
|
|
11656
|
+
"attributes",
|
|
11657
|
+
"listeners",
|
|
11658
|
+
"handlers",
|
|
11659
|
+
"filters",
|
|
11660
|
+
"interceptors",
|
|
11661
|
+
"validators",
|
|
11662
|
+
"extensions",
|
|
11663
|
+
"plugins",
|
|
11664
|
+
"modules",
|
|
11665
|
+
"components",
|
|
11666
|
+
"children",
|
|
11667
|
+
"items",
|
|
11668
|
+
"elements",
|
|
11669
|
+
"entries",
|
|
11670
|
+
"rows",
|
|
11671
|
+
"columns",
|
|
11672
|
+
"fields",
|
|
11673
|
+
"properties",
|
|
11674
|
+
"descriptors",
|
|
11675
|
+
"nodes",
|
|
11676
|
+
"actions",
|
|
11677
|
+
"results",
|
|
11678
|
+
"errors",
|
|
11679
|
+
"warnings",
|
|
11680
|
+
"messages",
|
|
11681
|
+
"notifications",
|
|
11682
|
+
"events",
|
|
11683
|
+
"subscribers",
|
|
11684
|
+
"observers",
|
|
11685
|
+
"providers",
|
|
11686
|
+
"services",
|
|
11687
|
+
"beans",
|
|
11688
|
+
"tasks",
|
|
11689
|
+
"jobs",
|
|
11690
|
+
"workers",
|
|
11691
|
+
"threads",
|
|
11692
|
+
"schedulers"
|
|
10957
11693
|
])
|
|
10958
11694
|
};
|
|
10959
|
-
function isKnownSafeReceiverForMethod(receiver, method,
|
|
11695
|
+
function isKnownSafeReceiverForMethod(receiver, method, sinkType) {
|
|
11696
|
+
const lowerMethod = method.toLowerCase();
|
|
11697
|
+
if ((lowerMethod === "fromxml" || lowerMethod === "unmarshal") && sinkType === "command_injection") {
|
|
11698
|
+
return true;
|
|
11699
|
+
}
|
|
10960
11700
|
const safeReceivers = SAFE_RECEIVERS_BY_METHOD[method];
|
|
10961
11701
|
if (!safeReceivers) return false;
|
|
10962
11702
|
const lowerReceiver = receiver.toLowerCase();
|
|
@@ -11062,11 +11802,23 @@ function receiverMightBeClass(receiver, className) {
|
|
|
11062
11802
|
}
|
|
11063
11803
|
}
|
|
11064
11804
|
}
|
|
11065
|
-
if (lowerClass.includes(lowerReceiver)) {
|
|
11066
|
-
|
|
11805
|
+
if (lowerReceiver.length >= 3 && lowerClass.includes(lowerReceiver)) {
|
|
11806
|
+
if (lowerReceiver.length >= 5 || lowerReceiver.length / lowerClass.length >= 0.4) {
|
|
11807
|
+
return true;
|
|
11808
|
+
}
|
|
11067
11809
|
}
|
|
11068
|
-
if (
|
|
11069
|
-
|
|
11810
|
+
if (lowerReceiver.length >= 2) {
|
|
11811
|
+
if (lowerClass.startsWith(lowerReceiver) || lowerClass.endsWith(lowerReceiver)) {
|
|
11812
|
+
return true;
|
|
11813
|
+
}
|
|
11814
|
+
}
|
|
11815
|
+
if (lowerReceiver.length >= 3) {
|
|
11816
|
+
const words = className.replace(/([a-z])([A-Z])/g, "$1\0$2").toLowerCase().split("\0");
|
|
11817
|
+
for (const word of words) {
|
|
11818
|
+
if (word.startsWith(lowerReceiver) && lowerReceiver.length / word.length >= 0.4) {
|
|
11819
|
+
return true;
|
|
11820
|
+
}
|
|
11821
|
+
}
|
|
11070
11822
|
}
|
|
11071
11823
|
const commonMappings = {
|
|
11072
11824
|
// HTTP/Servlet
|
|
@@ -11087,8 +11839,10 @@ function receiverMightBeClass(receiver, className) {
|
|
|
11087
11839
|
// Process/Runtime
|
|
11088
11840
|
runtime: ["Runtime"],
|
|
11089
11841
|
pb: ["ProcessBuilder"],
|
|
11090
|
-
// Scripting
|
|
11842
|
+
// Scripting / Expression evaluation
|
|
11091
11843
|
engine: ["ScriptEngine"],
|
|
11844
|
+
ev: ["ExpressionEvaluator", "ScriptEvaluator", "ClassBodyEvaluator"],
|
|
11845
|
+
evaluator: ["ExpressionEvaluator", "ScriptEvaluator", "ClassBodyEvaluator"],
|
|
11092
11846
|
// LDAP
|
|
11093
11847
|
ctx: ["Context", "InitialContext", "DirContext", "InitialDirContext", "LdapContext"],
|
|
11094
11848
|
context: ["Context", "InitialContext", "DirContext", "InitialDirContext", "LdapContext"],
|
|
@@ -11178,12 +11932,25 @@ function receiverMightBeClass(receiver, className) {
|
|
|
11178
11932
|
knex: ["knex"],
|
|
11179
11933
|
prisma: ["prisma"],
|
|
11180
11934
|
axios: ["axios"],
|
|
11181
|
-
fetch: ["fetch"]
|
|
11935
|
+
fetch: ["fetch"],
|
|
11936
|
+
// Go idioms (single-letter receivers)
|
|
11937
|
+
r: ["Request"],
|
|
11938
|
+
w: ["ResponseWriter"]
|
|
11182
11939
|
};
|
|
11183
11940
|
const mappings = commonMappings[lowerReceiver];
|
|
11184
11941
|
if (mappings && Array.isArray(mappings) && mappings.includes(className)) {
|
|
11185
11942
|
return true;
|
|
11186
11943
|
}
|
|
11944
|
+
const strippedReceiver = lowerReceiver.replace(/\d+$/, "");
|
|
11945
|
+
if (strippedReceiver !== lowerReceiver && strippedReceiver.length >= 2) {
|
|
11946
|
+
const strippedMappings = commonMappings[strippedReceiver];
|
|
11947
|
+
if (strippedMappings && Array.isArray(strippedMappings) && strippedMappings.includes(className)) {
|
|
11948
|
+
return true;
|
|
11949
|
+
}
|
|
11950
|
+
if (lowerClass.startsWith(strippedReceiver) || strippedReceiver.startsWith(lowerClass)) {
|
|
11951
|
+
return true;
|
|
11952
|
+
}
|
|
11953
|
+
}
|
|
11187
11954
|
return false;
|
|
11188
11955
|
}
|
|
11189
11956
|
function formatCallLocation(call) {
|