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
|
@@ -4297,6 +4297,9 @@ function extractTypes(tree, cache, language) {
|
|
|
4297
4297
|
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
4298
4298
|
const isPython = effectiveLanguage === "python";
|
|
4299
4299
|
const isRust = effectiveLanguage === "rust";
|
|
4300
|
+
if (effectiveLanguage === "go") {
|
|
4301
|
+
return extractGoTypes(tree, cache);
|
|
4302
|
+
}
|
|
4300
4303
|
if (isRust) {
|
|
4301
4304
|
return extractRustTypes(tree, cache);
|
|
4302
4305
|
}
|
|
@@ -5432,6 +5435,143 @@ function extractRustDerives(node) {
|
|
|
5432
5435
|
}
|
|
5433
5436
|
return derives;
|
|
5434
5437
|
}
|
|
5438
|
+
function extractGoTypes(tree, cache) {
|
|
5439
|
+
const types = [];
|
|
5440
|
+
const root = tree.rootNode;
|
|
5441
|
+
const typeDecls = getNodesFromCache(root, "type_declaration", cache);
|
|
5442
|
+
for (const decl of typeDecls) {
|
|
5443
|
+
for (let i2 = 0; i2 < decl.childCount; i2++) {
|
|
5444
|
+
const spec = decl.child(i2);
|
|
5445
|
+
if (!spec || spec.type !== "type_spec") continue;
|
|
5446
|
+
const nameNode = spec.childForFieldName("name");
|
|
5447
|
+
const typeNode = spec.childForFieldName("type");
|
|
5448
|
+
if (!nameNode || !typeNode) continue;
|
|
5449
|
+
const name2 = getNodeText(nameNode);
|
|
5450
|
+
const isInterface = typeNode.type === "interface_type";
|
|
5451
|
+
const isStruct = typeNode.type === "struct_type";
|
|
5452
|
+
if (!isStruct && !isInterface) continue;
|
|
5453
|
+
const fields = [];
|
|
5454
|
+
const methods = [];
|
|
5455
|
+
if (isStruct) {
|
|
5456
|
+
const fieldList = findChildByType(typeNode, "field_declaration_list");
|
|
5457
|
+
if (fieldList) {
|
|
5458
|
+
for (let j = 0; j < fieldList.childCount; j++) {
|
|
5459
|
+
const field = fieldList.child(j);
|
|
5460
|
+
if (!field || field.type !== "field_declaration") continue;
|
|
5461
|
+
const fieldName = field.childForFieldName("name");
|
|
5462
|
+
const fieldType = field.childForFieldName("type");
|
|
5463
|
+
if (fieldName) {
|
|
5464
|
+
fields.push({
|
|
5465
|
+
name: getNodeText(fieldName),
|
|
5466
|
+
type: fieldType ? getNodeText(fieldType) : null,
|
|
5467
|
+
modifiers: [],
|
|
5468
|
+
annotations: []
|
|
5469
|
+
});
|
|
5470
|
+
}
|
|
5471
|
+
}
|
|
5472
|
+
}
|
|
5473
|
+
}
|
|
5474
|
+
const methodDecls = getNodesFromCache(root, "method_declaration", cache);
|
|
5475
|
+
for (const md of methodDecls) {
|
|
5476
|
+
const receiver = md.childForFieldName("receiver");
|
|
5477
|
+
if (!receiver) continue;
|
|
5478
|
+
const receiverText = getNodeText(receiver);
|
|
5479
|
+
if (receiverText.includes(name2)) {
|
|
5480
|
+
const methodNameNode = md.childForFieldName("name");
|
|
5481
|
+
const params = md.childForFieldName("parameters");
|
|
5482
|
+
const result = md.childForFieldName("result");
|
|
5483
|
+
if (methodNameNode) {
|
|
5484
|
+
methods.push({
|
|
5485
|
+
name: getNodeText(methodNameNode),
|
|
5486
|
+
return_type: result ? getNodeText(result) : null,
|
|
5487
|
+
parameters: params ? extractGoParameters(params) : [],
|
|
5488
|
+
annotations: [],
|
|
5489
|
+
modifiers: [],
|
|
5490
|
+
start_line: md.startPosition.row + 1,
|
|
5491
|
+
end_line: md.endPosition.row + 1
|
|
5492
|
+
});
|
|
5493
|
+
}
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
let pkg = null;
|
|
5497
|
+
const pkgClause = findChildByType(root, "package_clause");
|
|
5498
|
+
if (pkgClause) {
|
|
5499
|
+
for (let j = 0; j < pkgClause.childCount; j++) {
|
|
5500
|
+
const child = pkgClause.child(j);
|
|
5501
|
+
if (child && child.type === "package_identifier") {
|
|
5502
|
+
pkg = getNodeText(child);
|
|
5503
|
+
break;
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
}
|
|
5507
|
+
types.push({
|
|
5508
|
+
name: name2,
|
|
5509
|
+
kind: isInterface ? "interface" : "class",
|
|
5510
|
+
package: pkg,
|
|
5511
|
+
extends: null,
|
|
5512
|
+
implements: [],
|
|
5513
|
+
annotations: [],
|
|
5514
|
+
methods,
|
|
5515
|
+
fields,
|
|
5516
|
+
start_line: decl.startPosition.row + 1,
|
|
5517
|
+
end_line: decl.endPosition.row + 1
|
|
5518
|
+
});
|
|
5519
|
+
}
|
|
5520
|
+
}
|
|
5521
|
+
const functions = getNodesFromCache(root, "function_declaration", cache);
|
|
5522
|
+
if (functions.length > 0) {
|
|
5523
|
+
const moduleFunctions = [];
|
|
5524
|
+
for (const func2 of functions) {
|
|
5525
|
+
const nameNode = func2.childForFieldName("name");
|
|
5526
|
+
const params = func2.childForFieldName("parameters");
|
|
5527
|
+
const result = func2.childForFieldName("result");
|
|
5528
|
+
if (nameNode) {
|
|
5529
|
+
moduleFunctions.push({
|
|
5530
|
+
name: getNodeText(nameNode),
|
|
5531
|
+
return_type: result ? getNodeText(result) : null,
|
|
5532
|
+
parameters: params ? extractGoParameters(params) : [],
|
|
5533
|
+
annotations: [],
|
|
5534
|
+
modifiers: [],
|
|
5535
|
+
start_line: func2.startPosition.row + 1,
|
|
5536
|
+
end_line: func2.endPosition.row + 1
|
|
5537
|
+
});
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
5540
|
+
if (moduleFunctions.length > 0) {
|
|
5541
|
+
types.push({
|
|
5542
|
+
name: "<module>",
|
|
5543
|
+
kind: "class",
|
|
5544
|
+
package: null,
|
|
5545
|
+
extends: null,
|
|
5546
|
+
implements: [],
|
|
5547
|
+
annotations: [],
|
|
5548
|
+
methods: moduleFunctions,
|
|
5549
|
+
fields: [],
|
|
5550
|
+
start_line: 1,
|
|
5551
|
+
end_line: root.endPosition.row + 1
|
|
5552
|
+
});
|
|
5553
|
+
}
|
|
5554
|
+
}
|
|
5555
|
+
return types;
|
|
5556
|
+
}
|
|
5557
|
+
function extractGoParameters(params) {
|
|
5558
|
+
const parameters = [];
|
|
5559
|
+
for (let i2 = 0; i2 < params.childCount; i2++) {
|
|
5560
|
+
const child = params.child(i2);
|
|
5561
|
+
if (!child || child.type !== "parameter_declaration") continue;
|
|
5562
|
+
const nameNode = child.childForFieldName("name");
|
|
5563
|
+
const typeNode = child.childForFieldName("type");
|
|
5564
|
+
if (nameNode) {
|
|
5565
|
+
parameters.push({
|
|
5566
|
+
name: getNodeText(nameNode),
|
|
5567
|
+
type: typeNode ? getNodeText(typeNode) : null,
|
|
5568
|
+
annotations: [],
|
|
5569
|
+
line: child.startPosition.row + 1
|
|
5570
|
+
});
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5573
|
+
return parameters;
|
|
5574
|
+
}
|
|
5435
5575
|
function findChildByType(node, type) {
|
|
5436
5576
|
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
5437
5577
|
const child = node.child(i2);
|
|
@@ -5479,6 +5619,9 @@ function extractCalls(tree, cache, language) {
|
|
|
5479
5619
|
const isJavaScript = detectedLanguage === "javascript" || detectedLanguage === "typescript";
|
|
5480
5620
|
const isPython = detectedLanguage === "python";
|
|
5481
5621
|
const isRust = detectedLanguage === "rust";
|
|
5622
|
+
if (detectedLanguage === "go") {
|
|
5623
|
+
return extractGoCalls(tree, cache);
|
|
5624
|
+
}
|
|
5482
5625
|
if (detectedLanguage === "bash") {
|
|
5483
5626
|
return extractBashCalls(tree, cache);
|
|
5484
5627
|
}
|
|
@@ -6205,7 +6348,7 @@ function extractBashCommandInfo(node) {
|
|
|
6205
6348
|
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
6206
6349
|
const child = node.child(i2);
|
|
6207
6350
|
if (!child) continue;
|
|
6208
|
-
if (child === nameNode) continue;
|
|
6351
|
+
if (child === nameNode || child.id === nameNode.id) continue;
|
|
6209
6352
|
if (child.type.includes("redirect") || child.type === "heredoc_body" || child.type === "file_descriptor") {
|
|
6210
6353
|
continue;
|
|
6211
6354
|
}
|
|
@@ -6761,6 +6904,104 @@ function resolveRustCall(methodName, receiver, context) {
|
|
|
6761
6904
|
status: "external_method"
|
|
6762
6905
|
};
|
|
6763
6906
|
}
|
|
6907
|
+
function extractGoCalls(tree, cache) {
|
|
6908
|
+
const calls = [];
|
|
6909
|
+
const callExpressions = getNodesFromCache(tree.rootNode, "call_expression", cache);
|
|
6910
|
+
for (const call of callExpressions) {
|
|
6911
|
+
const callInfo = extractGoCallInfo(call);
|
|
6912
|
+
if (callInfo) {
|
|
6913
|
+
calls.push(callInfo);
|
|
6914
|
+
}
|
|
6915
|
+
}
|
|
6916
|
+
return calls;
|
|
6917
|
+
}
|
|
6918
|
+
function extractGoCallInfo(node) {
|
|
6919
|
+
const funcNode = node.childForFieldName("function");
|
|
6920
|
+
if (!funcNode) return null;
|
|
6921
|
+
let methodName;
|
|
6922
|
+
let receiver = null;
|
|
6923
|
+
if (funcNode.type === "selector_expression") {
|
|
6924
|
+
const operand = funcNode.childForFieldName("operand");
|
|
6925
|
+
const field = funcNode.childForFieldName("field");
|
|
6926
|
+
receiver = operand ? getNodeText(operand) : null;
|
|
6927
|
+
methodName = field ? getNodeText(field) : getNodeText(funcNode);
|
|
6928
|
+
} else if (funcNode.type === "identifier") {
|
|
6929
|
+
methodName = getNodeText(funcNode);
|
|
6930
|
+
} else {
|
|
6931
|
+
methodName = getNodeText(funcNode);
|
|
6932
|
+
}
|
|
6933
|
+
const argsNode = node.childForFieldName("arguments");
|
|
6934
|
+
const args2 = argsNode ? extractGoArguments(argsNode) : [];
|
|
6935
|
+
const inMethod = findGoEnclosingFunction(node);
|
|
6936
|
+
return {
|
|
6937
|
+
method_name: methodName,
|
|
6938
|
+
receiver,
|
|
6939
|
+
arguments: args2,
|
|
6940
|
+
location: {
|
|
6941
|
+
line: node.startPosition.row + 1,
|
|
6942
|
+
column: node.startPosition.column
|
|
6943
|
+
},
|
|
6944
|
+
in_method: inMethod,
|
|
6945
|
+
resolved: false,
|
|
6946
|
+
resolution: { status: "external_method" }
|
|
6947
|
+
};
|
|
6948
|
+
}
|
|
6949
|
+
function extractGoArguments(argsNode) {
|
|
6950
|
+
const args2 = [];
|
|
6951
|
+
let position = 0;
|
|
6952
|
+
for (let i2 = 0; i2 < argsNode.childCount; i2++) {
|
|
6953
|
+
const child = argsNode.child(i2);
|
|
6954
|
+
if (!child) continue;
|
|
6955
|
+
if (child.type === "(" || child.type === ")" || child.type === ",") continue;
|
|
6956
|
+
const expression = getNodeText(child);
|
|
6957
|
+
const variable = child.type === "identifier" ? expression : null;
|
|
6958
|
+
const literal = isGoLiteral(child) ? extractGoLiteralValue(child) : null;
|
|
6959
|
+
args2.push({
|
|
6960
|
+
position: position++,
|
|
6961
|
+
expression,
|
|
6962
|
+
variable,
|
|
6963
|
+
literal
|
|
6964
|
+
});
|
|
6965
|
+
}
|
|
6966
|
+
return args2;
|
|
6967
|
+
}
|
|
6968
|
+
function isGoLiteral(node) {
|
|
6969
|
+
const literalTypes = /* @__PURE__ */ new Set([
|
|
6970
|
+
"interpreted_string_literal",
|
|
6971
|
+
"raw_string_literal",
|
|
6972
|
+
"int_literal",
|
|
6973
|
+
"float_literal",
|
|
6974
|
+
"true",
|
|
6975
|
+
"false",
|
|
6976
|
+
"nil"
|
|
6977
|
+
]);
|
|
6978
|
+
return literalTypes.has(node.type);
|
|
6979
|
+
}
|
|
6980
|
+
function extractGoLiteralValue(node) {
|
|
6981
|
+
const text = getNodeText(node);
|
|
6982
|
+
if (node.type === "interpreted_string_literal") {
|
|
6983
|
+
return text.slice(1, -1);
|
|
6984
|
+
}
|
|
6985
|
+
if (node.type === "raw_string_literal") {
|
|
6986
|
+
return text.slice(1, -1);
|
|
6987
|
+
}
|
|
6988
|
+
return text;
|
|
6989
|
+
}
|
|
6990
|
+
function findGoEnclosingFunction(node) {
|
|
6991
|
+
let current = node.parent;
|
|
6992
|
+
while (current) {
|
|
6993
|
+
if (current.type === "function_declaration") {
|
|
6994
|
+
const nameNode = current.childForFieldName("name");
|
|
6995
|
+
return nameNode ? getNodeText(nameNode) : null;
|
|
6996
|
+
}
|
|
6997
|
+
if (current.type === "method_declaration") {
|
|
6998
|
+
const nameNode = current.childForFieldName("name");
|
|
6999
|
+
return nameNode ? getNodeText(nameNode) : null;
|
|
7000
|
+
}
|
|
7001
|
+
current = current.parent;
|
|
7002
|
+
}
|
|
7003
|
+
return null;
|
|
7004
|
+
}
|
|
6764
7005
|
|
|
6765
7006
|
// src/core/extractors/imports.ts
|
|
6766
7007
|
function detectLanguage2(tree) {
|
|
@@ -6815,6 +7056,9 @@ function extractImports(tree, language) {
|
|
|
6815
7056
|
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
6816
7057
|
const isPython = effectiveLanguage === "python";
|
|
6817
7058
|
const isRust = effectiveLanguage === "rust";
|
|
7059
|
+
if (effectiveLanguage === "go") {
|
|
7060
|
+
return extractGoImports(tree);
|
|
7061
|
+
}
|
|
6818
7062
|
if (isRust) {
|
|
6819
7063
|
return extractRustImports(tree);
|
|
6820
7064
|
}
|
|
@@ -7362,6 +7606,61 @@ function extractRustScopedUseList(node, lineNumber) {
|
|
|
7362
7606
|
}
|
|
7363
7607
|
return imports;
|
|
7364
7608
|
}
|
|
7609
|
+
function extractGoImports(tree) {
|
|
7610
|
+
const imports = [];
|
|
7611
|
+
const importDecls = findNodes(tree.rootNode, "import_declaration");
|
|
7612
|
+
for (const decl of importDecls) {
|
|
7613
|
+
const singleSpec = findGoChildByType(decl, "import_spec");
|
|
7614
|
+
if (singleSpec) {
|
|
7615
|
+
const parsed = parseGoImportSpec(singleSpec);
|
|
7616
|
+
if (parsed) imports.push(parsed);
|
|
7617
|
+
continue;
|
|
7618
|
+
}
|
|
7619
|
+
const specList = findGoChildByType(decl, "import_spec_list");
|
|
7620
|
+
if (specList) {
|
|
7621
|
+
for (let i2 = 0; i2 < specList.childCount; i2++) {
|
|
7622
|
+
const spec = specList.child(i2);
|
|
7623
|
+
if (!spec || spec.type !== "import_spec") continue;
|
|
7624
|
+
const parsed = parseGoImportSpec(spec);
|
|
7625
|
+
if (parsed) imports.push(parsed);
|
|
7626
|
+
}
|
|
7627
|
+
}
|
|
7628
|
+
}
|
|
7629
|
+
return imports;
|
|
7630
|
+
}
|
|
7631
|
+
function parseGoImportSpec(spec) {
|
|
7632
|
+
let alias = null;
|
|
7633
|
+
let path;
|
|
7634
|
+
for (let i2 = 0; i2 < spec.childCount; i2++) {
|
|
7635
|
+
const child = spec.child(i2);
|
|
7636
|
+
if (!child) continue;
|
|
7637
|
+
if (child.type === "package_identifier" || child.type === "blank_identifier" || child.type === "dot") {
|
|
7638
|
+
alias = getNodeText(child);
|
|
7639
|
+
}
|
|
7640
|
+
if (child.type === "interpreted_string_literal") {
|
|
7641
|
+
const text = getNodeText(child);
|
|
7642
|
+
path = text.slice(1, -1);
|
|
7643
|
+
}
|
|
7644
|
+
}
|
|
7645
|
+
if (!path) return null;
|
|
7646
|
+
const shortName = alias || path.split("/").pop() || path;
|
|
7647
|
+
return {
|
|
7648
|
+
imported_name: shortName,
|
|
7649
|
+
from_package: path,
|
|
7650
|
+
alias,
|
|
7651
|
+
is_wildcard: alias === ".",
|
|
7652
|
+
line_number: spec.startPosition.row + 1
|
|
7653
|
+
};
|
|
7654
|
+
}
|
|
7655
|
+
function findGoChildByType(node, type) {
|
|
7656
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
7657
|
+
const child = node.child(i2);
|
|
7658
|
+
if (child?.type === type) {
|
|
7659
|
+
return child;
|
|
7660
|
+
}
|
|
7661
|
+
}
|
|
7662
|
+
return null;
|
|
7663
|
+
}
|
|
7365
7664
|
|
|
7366
7665
|
// src/core/extractors/exports.ts
|
|
7367
7666
|
function extractExports(types) {
|
|
@@ -7453,6 +7752,9 @@ function buildCFG(tree, language) {
|
|
|
7453
7752
|
if (effectiveLanguage === "bash") {
|
|
7454
7753
|
return buildBashCFG(tree, blockIdCounter);
|
|
7455
7754
|
}
|
|
7755
|
+
if (effectiveLanguage === "go") {
|
|
7756
|
+
return buildGoCFG(tree, blockIdCounter);
|
|
7757
|
+
}
|
|
7456
7758
|
if (isJavaScript) {
|
|
7457
7759
|
const functions = [
|
|
7458
7760
|
...findNodes(tree.rootNode, "function_declaration"),
|
|
@@ -7929,6 +8231,58 @@ function isStatement(node, isJavaScript) {
|
|
|
7929
8231
|
]);
|
|
7930
8232
|
return isJavaScript ? jsStatementTypes.has(node.type) : javaStatementTypes.has(node.type);
|
|
7931
8233
|
}
|
|
8234
|
+
function buildGoCFG(tree, blockIdCounter) {
|
|
8235
|
+
const allBlocks = [];
|
|
8236
|
+
const allEdges = [];
|
|
8237
|
+
const functions = [
|
|
8238
|
+
...findNodes(tree.rootNode, "function_declaration"),
|
|
8239
|
+
...findNodes(tree.rootNode, "method_declaration")
|
|
8240
|
+
];
|
|
8241
|
+
for (const func2 of functions) {
|
|
8242
|
+
const body2 = func2.childForFieldName("body");
|
|
8243
|
+
if (!body2 || body2.type !== "block") continue;
|
|
8244
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, false);
|
|
8245
|
+
allBlocks.push(...blocks);
|
|
8246
|
+
allEdges.push(...edges);
|
|
8247
|
+
blockIdCounter = nextId;
|
|
8248
|
+
}
|
|
8249
|
+
const hasTopLevelDecls = tree.rootNode.children.some(
|
|
8250
|
+
(c) => c !== null && isGoStatement(c)
|
|
8251
|
+
);
|
|
8252
|
+
if (hasTopLevelDecls) {
|
|
8253
|
+
const block = {
|
|
8254
|
+
id: blockIdCounter++,
|
|
8255
|
+
type: "normal",
|
|
8256
|
+
start_line: 1,
|
|
8257
|
+
end_line: tree.rootNode.endPosition.row + 1
|
|
8258
|
+
};
|
|
8259
|
+
allBlocks.push(block);
|
|
8260
|
+
}
|
|
8261
|
+
return { blocks: allBlocks, edges: allEdges };
|
|
8262
|
+
}
|
|
8263
|
+
function isGoStatement(node) {
|
|
8264
|
+
const goStatementTypes = /* @__PURE__ */ new Set([
|
|
8265
|
+
"short_var_declaration",
|
|
8266
|
+
"var_declaration",
|
|
8267
|
+
"assignment_statement",
|
|
8268
|
+
"expression_statement",
|
|
8269
|
+
"if_statement",
|
|
8270
|
+
"for_statement",
|
|
8271
|
+
"switch_statement",
|
|
8272
|
+
"type_switch_statement",
|
|
8273
|
+
"select_statement",
|
|
8274
|
+
"return_statement",
|
|
8275
|
+
"go_statement",
|
|
8276
|
+
"defer_statement",
|
|
8277
|
+
"send_statement",
|
|
8278
|
+
"inc_statement",
|
|
8279
|
+
"dec_statement",
|
|
8280
|
+
"block",
|
|
8281
|
+
"type_declaration",
|
|
8282
|
+
"const_declaration"
|
|
8283
|
+
]);
|
|
8284
|
+
return goStatementTypes.has(node.type);
|
|
8285
|
+
}
|
|
7932
8286
|
|
|
7933
8287
|
// src/core/extractors/dfg.ts
|
|
7934
8288
|
function detectLanguage4(tree) {
|
|
@@ -7976,6 +8330,9 @@ function buildDFG(tree, cache, language) {
|
|
|
7976
8330
|
if (effectiveLanguage === "bash") {
|
|
7977
8331
|
return buildBashDFG(tree);
|
|
7978
8332
|
}
|
|
8333
|
+
if (effectiveLanguage === "go") {
|
|
8334
|
+
return buildGoDFG(tree);
|
|
8335
|
+
}
|
|
7979
8336
|
return buildJavaDFG(tree, cache);
|
|
7980
8337
|
}
|
|
7981
8338
|
function buildJavaDFG(tree, cache) {
|
|
@@ -8959,68 +9316,321 @@ function isRustKeyword(name2) {
|
|
|
8959
9316
|
]);
|
|
8960
9317
|
return keywords.has(name2);
|
|
8961
9318
|
}
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
8967
|
-
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
8974
|
-
|
|
8975
|
-
|
|
8976
|
-
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9319
|
+
var GO_KEYWORDS = /* @__PURE__ */ new Set([
|
|
9320
|
+
"break",
|
|
9321
|
+
"case",
|
|
9322
|
+
"chan",
|
|
9323
|
+
"const",
|
|
9324
|
+
"continue",
|
|
9325
|
+
"default",
|
|
9326
|
+
"defer",
|
|
9327
|
+
"else",
|
|
9328
|
+
"fallthrough",
|
|
9329
|
+
"for",
|
|
9330
|
+
"func",
|
|
9331
|
+
"go",
|
|
9332
|
+
"goto",
|
|
9333
|
+
"if",
|
|
9334
|
+
"import",
|
|
9335
|
+
"interface",
|
|
9336
|
+
"map",
|
|
9337
|
+
"package",
|
|
9338
|
+
"range",
|
|
9339
|
+
"return",
|
|
9340
|
+
"select",
|
|
9341
|
+
"struct",
|
|
9342
|
+
"switch",
|
|
9343
|
+
"type",
|
|
9344
|
+
"var",
|
|
9345
|
+
"true",
|
|
9346
|
+
"false",
|
|
9347
|
+
"nil",
|
|
9348
|
+
"iota",
|
|
9349
|
+
"append",
|
|
9350
|
+
"cap",
|
|
9351
|
+
"close",
|
|
9352
|
+
"complex",
|
|
9353
|
+
"copy",
|
|
9354
|
+
"delete",
|
|
9355
|
+
"imag",
|
|
9356
|
+
"len",
|
|
9357
|
+
"make",
|
|
9358
|
+
"new",
|
|
9359
|
+
"panic",
|
|
9360
|
+
"print",
|
|
9361
|
+
"println",
|
|
9362
|
+
"real",
|
|
9363
|
+
"recover",
|
|
9364
|
+
"string",
|
|
9365
|
+
"int",
|
|
9366
|
+
"int8",
|
|
9367
|
+
"int16",
|
|
9368
|
+
"int32",
|
|
9369
|
+
"int64",
|
|
9370
|
+
"uint",
|
|
9371
|
+
"uint8",
|
|
9372
|
+
"uint16",
|
|
9373
|
+
"uint32",
|
|
9374
|
+
"uint64",
|
|
9375
|
+
"float32",
|
|
9376
|
+
"float64",
|
|
9377
|
+
"complex64",
|
|
9378
|
+
"complex128",
|
|
9379
|
+
"byte",
|
|
9380
|
+
"rune",
|
|
9381
|
+
"bool",
|
|
9382
|
+
"error",
|
|
9383
|
+
"any"
|
|
9384
|
+
]);
|
|
9385
|
+
function buildGoDFG(tree) {
|
|
9386
|
+
const defs = [];
|
|
9387
|
+
const uses = [];
|
|
9388
|
+
let defIdCounter = 1;
|
|
9389
|
+
let useIdCounter = 1;
|
|
9390
|
+
const scopeStack = [/* @__PURE__ */ new Map()];
|
|
9391
|
+
const functions = [
|
|
9392
|
+
...findNodes(tree.rootNode, "function_declaration"),
|
|
9393
|
+
...findNodes(tree.rootNode, "method_declaration")
|
|
9394
|
+
];
|
|
9395
|
+
for (const func2 of functions) {
|
|
9396
|
+
scopeStack.push(/* @__PURE__ */ new Map());
|
|
9397
|
+
const params = func2.childForFieldName("parameters");
|
|
9398
|
+
if (params) {
|
|
9399
|
+
extractGoParamDefs(params, defs, defIdCounter, scopeStack);
|
|
9400
|
+
defIdCounter = defs.length + 1;
|
|
9401
|
+
}
|
|
9402
|
+
const receiver = func2.childForFieldName("receiver");
|
|
9403
|
+
if (receiver) {
|
|
9404
|
+
extractGoParamDefs(receiver, defs, defIdCounter, scopeStack);
|
|
9405
|
+
defIdCounter = defs.length + 1;
|
|
9406
|
+
}
|
|
9407
|
+
const body2 = func2.childForFieldName("body");
|
|
9408
|
+
if (body2) {
|
|
9409
|
+
processGoBlock(body2, defs, uses, scopeStack, { defId: defIdCounter, useId: useIdCounter });
|
|
9410
|
+
defIdCounter = defs.length + 1;
|
|
9411
|
+
useIdCounter = uses.length + 1;
|
|
9412
|
+
}
|
|
9413
|
+
scopeStack.pop();
|
|
9414
|
+
}
|
|
9415
|
+
for (let i2 = 0; i2 < tree.rootNode.childCount; i2++) {
|
|
9416
|
+
const child = tree.rootNode.child(i2);
|
|
9417
|
+
if (!child) continue;
|
|
9418
|
+
if (child.type === "var_declaration") {
|
|
9419
|
+
processGoVarDecl(child, defs, scopeStack, { defId: defIdCounter });
|
|
9420
|
+
defIdCounter = defs.length + 1;
|
|
9421
|
+
}
|
|
9422
|
+
}
|
|
9423
|
+
const chains = computeChains(defs, uses);
|
|
9424
|
+
return { defs, uses, chains };
|
|
9425
|
+
}
|
|
9426
|
+
function extractGoParamDefs(params, defs, _startId, scopeStack) {
|
|
9427
|
+
for (let i2 = 0; i2 < params.childCount; i2++) {
|
|
9428
|
+
const param = params.child(i2);
|
|
9429
|
+
if (!param || param.type !== "parameter_declaration") continue;
|
|
9430
|
+
const typeNode = param.childForFieldName("type");
|
|
9431
|
+
for (let j = 0; j < param.childCount; j++) {
|
|
9432
|
+
const nameNode = param.child(j);
|
|
9433
|
+
if (!nameNode || nameNode.type !== "identifier") continue;
|
|
9434
|
+
if (nameNode === typeNode) continue;
|
|
9435
|
+
const varName = getNodeText(nameNode);
|
|
9436
|
+
if (varName === "_") continue;
|
|
9437
|
+
const def = {
|
|
9438
|
+
id: defs.length + 1,
|
|
9439
|
+
variable: varName,
|
|
9440
|
+
kind: "param",
|
|
9441
|
+
line: param.startPosition.row + 1
|
|
9442
|
+
};
|
|
9443
|
+
defs.push(def);
|
|
9444
|
+
currentScope(scopeStack).set(varName, def.id);
|
|
9445
|
+
}
|
|
9446
|
+
}
|
|
9447
|
+
}
|
|
9448
|
+
function processGoBlock(node, defs, uses, scopeStack, counters) {
|
|
9449
|
+
walkTree(node, (child) => {
|
|
9450
|
+
if (child.type === "short_var_declaration") {
|
|
9451
|
+
const left = child.childForFieldName("left");
|
|
9452
|
+
const right = child.childForFieldName("right");
|
|
9453
|
+
if (right) {
|
|
9454
|
+
extractGoUses(right, uses, scopeStack);
|
|
9455
|
+
}
|
|
9456
|
+
if (left) {
|
|
9457
|
+
extractGoLhsDefs(left, defs, scopeStack, child.startPosition.row + 1);
|
|
9458
|
+
}
|
|
9459
|
+
} else if (child.type === "var_declaration") {
|
|
9460
|
+
processGoVarDecl(child, defs, scopeStack, counters);
|
|
9461
|
+
} else if (child.type === "assignment_statement") {
|
|
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 === "for_statement") {
|
|
9471
|
+
const rangeClause = findChildByTypeGo(child, "range_clause");
|
|
9472
|
+
if (rangeClause) {
|
|
9473
|
+
const left = rangeClause.childForFieldName("left");
|
|
9474
|
+
if (left) {
|
|
9475
|
+
extractGoLhsDefs(left, defs, scopeStack, child.startPosition.row + 1);
|
|
9476
|
+
}
|
|
9477
|
+
}
|
|
9478
|
+
} else if (child.type === "call_expression") {
|
|
9479
|
+
extractGoUses(child, uses, scopeStack);
|
|
9480
|
+
} else if (child.type === "return_statement") {
|
|
9481
|
+
for (let i2 = 0; i2 < child.childCount; i2++) {
|
|
9482
|
+
const expr = child.child(i2);
|
|
9483
|
+
if (expr && expr.type !== "return") {
|
|
9484
|
+
extractGoUses(expr, uses, scopeStack);
|
|
9485
|
+
}
|
|
9486
|
+
}
|
|
9487
|
+
}
|
|
9488
|
+
});
|
|
9489
|
+
}
|
|
9490
|
+
function processGoVarDecl(node, defs, scopeStack, _counters) {
|
|
9491
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
9492
|
+
const spec = node.child(i2);
|
|
9493
|
+
if (!spec || spec.type !== "var_spec") continue;
|
|
9494
|
+
for (let j = 0; j < spec.childCount; j++) {
|
|
9495
|
+
const nameNode = spec.child(j);
|
|
9496
|
+
if (!nameNode || nameNode.type !== "identifier") continue;
|
|
9497
|
+
const varName = getNodeText(nameNode);
|
|
9498
|
+
if (varName === "_") continue;
|
|
9499
|
+
const def = {
|
|
9500
|
+
id: defs.length + 1,
|
|
9501
|
+
variable: varName,
|
|
9502
|
+
kind: "local",
|
|
9503
|
+
line: spec.startPosition.row + 1
|
|
9504
|
+
};
|
|
9505
|
+
defs.push(def);
|
|
9506
|
+
currentScope(scopeStack).set(varName, def.id);
|
|
9507
|
+
}
|
|
9508
|
+
}
|
|
9509
|
+
}
|
|
9510
|
+
function extractGoLhsDefs(left, defs, scopeStack, line) {
|
|
9511
|
+
if (left.type === "identifier") {
|
|
9512
|
+
const varName = getNodeText(left);
|
|
9513
|
+
if (varName === "_") return;
|
|
9514
|
+
const def = {
|
|
9515
|
+
id: defs.length + 1,
|
|
9516
|
+
variable: varName,
|
|
9517
|
+
kind: "local",
|
|
9518
|
+
line
|
|
9519
|
+
};
|
|
9520
|
+
defs.push(def);
|
|
9521
|
+
currentScope(scopeStack).set(varName, def.id);
|
|
9522
|
+
} else if (left.type === "expression_list") {
|
|
9523
|
+
for (let i2 = 0; i2 < left.childCount; i2++) {
|
|
9524
|
+
const item = left.child(i2);
|
|
9525
|
+
if (item && item.type === "identifier") {
|
|
9526
|
+
const varName = getNodeText(item);
|
|
9527
|
+
if (varName === "_") continue;
|
|
9528
|
+
const def = {
|
|
9529
|
+
id: defs.length + 1,
|
|
9530
|
+
variable: varName,
|
|
9531
|
+
kind: "local",
|
|
9532
|
+
line
|
|
9533
|
+
};
|
|
9534
|
+
defs.push(def);
|
|
9535
|
+
currentScope(scopeStack).set(varName, def.id);
|
|
9536
|
+
}
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9539
|
+
}
|
|
9540
|
+
function extractGoUses(node, uses, scopeStack) {
|
|
9541
|
+
walkTree(node, (child) => {
|
|
9542
|
+
if (child.type === "identifier") {
|
|
9543
|
+
const varName = getNodeText(child);
|
|
9544
|
+
if (varName === "_" || GO_KEYWORDS.has(varName)) return;
|
|
9545
|
+
const parent = child.parent;
|
|
9546
|
+
if (parent?.type === "selector_expression" && parent.childForFieldName("field") === child) {
|
|
9547
|
+
return;
|
|
9548
|
+
}
|
|
9549
|
+
if (parent?.type === "parameter_declaration" && parent.childForFieldName("type") === child) {
|
|
9550
|
+
return;
|
|
9551
|
+
}
|
|
9552
|
+
if (parent?.type === "type_identifier") {
|
|
9553
|
+
return;
|
|
9554
|
+
}
|
|
9555
|
+
const defId = findReachingDef(varName, scopeStack);
|
|
9556
|
+
uses.push({
|
|
9557
|
+
id: uses.length + 1,
|
|
9558
|
+
variable: varName,
|
|
9559
|
+
line: child.startPosition.row + 1,
|
|
9560
|
+
def_id: defId
|
|
9561
|
+
});
|
|
9562
|
+
}
|
|
9563
|
+
});
|
|
9564
|
+
}
|
|
9565
|
+
function findChildByTypeGo(node, type) {
|
|
9566
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
9567
|
+
const child = node.child(i2);
|
|
9568
|
+
if (child?.type === type) return child;
|
|
9569
|
+
}
|
|
9570
|
+
return null;
|
|
9571
|
+
}
|
|
9572
|
+
|
|
9573
|
+
// src/analysis/config-loader.ts
|
|
9574
|
+
var DEFAULT_SOURCES = [
|
|
9575
|
+
// HTTP Sources (Servlet API)
|
|
9576
|
+
{ method: "getParameter", class: "HttpServletRequest", type: "http_param", severity: "high", return_tainted: true },
|
|
9577
|
+
{ method: "getParameterValues", class: "HttpServletRequest", type: "http_param", severity: "high", return_tainted: true },
|
|
9578
|
+
{ method: "getParameterMap", class: "HttpServletRequest", type: "http_param", severity: "high", return_tainted: true },
|
|
9579
|
+
{ method: "getParameterNames", class: "HttpServletRequest", type: "http_param", severity: "high", return_tainted: true },
|
|
9580
|
+
{ method: "getHeader", class: "HttpServletRequest", type: "http_header", severity: "high", return_tainted: true },
|
|
9581
|
+
{ method: "getHeaders", class: "HttpServletRequest", type: "http_header", severity: "high", return_tainted: true },
|
|
9582
|
+
{ method: "getHeaderNames", class: "HttpServletRequest", type: "http_header", severity: "high", return_tainted: true },
|
|
9583
|
+
{ method: "getQueryString", class: "HttpServletRequest", type: "http_query", severity: "high", return_tainted: true },
|
|
9584
|
+
{ method: "getCookies", class: "HttpServletRequest", type: "http_cookie", severity: "high", return_tainted: true },
|
|
9585
|
+
{ method: "getInputStream", class: "HttpServletRequest", type: "http_body", severity: "high", return_tainted: true },
|
|
9586
|
+
{ method: "getReader", class: "HttpServletRequest", type: "http_body", severity: "high", return_tainted: true },
|
|
9587
|
+
{ method: "getPathInfo", class: "HttpServletRequest", type: "http_path", severity: "high", return_tainted: true },
|
|
9588
|
+
{ method: "getRequestURI", class: "HttpServletRequest", type: "http_path", severity: "high", return_tainted: true },
|
|
9589
|
+
{ method: "getRequestURL", class: "HttpServletRequest", type: "http_path", severity: "high", return_tainted: true },
|
|
9590
|
+
{ method: "getServletPath", class: "HttpServletRequest", type: "http_path", severity: "high", return_tainted: true },
|
|
9591
|
+
{ method: "getContextPath", class: "HttpServletRequest", type: "http_path", severity: "medium", return_tainted: true },
|
|
9592
|
+
{ method: "getRemoteHost", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
|
|
9593
|
+
{ method: "getRemoteAddr", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
|
|
9594
|
+
// Additional HTTP request methods that can be attacker-controlled
|
|
9595
|
+
{ method: "getProtocol", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
|
|
9596
|
+
{ method: "getScheme", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
|
|
9597
|
+
{ method: "getAuthType", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
|
|
9598
|
+
{ method: "getRemoteUser", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
|
|
9599
|
+
{ method: "getMethod", class: "HttpServletRequest", type: "http_header", severity: "low", return_tainted: true },
|
|
9600
|
+
{ method: "getContentType", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
|
|
9601
|
+
{ method: "getCharacterEncoding", class: "HttpServletRequest", type: "http_header", severity: "low", return_tainted: true },
|
|
9602
|
+
// Enumeration/Iterator sources (from request.getHeaders(), etc.)
|
|
9603
|
+
{ method: "nextElement", class: "Enumeration", type: "http_header", severity: "high", return_tainted: true },
|
|
9604
|
+
{ method: "nextElement", type: "http_header", severity: "high", return_tainted: true },
|
|
9605
|
+
// Cookie sources
|
|
9606
|
+
{ method: "getValue", class: "Cookie", type: "http_cookie", severity: "high", return_tainted: true },
|
|
9607
|
+
{ method: "getName", class: "Cookie", type: "http_cookie", severity: "high", return_tainted: true },
|
|
9608
|
+
// I/O Sources (Scanner, BufferedReader, etc.)
|
|
9609
|
+
{ method: "readLine", class: "BufferedReader", type: "io_input", severity: "high", return_tainted: true },
|
|
9610
|
+
{ method: "readLine", type: "io_input", severity: "high", return_tainted: true },
|
|
9611
|
+
{ method: "nextLine", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
|
|
9612
|
+
{ method: "next", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
|
|
9613
|
+
{ method: "nextInt", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
|
|
9614
|
+
// Database result sources
|
|
9615
|
+
{ method: "getString", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
|
|
9616
|
+
{ method: "getObject", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
|
|
9617
|
+
{ method: "getInt", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
|
|
9618
|
+
// Spring annotations
|
|
9619
|
+
{ annotation: "RequestParam", type: "http_param", severity: "high", param_tainted: true },
|
|
9620
|
+
{ annotation: "RequestBody", type: "http_body", severity: "high", param_tainted: true },
|
|
9621
|
+
{ annotation: "PathVariable", type: "http_path", severity: "medium", param_tainted: true },
|
|
9622
|
+
{ annotation: "RequestHeader", type: "http_header", severity: "high", param_tainted: true },
|
|
9623
|
+
{ annotation: "CookieValue", type: "http_cookie", severity: "high", param_tainted: true },
|
|
9624
|
+
// JAX-RS annotations
|
|
9625
|
+
{ annotation: "QueryParam", type: "http_param", severity: "high", param_tainted: true },
|
|
9626
|
+
{ annotation: "FormParam", type: "http_param", severity: "high", param_tainted: true },
|
|
9627
|
+
{ annotation: "PathParam", type: "http_path", severity: "medium", param_tainted: true },
|
|
9628
|
+
{ annotation: "HeaderParam", type: "http_header", severity: "high", param_tainted: true },
|
|
9629
|
+
// Environment
|
|
9630
|
+
{ method: "getenv", class: "System", type: "env_input", severity: "medium", return_tainted: true },
|
|
9631
|
+
{ method: "getProperty", class: "System", type: "env_input", severity: "medium", return_tainted: true },
|
|
9632
|
+
// Note: Properties.getProperty is NOT included by default as it causes many false positives
|
|
9633
|
+
// in OWASP Benchmark. Include it via custom config if needed for specific analyses.
|
|
9024
9634
|
// Servlet Configuration Parameters (can be attacker-influenced in some deployments)
|
|
9025
9635
|
{ method: "getInitParameter", class: "ServletConfig", type: "http_param", severity: "medium", return_tainted: true },
|
|
9026
9636
|
{ method: "getInitParameter", class: "ServletContext", type: "http_param", severity: "medium", return_tainted: true },
|
|
@@ -9373,9 +9983,8 @@ var DEFAULT_SINKS = [
|
|
|
9373
9983
|
{ method: "FlowExecution", class: "constructor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
9374
9984
|
// ActiveMQ control commands
|
|
9375
9985
|
{ method: "processControlCommand", class: "TransportConnection", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
9376
|
-
// XStream deserialization (
|
|
9377
|
-
|
|
9378
|
-
{ method: "unmarshal", class: "XStream", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
9986
|
+
// XStream deserialization — classified as CWE-502 (deserialization), not CWE-78 (command injection).
|
|
9987
|
+
// The deserialization sink entries at lines ~1059 handle this correctly.
|
|
9379
9988
|
{ method: "fromString", class: "FileConverter", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
9380
9989
|
// Plexus command line
|
|
9381
9990
|
{ method: "getPosition", class: "Commandline", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
@@ -10059,7 +10668,8 @@ var DEFAULT_SINKS = [
|
|
|
10059
10668
|
{ method: "query", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10060
10669
|
{ method: "query", class: "Pool", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10061
10670
|
{ method: "query", class: "Client", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10062
|
-
{ method:
|
|
10671
|
+
// Note: classless { method: 'query' } removed — too many FPs (UriComponentsBuilder.query(), etc.)
|
|
10672
|
+
// SQL query calls are covered by class-specific patterns above (Connection, Pool, Client, JdbcTemplate)
|
|
10063
10673
|
{ method: "raw", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0] },
|
|
10064
10674
|
// Browser DOM XSS sinks
|
|
10065
10675
|
{ method: "setAttribute", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
@@ -10256,8 +10866,8 @@ var DEFAULT_SINKS = [
|
|
|
10256
10866
|
{ method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10257
10867
|
{ method: "query_row", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10258
10868
|
{ method: "prepare", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10259
|
-
// sqlx::query macro
|
|
10260
|
-
{ method: "query", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10869
|
+
// sqlx::query macro — use class-specific pattern
|
|
10870
|
+
{ method: "query", class: "sqlx", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10261
10871
|
// rusqlite specific
|
|
10262
10872
|
{ method: "prepare", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
10263
10873
|
{ method: "execute", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
@@ -10859,15 +11469,24 @@ function isInterproceduralTaintableType(typeName) {
|
|
|
10859
11469
|
}
|
|
10860
11470
|
function isParameterizedQueryCall(call, pattern) {
|
|
10861
11471
|
if (pattern.type !== "sql_injection") return false;
|
|
10862
|
-
|
|
10863
|
-
|
|
10864
|
-
|
|
10865
|
-
|
|
10866
|
-
const
|
|
10867
|
-
if (
|
|
11472
|
+
const queryArg = call.arguments.find((a) => a.position === 0);
|
|
11473
|
+
if (queryArg) {
|
|
11474
|
+
const queryText = queryArg.literal ?? queryArg.expression ?? "";
|
|
11475
|
+
const hasPlaceholders = /(\?(?:\s|,|$|\))|\$\d+|:\w+|%s)/.test(queryText);
|
|
11476
|
+
const hasConcatenation = /\+\s*[^+]/.test(queryText) || queryText.includes("${");
|
|
11477
|
+
if (hasPlaceholders && !hasConcatenation && call.arguments.length >= 2) {
|
|
10868
11478
|
return true;
|
|
10869
11479
|
}
|
|
10870
11480
|
}
|
|
11481
|
+
if (call.arguments.length >= 2) {
|
|
11482
|
+
const secondArg = call.arguments.find((a) => a.position === 1);
|
|
11483
|
+
if (secondArg?.expression) {
|
|
11484
|
+
const expr = secondArg.expression.trim();
|
|
11485
|
+
if (expr.startsWith("[")) {
|
|
11486
|
+
return true;
|
|
11487
|
+
}
|
|
11488
|
+
}
|
|
11489
|
+
}
|
|
10871
11490
|
return false;
|
|
10872
11491
|
}
|
|
10873
11492
|
function findSinks(calls, patterns, typeHierarchy) {
|
|
@@ -10993,9 +11612,130 @@ var SAFE_RECEIVERS_BY_METHOD = {
|
|
|
10993
11612
|
"stmt",
|
|
10994
11613
|
"statement",
|
|
10995
11614
|
"cursor"
|
|
11615
|
+
]),
|
|
11616
|
+
// query() is only a SQL sink when receiver is a database handle — not URL builders,
|
|
11617
|
+
// DOM selectors, GraphQL clients, DNS resolvers, etc.
|
|
11618
|
+
query: /* @__PURE__ */ new Set([
|
|
11619
|
+
"uri",
|
|
11620
|
+
"url",
|
|
11621
|
+
"builder",
|
|
11622
|
+
"uribuilder",
|
|
11623
|
+
"uricomponents",
|
|
11624
|
+
"uricomponentsbuilder",
|
|
11625
|
+
"servleturicomponentsbuilder",
|
|
11626
|
+
"httpurl",
|
|
11627
|
+
"urlbuilder",
|
|
11628
|
+
"webclient",
|
|
11629
|
+
"request",
|
|
11630
|
+
"req",
|
|
11631
|
+
"router",
|
|
11632
|
+
"route",
|
|
11633
|
+
"app",
|
|
11634
|
+
"express",
|
|
11635
|
+
"parser",
|
|
11636
|
+
"selector",
|
|
11637
|
+
"jquery",
|
|
11638
|
+
"dom",
|
|
11639
|
+
"document",
|
|
11640
|
+
"element",
|
|
11641
|
+
"xmlpath",
|
|
11642
|
+
"xpath",
|
|
11643
|
+
"dns",
|
|
11644
|
+
"resolver",
|
|
11645
|
+
"graphql",
|
|
11646
|
+
"apollo",
|
|
11647
|
+
"querybuilder",
|
|
11648
|
+
"criteria"
|
|
11649
|
+
]),
|
|
11650
|
+
// authenticate() — safe on auth framework objects (token verification, not code exec)
|
|
11651
|
+
authenticate: /* @__PURE__ */ new Set([
|
|
11652
|
+
"auth",
|
|
11653
|
+
"authenticator",
|
|
11654
|
+
"authmanager",
|
|
11655
|
+
"authprovider",
|
|
11656
|
+
"authenticationmanager",
|
|
11657
|
+
"authservice",
|
|
11658
|
+
"oauth",
|
|
11659
|
+
"token",
|
|
11660
|
+
"jwt",
|
|
11661
|
+
"passport",
|
|
11662
|
+
"session",
|
|
11663
|
+
"security",
|
|
11664
|
+
"credentials",
|
|
11665
|
+
"identityprovider",
|
|
11666
|
+
"ldap",
|
|
11667
|
+
"saml",
|
|
11668
|
+
"oidc"
|
|
11669
|
+
]),
|
|
11670
|
+
// add() is extremely generic — safe on collections, UI containers, builders, etc.
|
|
11671
|
+
add: /* @__PURE__ */ new Set([
|
|
11672
|
+
"list",
|
|
11673
|
+
"set",
|
|
11674
|
+
"map",
|
|
11675
|
+
"collection",
|
|
11676
|
+
"array",
|
|
11677
|
+
"queue",
|
|
11678
|
+
"deque",
|
|
11679
|
+
"stack",
|
|
11680
|
+
"vector",
|
|
11681
|
+
"builder",
|
|
11682
|
+
"panel",
|
|
11683
|
+
"container",
|
|
11684
|
+
"group",
|
|
11685
|
+
"layout",
|
|
11686
|
+
"menu",
|
|
11687
|
+
"toolbar",
|
|
11688
|
+
"model",
|
|
11689
|
+
"registry",
|
|
11690
|
+
"context",
|
|
11691
|
+
"config",
|
|
11692
|
+
"options",
|
|
11693
|
+
"params",
|
|
11694
|
+
"headers",
|
|
11695
|
+
"attributes",
|
|
11696
|
+
"listeners",
|
|
11697
|
+
"handlers",
|
|
11698
|
+
"filters",
|
|
11699
|
+
"interceptors",
|
|
11700
|
+
"validators",
|
|
11701
|
+
"extensions",
|
|
11702
|
+
"plugins",
|
|
11703
|
+
"modules",
|
|
11704
|
+
"components",
|
|
11705
|
+
"children",
|
|
11706
|
+
"items",
|
|
11707
|
+
"elements",
|
|
11708
|
+
"entries",
|
|
11709
|
+
"rows",
|
|
11710
|
+
"columns",
|
|
11711
|
+
"fields",
|
|
11712
|
+
"properties",
|
|
11713
|
+
"descriptors",
|
|
11714
|
+
"nodes",
|
|
11715
|
+
"actions",
|
|
11716
|
+
"results",
|
|
11717
|
+
"errors",
|
|
11718
|
+
"warnings",
|
|
11719
|
+
"messages",
|
|
11720
|
+
"notifications",
|
|
11721
|
+
"events",
|
|
11722
|
+
"subscribers",
|
|
11723
|
+
"observers",
|
|
11724
|
+
"providers",
|
|
11725
|
+
"services",
|
|
11726
|
+
"beans",
|
|
11727
|
+
"tasks",
|
|
11728
|
+
"jobs",
|
|
11729
|
+
"workers",
|
|
11730
|
+
"threads",
|
|
11731
|
+
"schedulers"
|
|
10996
11732
|
])
|
|
10997
11733
|
};
|
|
10998
|
-
function isKnownSafeReceiverForMethod(receiver, method,
|
|
11734
|
+
function isKnownSafeReceiverForMethod(receiver, method, sinkType) {
|
|
11735
|
+
const lowerMethod = method.toLowerCase();
|
|
11736
|
+
if ((lowerMethod === "fromxml" || lowerMethod === "unmarshal") && sinkType === "command_injection") {
|
|
11737
|
+
return true;
|
|
11738
|
+
}
|
|
10999
11739
|
const safeReceivers = SAFE_RECEIVERS_BY_METHOD[method];
|
|
11000
11740
|
if (!safeReceivers) return false;
|
|
11001
11741
|
const lowerReceiver = receiver.toLowerCase();
|
|
@@ -11101,11 +11841,23 @@ function receiverMightBeClass(receiver, className) {
|
|
|
11101
11841
|
}
|
|
11102
11842
|
}
|
|
11103
11843
|
}
|
|
11104
|
-
if (lowerClass.includes(lowerReceiver)) {
|
|
11105
|
-
|
|
11844
|
+
if (lowerReceiver.length >= 3 && lowerClass.includes(lowerReceiver)) {
|
|
11845
|
+
if (lowerReceiver.length >= 5 || lowerReceiver.length / lowerClass.length >= 0.4) {
|
|
11846
|
+
return true;
|
|
11847
|
+
}
|
|
11106
11848
|
}
|
|
11107
|
-
if (
|
|
11108
|
-
|
|
11849
|
+
if (lowerReceiver.length >= 2) {
|
|
11850
|
+
if (lowerClass.startsWith(lowerReceiver) || lowerClass.endsWith(lowerReceiver)) {
|
|
11851
|
+
return true;
|
|
11852
|
+
}
|
|
11853
|
+
}
|
|
11854
|
+
if (lowerReceiver.length >= 3) {
|
|
11855
|
+
const words = className.replace(/([a-z])([A-Z])/g, "$1\0$2").toLowerCase().split("\0");
|
|
11856
|
+
for (const word of words) {
|
|
11857
|
+
if (word.startsWith(lowerReceiver) && lowerReceiver.length / word.length >= 0.4) {
|
|
11858
|
+
return true;
|
|
11859
|
+
}
|
|
11860
|
+
}
|
|
11109
11861
|
}
|
|
11110
11862
|
const commonMappings = {
|
|
11111
11863
|
// HTTP/Servlet
|
|
@@ -11126,8 +11878,10 @@ function receiverMightBeClass(receiver, className) {
|
|
|
11126
11878
|
// Process/Runtime
|
|
11127
11879
|
runtime: ["Runtime"],
|
|
11128
11880
|
pb: ["ProcessBuilder"],
|
|
11129
|
-
// Scripting
|
|
11881
|
+
// Scripting / Expression evaluation
|
|
11130
11882
|
engine: ["ScriptEngine"],
|
|
11883
|
+
ev: ["ExpressionEvaluator", "ScriptEvaluator", "ClassBodyEvaluator"],
|
|
11884
|
+
evaluator: ["ExpressionEvaluator", "ScriptEvaluator", "ClassBodyEvaluator"],
|
|
11131
11885
|
// LDAP
|
|
11132
11886
|
ctx: ["Context", "InitialContext", "DirContext", "InitialDirContext", "LdapContext"],
|
|
11133
11887
|
context: ["Context", "InitialContext", "DirContext", "InitialDirContext", "LdapContext"],
|
|
@@ -11217,12 +11971,25 @@ function receiverMightBeClass(receiver, className) {
|
|
|
11217
11971
|
knex: ["knex"],
|
|
11218
11972
|
prisma: ["prisma"],
|
|
11219
11973
|
axios: ["axios"],
|
|
11220
|
-
fetch: ["fetch"]
|
|
11974
|
+
fetch: ["fetch"],
|
|
11975
|
+
// Go idioms (single-letter receivers)
|
|
11976
|
+
r: ["Request"],
|
|
11977
|
+
w: ["ResponseWriter"]
|
|
11221
11978
|
};
|
|
11222
11979
|
const mappings = commonMappings[lowerReceiver];
|
|
11223
11980
|
if (mappings && Array.isArray(mappings) && mappings.includes(className)) {
|
|
11224
11981
|
return true;
|
|
11225
11982
|
}
|
|
11983
|
+
const strippedReceiver = lowerReceiver.replace(/\d+$/, "");
|
|
11984
|
+
if (strippedReceiver !== lowerReceiver && strippedReceiver.length >= 2) {
|
|
11985
|
+
const strippedMappings = commonMappings[strippedReceiver];
|
|
11986
|
+
if (strippedMappings && Array.isArray(strippedMappings) && strippedMappings.includes(className)) {
|
|
11987
|
+
return true;
|
|
11988
|
+
}
|
|
11989
|
+
if (lowerClass.startsWith(strippedReceiver) || strippedReceiver.startsWith(lowerClass)) {
|
|
11990
|
+
return true;
|
|
11991
|
+
}
|
|
11992
|
+
}
|
|
11226
11993
|
return false;
|
|
11227
11994
|
}
|
|
11228
11995
|
function formatCallLocation(call) {
|
|
@@ -17886,211 +18653,784 @@ var BashPlugin = class extends BaseLanguagePlugin {
|
|
|
17886
18653
|
argPositions: [1]
|
|
17887
18654
|
},
|
|
17888
18655
|
{
|
|
17889
|
-
method: "zsh",
|
|
17890
|
-
type: "command_injection",
|
|
17891
|
-
cwe: "CWE-78",
|
|
17892
|
-
severity: "critical",
|
|
17893
|
-
argPositions: [1]
|
|
18656
|
+
method: "zsh",
|
|
18657
|
+
type: "command_injection",
|
|
18658
|
+
cwe: "CWE-78",
|
|
18659
|
+
severity: "critical",
|
|
18660
|
+
argPositions: [1]
|
|
18661
|
+
},
|
|
18662
|
+
{
|
|
18663
|
+
method: "ksh",
|
|
18664
|
+
type: "command_injection",
|
|
18665
|
+
cwe: "CWE-78",
|
|
18666
|
+
severity: "critical",
|
|
18667
|
+
argPositions: [1]
|
|
18668
|
+
},
|
|
18669
|
+
// SQL injection via DB CLI clients (first arg is query/expression)
|
|
18670
|
+
{
|
|
18671
|
+
method: "mysql",
|
|
18672
|
+
type: "sql_injection",
|
|
18673
|
+
cwe: "CWE-89",
|
|
18674
|
+
severity: "critical",
|
|
18675
|
+
argPositions: [1]
|
|
18676
|
+
},
|
|
18677
|
+
{
|
|
18678
|
+
method: "psql",
|
|
18679
|
+
type: "sql_injection",
|
|
18680
|
+
cwe: "CWE-89",
|
|
18681
|
+
severity: "critical",
|
|
18682
|
+
argPositions: [1]
|
|
18683
|
+
},
|
|
18684
|
+
{
|
|
18685
|
+
method: "sqlite3",
|
|
18686
|
+
type: "sql_injection",
|
|
18687
|
+
cwe: "CWE-89",
|
|
18688
|
+
severity: "critical",
|
|
18689
|
+
argPositions: [1]
|
|
18690
|
+
},
|
|
18691
|
+
// Path traversal via file operations (first arg is path)
|
|
18692
|
+
{
|
|
18693
|
+
method: "cat",
|
|
18694
|
+
type: "path_traversal",
|
|
18695
|
+
cwe: "CWE-22",
|
|
18696
|
+
severity: "high",
|
|
18697
|
+
argPositions: [0]
|
|
18698
|
+
},
|
|
18699
|
+
{
|
|
18700
|
+
method: "rm",
|
|
18701
|
+
type: "path_traversal",
|
|
18702
|
+
cwe: "CWE-22",
|
|
18703
|
+
severity: "high",
|
|
18704
|
+
argPositions: [0]
|
|
18705
|
+
},
|
|
18706
|
+
{
|
|
18707
|
+
method: "cp",
|
|
18708
|
+
type: "path_traversal",
|
|
18709
|
+
cwe: "CWE-22",
|
|
18710
|
+
severity: "high",
|
|
18711
|
+
argPositions: [0]
|
|
18712
|
+
},
|
|
18713
|
+
{
|
|
18714
|
+
method: "mv",
|
|
18715
|
+
type: "path_traversal",
|
|
18716
|
+
cwe: "CWE-22",
|
|
18717
|
+
severity: "high",
|
|
18718
|
+
argPositions: [0]
|
|
18719
|
+
},
|
|
18720
|
+
{
|
|
18721
|
+
method: "chmod",
|
|
18722
|
+
type: "path_traversal",
|
|
18723
|
+
cwe: "CWE-22",
|
|
18724
|
+
severity: "medium",
|
|
18725
|
+
argPositions: [1]
|
|
18726
|
+
},
|
|
18727
|
+
{
|
|
18728
|
+
method: "chown",
|
|
18729
|
+
type: "path_traversal",
|
|
18730
|
+
cwe: "CWE-22",
|
|
18731
|
+
severity: "medium",
|
|
18732
|
+
argPositions: [1]
|
|
18733
|
+
},
|
|
18734
|
+
// SSRF — curl/wget with externally-controlled URL
|
|
18735
|
+
{
|
|
18736
|
+
method: "curl",
|
|
18737
|
+
type: "ssrf",
|
|
18738
|
+
cwe: "CWE-918",
|
|
18739
|
+
severity: "high",
|
|
18740
|
+
argPositions: [0]
|
|
18741
|
+
},
|
|
18742
|
+
{
|
|
18743
|
+
method: "wget",
|
|
18744
|
+
type: "ssrf",
|
|
18745
|
+
cwe: "CWE-918",
|
|
18746
|
+
severity: "high",
|
|
18747
|
+
argPositions: [0]
|
|
18748
|
+
}
|
|
18749
|
+
];
|
|
18750
|
+
}
|
|
18751
|
+
/**
|
|
18752
|
+
* Shell has no OOP receiver types.
|
|
18753
|
+
*/
|
|
18754
|
+
getReceiverType(_node, _context) {
|
|
18755
|
+
return void 0;
|
|
18756
|
+
}
|
|
18757
|
+
/**
|
|
18758
|
+
* Bash string literals: quoted strings and raw ($'...') strings.
|
|
18759
|
+
*/
|
|
18760
|
+
isStringLiteral(node) {
|
|
18761
|
+
return node.type === "string" || node.type === "raw_string" || node.type === "ansi_c_string";
|
|
18762
|
+
}
|
|
18763
|
+
/**
|
|
18764
|
+
* Extract string value from bash string literal, stripping quotes.
|
|
18765
|
+
*/
|
|
18766
|
+
getStringValue(node) {
|
|
18767
|
+
if (!this.isStringLiteral(node)) return void 0;
|
|
18768
|
+
const text = node.text;
|
|
18769
|
+
if (node.type === "raw_string") {
|
|
18770
|
+
return text.slice(1, -1);
|
|
18771
|
+
}
|
|
18772
|
+
if (node.type === "ansi_c_string") {
|
|
18773
|
+
return text.slice(2, -1);
|
|
18774
|
+
}
|
|
18775
|
+
const match = text.match(/^"(.*)"$/s);
|
|
18776
|
+
if (match) return match[1];
|
|
18777
|
+
return text;
|
|
18778
|
+
}
|
|
18779
|
+
// Extraction methods — delegate to base extractors via generic walker
|
|
18780
|
+
extractTypes(_context) {
|
|
18781
|
+
return [];
|
|
18782
|
+
}
|
|
18783
|
+
extractCalls(_context) {
|
|
18784
|
+
return [];
|
|
18785
|
+
}
|
|
18786
|
+
extractImports(_context) {
|
|
18787
|
+
return [];
|
|
18788
|
+
}
|
|
18789
|
+
extractPackage(_context) {
|
|
18790
|
+
return void 0;
|
|
18791
|
+
}
|
|
18792
|
+
};
|
|
18793
|
+
|
|
18794
|
+
// src/languages/plugins/html.ts
|
|
18795
|
+
var HtmlPlugin = class extends BaseLanguagePlugin {
|
|
18796
|
+
id = "html";
|
|
18797
|
+
name = "HTML";
|
|
18798
|
+
extensions = [".html", ".htm", ".xhtml"];
|
|
18799
|
+
wasmPath = "tree-sitter-html.wasm";
|
|
18800
|
+
nodeTypes = {
|
|
18801
|
+
// HTML has no OOP constructs
|
|
18802
|
+
classDeclaration: [],
|
|
18803
|
+
interfaceDeclaration: [],
|
|
18804
|
+
enumDeclaration: [],
|
|
18805
|
+
functionDeclaration: [],
|
|
18806
|
+
methodDeclaration: [],
|
|
18807
|
+
// No expressions in HTML
|
|
18808
|
+
methodCall: [],
|
|
18809
|
+
functionCall: [],
|
|
18810
|
+
assignment: [],
|
|
18811
|
+
variableDeclaration: [],
|
|
18812
|
+
// No parameters
|
|
18813
|
+
parameter: [],
|
|
18814
|
+
argument: [],
|
|
18815
|
+
// No annotations
|
|
18816
|
+
annotation: [],
|
|
18817
|
+
decorator: [],
|
|
18818
|
+
// No imports
|
|
18819
|
+
importStatement: [],
|
|
18820
|
+
// No control flow
|
|
18821
|
+
ifStatement: [],
|
|
18822
|
+
forStatement: [],
|
|
18823
|
+
whileStatement: [],
|
|
18824
|
+
tryStatement: [],
|
|
18825
|
+
returnStatement: []
|
|
18826
|
+
};
|
|
18827
|
+
detectFramework(_context) {
|
|
18828
|
+
return void 0;
|
|
18829
|
+
}
|
|
18830
|
+
getBuiltinSources() {
|
|
18831
|
+
return [];
|
|
18832
|
+
}
|
|
18833
|
+
getBuiltinSinks() {
|
|
18834
|
+
return [];
|
|
18835
|
+
}
|
|
18836
|
+
getReceiverType(_node, _context) {
|
|
18837
|
+
return void 0;
|
|
18838
|
+
}
|
|
18839
|
+
isStringLiteral(node) {
|
|
18840
|
+
return node.type === "attribute_value" || node.type === "quoted_attribute_value";
|
|
18841
|
+
}
|
|
18842
|
+
getStringValue(node) {
|
|
18843
|
+
if (!this.isStringLiteral(node)) return void 0;
|
|
18844
|
+
const text = node.text;
|
|
18845
|
+
if (text.startsWith('"') && text.endsWith('"') || text.startsWith("'") && text.endsWith("'")) {
|
|
18846
|
+
return text.slice(1, -1);
|
|
18847
|
+
}
|
|
18848
|
+
return text;
|
|
18849
|
+
}
|
|
18850
|
+
extractTypes(_context) {
|
|
18851
|
+
return [];
|
|
18852
|
+
}
|
|
18853
|
+
extractCalls(_context) {
|
|
18854
|
+
return [];
|
|
18855
|
+
}
|
|
18856
|
+
extractImports(_context) {
|
|
18857
|
+
return [];
|
|
18858
|
+
}
|
|
18859
|
+
extractPackage(_context) {
|
|
18860
|
+
return void 0;
|
|
18861
|
+
}
|
|
18862
|
+
};
|
|
18863
|
+
|
|
18864
|
+
// src/languages/plugins/go.ts
|
|
18865
|
+
var GoPlugin = class extends BaseLanguagePlugin {
|
|
18866
|
+
id = "go";
|
|
18867
|
+
name = "Go";
|
|
18868
|
+
extensions = [".go"];
|
|
18869
|
+
wasmPath = "tree-sitter-go.wasm";
|
|
18870
|
+
nodeTypes = {
|
|
18871
|
+
// Type declarations
|
|
18872
|
+
classDeclaration: ["type_declaration"],
|
|
18873
|
+
interfaceDeclaration: ["type_declaration"],
|
|
18874
|
+
enumDeclaration: [],
|
|
18875
|
+
// Go has no enums (uses iota constants)
|
|
18876
|
+
functionDeclaration: ["function_declaration"],
|
|
18877
|
+
methodDeclaration: ["method_declaration"],
|
|
18878
|
+
// Expressions
|
|
18879
|
+
methodCall: ["call_expression"],
|
|
18880
|
+
functionCall: ["call_expression"],
|
|
18881
|
+
assignment: ["short_var_declaration", "assignment_statement", "var_declaration"],
|
|
18882
|
+
variableDeclaration: ["short_var_declaration", "var_declaration"],
|
|
18883
|
+
// Parameters and arguments
|
|
18884
|
+
parameter: ["parameter_declaration"],
|
|
18885
|
+
argument: ["argument_list"],
|
|
18886
|
+
// Annotations/decorators
|
|
18887
|
+
annotation: [],
|
|
18888
|
+
// Go has no annotations
|
|
18889
|
+
decorator: [],
|
|
18890
|
+
// Go has no decorators
|
|
18891
|
+
// Imports
|
|
18892
|
+
importStatement: ["import_declaration"],
|
|
18893
|
+
// Control flow
|
|
18894
|
+
ifStatement: ["if_statement"],
|
|
18895
|
+
forStatement: ["for_statement"],
|
|
18896
|
+
whileStatement: [],
|
|
18897
|
+
// Go uses for with condition
|
|
18898
|
+
tryStatement: [],
|
|
18899
|
+
// Go uses defer/recover
|
|
18900
|
+
returnStatement: ["return_statement"]
|
|
18901
|
+
};
|
|
18902
|
+
/**
|
|
18903
|
+
* Detect Go web frameworks from imports.
|
|
18904
|
+
*/
|
|
18905
|
+
detectFramework(context) {
|
|
18906
|
+
const indicators = [];
|
|
18907
|
+
let framework;
|
|
18908
|
+
let confidence = 0;
|
|
18909
|
+
for (const imp of context.imports) {
|
|
18910
|
+
const path = imp.from_package || imp.imported_name;
|
|
18911
|
+
if (path.includes("gin-gonic/gin")) {
|
|
18912
|
+
framework = "gin";
|
|
18913
|
+
confidence = Math.max(confidence, 0.95);
|
|
18914
|
+
indicators.push(`import: ${path}`);
|
|
18915
|
+
}
|
|
18916
|
+
if (path.includes("labstack/echo")) {
|
|
18917
|
+
framework = "echo";
|
|
18918
|
+
confidence = Math.max(confidence, 0.95);
|
|
18919
|
+
indicators.push(`import: ${path}`);
|
|
18920
|
+
}
|
|
18921
|
+
if (path.includes("gofiber/fiber")) {
|
|
18922
|
+
framework = "fiber";
|
|
18923
|
+
confidence = Math.max(confidence, 0.95);
|
|
18924
|
+
indicators.push(`import: ${path}`);
|
|
18925
|
+
}
|
|
18926
|
+
if (path.includes("go-chi/chi")) {
|
|
18927
|
+
framework = "chi";
|
|
18928
|
+
confidence = Math.max(confidence, 0.9);
|
|
18929
|
+
indicators.push(`import: ${path}`);
|
|
18930
|
+
}
|
|
18931
|
+
if (path.includes("gorm.io/gorm")) {
|
|
18932
|
+
indicators.push(`import: ${path}`);
|
|
18933
|
+
}
|
|
18934
|
+
if (path === "net/http") {
|
|
18935
|
+
framework = framework || "net/http";
|
|
18936
|
+
confidence = Math.max(confidence, 0.8);
|
|
18937
|
+
indicators.push(`import: ${path}`);
|
|
18938
|
+
}
|
|
18939
|
+
}
|
|
18940
|
+
if (framework) {
|
|
18941
|
+
return { name: framework, confidence, indicators };
|
|
18942
|
+
}
|
|
18943
|
+
return void 0;
|
|
18944
|
+
}
|
|
18945
|
+
/**
|
|
18946
|
+
* Go taint source patterns.
|
|
18947
|
+
*/
|
|
18948
|
+
getBuiltinSources() {
|
|
18949
|
+
return [
|
|
18950
|
+
// net/http request methods
|
|
18951
|
+
{
|
|
18952
|
+
method: "FormValue",
|
|
18953
|
+
class: "Request",
|
|
18954
|
+
type: "http_param",
|
|
18955
|
+
severity: "high",
|
|
18956
|
+
confidence: 0.95,
|
|
18957
|
+
returnTainted: true
|
|
18958
|
+
},
|
|
18959
|
+
{
|
|
18960
|
+
method: "PostFormValue",
|
|
18961
|
+
class: "Request",
|
|
18962
|
+
type: "http_param",
|
|
18963
|
+
severity: "high",
|
|
18964
|
+
confidence: 0.95,
|
|
18965
|
+
returnTainted: true
|
|
18966
|
+
},
|
|
18967
|
+
{
|
|
18968
|
+
method: "Query",
|
|
18969
|
+
class: "URL",
|
|
18970
|
+
type: "http_param",
|
|
18971
|
+
severity: "high",
|
|
18972
|
+
confidence: 0.95,
|
|
18973
|
+
returnTainted: true
|
|
18974
|
+
},
|
|
18975
|
+
{
|
|
18976
|
+
method: "Get",
|
|
18977
|
+
class: "Header",
|
|
18978
|
+
type: "http_header",
|
|
18979
|
+
severity: "high",
|
|
18980
|
+
confidence: 0.9,
|
|
18981
|
+
returnTainted: true
|
|
18982
|
+
},
|
|
18983
|
+
{
|
|
18984
|
+
method: "Cookie",
|
|
18985
|
+
class: "Request",
|
|
18986
|
+
type: "http_cookie",
|
|
18987
|
+
severity: "high",
|
|
18988
|
+
confidence: 0.9,
|
|
18989
|
+
returnTainted: true
|
|
18990
|
+
},
|
|
18991
|
+
// Gin framework
|
|
18992
|
+
{
|
|
18993
|
+
method: "Query",
|
|
18994
|
+
class: "Context",
|
|
18995
|
+
type: "http_param",
|
|
18996
|
+
severity: "high",
|
|
18997
|
+
confidence: 0.95,
|
|
18998
|
+
returnTainted: true
|
|
18999
|
+
},
|
|
19000
|
+
{
|
|
19001
|
+
method: "Param",
|
|
19002
|
+
class: "Context",
|
|
19003
|
+
type: "http_path",
|
|
19004
|
+
severity: "high",
|
|
19005
|
+
confidence: 0.95,
|
|
19006
|
+
returnTainted: true
|
|
19007
|
+
},
|
|
19008
|
+
{
|
|
19009
|
+
method: "PostForm",
|
|
19010
|
+
class: "Context",
|
|
19011
|
+
type: "http_param",
|
|
19012
|
+
severity: "high",
|
|
19013
|
+
confidence: 0.95,
|
|
19014
|
+
returnTainted: true
|
|
19015
|
+
},
|
|
19016
|
+
{
|
|
19017
|
+
method: "GetRawData",
|
|
19018
|
+
class: "Context",
|
|
19019
|
+
type: "http_body",
|
|
19020
|
+
severity: "high",
|
|
19021
|
+
confidence: 0.95,
|
|
19022
|
+
returnTainted: true
|
|
19023
|
+
},
|
|
19024
|
+
// Standard library I/O
|
|
19025
|
+
{
|
|
19026
|
+
method: "Getenv",
|
|
19027
|
+
class: "os",
|
|
19028
|
+
type: "env_var",
|
|
19029
|
+
severity: "medium",
|
|
19030
|
+
confidence: 0.85,
|
|
19031
|
+
returnTainted: true
|
|
19032
|
+
},
|
|
19033
|
+
{
|
|
19034
|
+
method: "ReadAll",
|
|
19035
|
+
class: "io",
|
|
19036
|
+
type: "io_input",
|
|
19037
|
+
severity: "medium",
|
|
19038
|
+
confidence: 0.8,
|
|
19039
|
+
returnTainted: true
|
|
19040
|
+
},
|
|
19041
|
+
{
|
|
19042
|
+
method: "ReadFile",
|
|
19043
|
+
class: "os",
|
|
19044
|
+
type: "file_input",
|
|
19045
|
+
severity: "medium",
|
|
19046
|
+
confidence: 0.8,
|
|
19047
|
+
returnTainted: true
|
|
17894
19048
|
},
|
|
17895
19049
|
{
|
|
17896
|
-
method: "
|
|
17897
|
-
|
|
17898
|
-
|
|
19050
|
+
method: "Text",
|
|
19051
|
+
class: "Scanner",
|
|
19052
|
+
type: "io_input",
|
|
19053
|
+
severity: "medium",
|
|
19054
|
+
confidence: 0.8,
|
|
19055
|
+
returnTainted: true
|
|
19056
|
+
}
|
|
19057
|
+
];
|
|
19058
|
+
}
|
|
19059
|
+
/**
|
|
19060
|
+
* Go taint sink patterns.
|
|
19061
|
+
*/
|
|
19062
|
+
getBuiltinSinks() {
|
|
19063
|
+
return [
|
|
19064
|
+
// SQL Injection
|
|
19065
|
+
{
|
|
19066
|
+
method: "Query",
|
|
19067
|
+
class: "DB",
|
|
19068
|
+
type: "sql_injection",
|
|
19069
|
+
cwe: "CWE-89",
|
|
17899
19070
|
severity: "critical",
|
|
17900
|
-
argPositions: [
|
|
19071
|
+
argPositions: [0]
|
|
17901
19072
|
},
|
|
17902
|
-
// SQL injection via DB CLI clients (first arg is query/expression)
|
|
17903
19073
|
{
|
|
17904
|
-
method: "
|
|
19074
|
+
method: "QueryRow",
|
|
19075
|
+
class: "DB",
|
|
17905
19076
|
type: "sql_injection",
|
|
17906
19077
|
cwe: "CWE-89",
|
|
17907
19078
|
severity: "critical",
|
|
17908
|
-
argPositions: [
|
|
19079
|
+
argPositions: [0]
|
|
17909
19080
|
},
|
|
17910
19081
|
{
|
|
17911
|
-
method: "
|
|
19082
|
+
method: "Exec",
|
|
19083
|
+
class: "DB",
|
|
17912
19084
|
type: "sql_injection",
|
|
17913
19085
|
cwe: "CWE-89",
|
|
17914
19086
|
severity: "critical",
|
|
17915
|
-
argPositions: [
|
|
19087
|
+
argPositions: [0]
|
|
17916
19088
|
},
|
|
17917
19089
|
{
|
|
17918
|
-
method: "
|
|
19090
|
+
method: "Query",
|
|
19091
|
+
class: "Tx",
|
|
17919
19092
|
type: "sql_injection",
|
|
17920
19093
|
cwe: "CWE-89",
|
|
17921
19094
|
severity: "critical",
|
|
17922
|
-
argPositions: [
|
|
19095
|
+
argPositions: [0]
|
|
17923
19096
|
},
|
|
17924
|
-
//
|
|
19097
|
+
// Command Injection
|
|
17925
19098
|
{
|
|
17926
|
-
method: "
|
|
17927
|
-
|
|
17928
|
-
|
|
17929
|
-
|
|
19099
|
+
method: "Command",
|
|
19100
|
+
class: "exec",
|
|
19101
|
+
type: "command_injection",
|
|
19102
|
+
cwe: "CWE-78",
|
|
19103
|
+
severity: "critical",
|
|
17930
19104
|
argPositions: [0]
|
|
17931
19105
|
},
|
|
17932
19106
|
{
|
|
17933
|
-
method: "
|
|
19107
|
+
method: "CommandContext",
|
|
19108
|
+
class: "exec",
|
|
19109
|
+
type: "command_injection",
|
|
19110
|
+
cwe: "CWE-78",
|
|
19111
|
+
severity: "critical",
|
|
19112
|
+
argPositions: [1]
|
|
19113
|
+
},
|
|
19114
|
+
// Path Traversal
|
|
19115
|
+
{
|
|
19116
|
+
method: "Open",
|
|
19117
|
+
class: "os",
|
|
17934
19118
|
type: "path_traversal",
|
|
17935
19119
|
cwe: "CWE-22",
|
|
17936
19120
|
severity: "high",
|
|
17937
19121
|
argPositions: [0]
|
|
17938
19122
|
},
|
|
17939
19123
|
{
|
|
17940
|
-
method: "
|
|
19124
|
+
method: "ReadFile",
|
|
19125
|
+
class: "os",
|
|
17941
19126
|
type: "path_traversal",
|
|
17942
19127
|
cwe: "CWE-22",
|
|
17943
19128
|
severity: "high",
|
|
17944
19129
|
argPositions: [0]
|
|
17945
19130
|
},
|
|
17946
19131
|
{
|
|
17947
|
-
method: "
|
|
19132
|
+
method: "WriteFile",
|
|
19133
|
+
class: "os",
|
|
17948
19134
|
type: "path_traversal",
|
|
17949
19135
|
cwe: "CWE-22",
|
|
17950
19136
|
severity: "high",
|
|
17951
19137
|
argPositions: [0]
|
|
17952
19138
|
},
|
|
19139
|
+
// XSS (writing to http.ResponseWriter without escaping)
|
|
17953
19140
|
{
|
|
17954
|
-
method: "
|
|
17955
|
-
|
|
17956
|
-
|
|
17957
|
-
|
|
19141
|
+
method: "Fprintf",
|
|
19142
|
+
class: "fmt",
|
|
19143
|
+
type: "xss",
|
|
19144
|
+
cwe: "CWE-79",
|
|
19145
|
+
severity: "high",
|
|
17958
19146
|
argPositions: [1]
|
|
17959
19147
|
},
|
|
17960
19148
|
{
|
|
17961
|
-
method: "
|
|
17962
|
-
|
|
17963
|
-
|
|
17964
|
-
|
|
17965
|
-
|
|
19149
|
+
method: "Write",
|
|
19150
|
+
class: "ResponseWriter",
|
|
19151
|
+
type: "xss",
|
|
19152
|
+
cwe: "CWE-79",
|
|
19153
|
+
severity: "high",
|
|
19154
|
+
argPositions: [0]
|
|
17966
19155
|
},
|
|
17967
|
-
// SSRF
|
|
19156
|
+
// SSRF
|
|
17968
19157
|
{
|
|
17969
|
-
method: "
|
|
19158
|
+
method: "Get",
|
|
19159
|
+
class: "http",
|
|
17970
19160
|
type: "ssrf",
|
|
17971
19161
|
cwe: "CWE-918",
|
|
17972
19162
|
severity: "high",
|
|
17973
19163
|
argPositions: [0]
|
|
17974
19164
|
},
|
|
17975
19165
|
{
|
|
17976
|
-
method: "
|
|
19166
|
+
method: "Post",
|
|
19167
|
+
class: "http",
|
|
17977
19168
|
type: "ssrf",
|
|
17978
19169
|
cwe: "CWE-918",
|
|
17979
19170
|
severity: "high",
|
|
19171
|
+
argPositions: [0, 2]
|
|
19172
|
+
},
|
|
19173
|
+
// Deserialization
|
|
19174
|
+
{
|
|
19175
|
+
method: "Unmarshal",
|
|
19176
|
+
class: "json",
|
|
19177
|
+
type: "deserialization",
|
|
19178
|
+
cwe: "CWE-502",
|
|
19179
|
+
severity: "medium",
|
|
19180
|
+
argPositions: [0]
|
|
19181
|
+
},
|
|
19182
|
+
{
|
|
19183
|
+
method: "Decode",
|
|
19184
|
+
class: "Decoder",
|
|
19185
|
+
type: "deserialization",
|
|
19186
|
+
cwe: "CWE-502",
|
|
19187
|
+
severity: "medium",
|
|
17980
19188
|
argPositions: [0]
|
|
17981
19189
|
}
|
|
17982
19190
|
];
|
|
17983
19191
|
}
|
|
17984
19192
|
/**
|
|
17985
|
-
*
|
|
19193
|
+
* Get receiver type from a Go call expression.
|
|
17986
19194
|
*/
|
|
17987
|
-
getReceiverType(
|
|
19195
|
+
getReceiverType(node, _context) {
|
|
19196
|
+
if (node.type !== "call_expression") return void 0;
|
|
19197
|
+
const func2 = node.childForFieldName("function");
|
|
19198
|
+
if (!func2) return void 0;
|
|
19199
|
+
if (func2.type === "selector_expression") {
|
|
19200
|
+
const operand = func2.childForFieldName("operand");
|
|
19201
|
+
if (operand) {
|
|
19202
|
+
return operand.text;
|
|
19203
|
+
}
|
|
19204
|
+
}
|
|
17988
19205
|
return void 0;
|
|
17989
19206
|
}
|
|
17990
19207
|
/**
|
|
17991
|
-
*
|
|
19208
|
+
* Check if node is a Go string literal.
|
|
17992
19209
|
*/
|
|
17993
19210
|
isStringLiteral(node) {
|
|
17994
|
-
return node.type === "
|
|
19211
|
+
return node.type === "interpreted_string_literal" || node.type === "raw_string_literal";
|
|
17995
19212
|
}
|
|
17996
19213
|
/**
|
|
17997
|
-
*
|
|
19214
|
+
* Get string value from Go string literal.
|
|
17998
19215
|
*/
|
|
17999
19216
|
getStringValue(node) {
|
|
18000
19217
|
if (!this.isStringLiteral(node)) return void 0;
|
|
18001
19218
|
const text = node.text;
|
|
18002
|
-
if (
|
|
19219
|
+
if (text.startsWith("`") && text.endsWith("`")) {
|
|
18003
19220
|
return text.slice(1, -1);
|
|
18004
19221
|
}
|
|
18005
|
-
if (
|
|
18006
|
-
return text.slice(
|
|
19222
|
+
if (text.startsWith('"') && text.endsWith('"')) {
|
|
19223
|
+
return text.slice(1, -1);
|
|
18007
19224
|
}
|
|
18008
|
-
const match = text.match(/^"(.*)"$/s);
|
|
18009
|
-
if (match) return match[1];
|
|
18010
19225
|
return text;
|
|
18011
19226
|
}
|
|
18012
|
-
|
|
18013
|
-
|
|
18014
|
-
|
|
18015
|
-
|
|
18016
|
-
|
|
18017
|
-
|
|
18018
|
-
|
|
18019
|
-
|
|
18020
|
-
|
|
18021
|
-
|
|
18022
|
-
|
|
18023
|
-
|
|
18024
|
-
|
|
18025
|
-
|
|
18026
|
-
|
|
18027
|
-
|
|
18028
|
-
|
|
18029
|
-
|
|
18030
|
-
|
|
18031
|
-
|
|
18032
|
-
|
|
18033
|
-
|
|
18034
|
-
|
|
18035
|
-
|
|
18036
|
-
|
|
18037
|
-
|
|
18038
|
-
|
|
18039
|
-
|
|
18040
|
-
|
|
18041
|
-
|
|
18042
|
-
|
|
18043
|
-
|
|
18044
|
-
|
|
18045
|
-
|
|
18046
|
-
|
|
18047
|
-
|
|
18048
|
-
|
|
18049
|
-
|
|
18050
|
-
|
|
18051
|
-
|
|
18052
|
-
|
|
18053
|
-
|
|
18054
|
-
|
|
18055
|
-
|
|
18056
|
-
|
|
18057
|
-
|
|
18058
|
-
|
|
18059
|
-
|
|
18060
|
-
|
|
18061
|
-
|
|
19227
|
+
/**
|
|
19228
|
+
* Extract type information from Go source.
|
|
19229
|
+
*/
|
|
19230
|
+
extractTypes(context) {
|
|
19231
|
+
const types = [];
|
|
19232
|
+
const root = context.tree.rootNode;
|
|
19233
|
+
const typeDecls = this.findNodes(root, "type_declaration");
|
|
19234
|
+
for (const decl of typeDecls) {
|
|
19235
|
+
for (let i2 = 0; i2 < decl.childCount; i2++) {
|
|
19236
|
+
const spec = decl.child(i2);
|
|
19237
|
+
if (!spec || spec.type !== "type_spec") continue;
|
|
19238
|
+
const nameNode = spec.childForFieldName("name");
|
|
19239
|
+
const typeNode = spec.childForFieldName("type");
|
|
19240
|
+
if (!nameNode || !typeNode) continue;
|
|
19241
|
+
const name2 = nameNode.text;
|
|
19242
|
+
const isInterface = typeNode.type === "interface_type";
|
|
19243
|
+
const isStruct = typeNode.type === "struct_type";
|
|
19244
|
+
if (isStruct || isInterface) {
|
|
19245
|
+
const fields = [];
|
|
19246
|
+
const methods = [];
|
|
19247
|
+
if (isStruct) {
|
|
19248
|
+
const fieldList = typeNode.childForFieldName("fields") ?? this.findChildByType(typeNode, "field_declaration_list");
|
|
19249
|
+
if (fieldList) {
|
|
19250
|
+
for (let j = 0; j < fieldList.childCount; j++) {
|
|
19251
|
+
const field = fieldList.child(j);
|
|
19252
|
+
if (!field || field.type !== "field_declaration") continue;
|
|
19253
|
+
const fieldName = field.childForFieldName("name");
|
|
19254
|
+
const fieldType = field.childForFieldName("type");
|
|
19255
|
+
if (fieldName) {
|
|
19256
|
+
fields.push({
|
|
19257
|
+
name: fieldName.text,
|
|
19258
|
+
type: fieldType?.text || null,
|
|
19259
|
+
modifiers: [],
|
|
19260
|
+
annotations: []
|
|
19261
|
+
});
|
|
19262
|
+
}
|
|
19263
|
+
}
|
|
19264
|
+
}
|
|
19265
|
+
}
|
|
19266
|
+
const methodDecls = this.findNodes(root, "method_declaration");
|
|
19267
|
+
for (const md of methodDecls) {
|
|
19268
|
+
const receiver = md.childForFieldName("receiver");
|
|
19269
|
+
if (!receiver) continue;
|
|
19270
|
+
const receiverText = receiver.text;
|
|
19271
|
+
if (receiverText.includes(name2)) {
|
|
19272
|
+
const methodName = md.childForFieldName("name");
|
|
19273
|
+
const params = md.childForFieldName("parameters");
|
|
19274
|
+
const result = md.childForFieldName("result");
|
|
19275
|
+
if (methodName) {
|
|
19276
|
+
methods.push({
|
|
19277
|
+
name: methodName.text,
|
|
19278
|
+
return_type: result?.text || null,
|
|
19279
|
+
parameters: params ? this.extractGoParams(params) : [],
|
|
19280
|
+
annotations: [],
|
|
19281
|
+
modifiers: [],
|
|
19282
|
+
start_line: md.startPosition.row + 1,
|
|
19283
|
+
end_line: md.endPosition.row + 1
|
|
19284
|
+
});
|
|
19285
|
+
}
|
|
19286
|
+
}
|
|
19287
|
+
}
|
|
19288
|
+
types.push({
|
|
19289
|
+
name: name2,
|
|
19290
|
+
kind: isInterface ? "interface" : "class",
|
|
19291
|
+
package: context.package || null,
|
|
19292
|
+
extends: null,
|
|
19293
|
+
implements: [],
|
|
19294
|
+
annotations: [],
|
|
19295
|
+
methods,
|
|
19296
|
+
fields,
|
|
19297
|
+
start_line: decl.startPosition.row + 1,
|
|
19298
|
+
end_line: decl.endPosition.row + 1
|
|
19299
|
+
});
|
|
19300
|
+
}
|
|
19301
|
+
}
|
|
19302
|
+
}
|
|
19303
|
+
return types;
|
|
18062
19304
|
}
|
|
18063
|
-
|
|
18064
|
-
|
|
19305
|
+
/**
|
|
19306
|
+
* Extract call information from Go source.
|
|
19307
|
+
*/
|
|
19308
|
+
extractCalls(context) {
|
|
19309
|
+
const calls = [];
|
|
19310
|
+
const root = context.tree.rootNode;
|
|
19311
|
+
const callExprs = this.findNodes(root, "call_expression");
|
|
19312
|
+
for (const call of callExprs) {
|
|
19313
|
+
const func2 = call.childForFieldName("function");
|
|
19314
|
+
if (!func2) continue;
|
|
19315
|
+
let methodName;
|
|
19316
|
+
let receiver = null;
|
|
19317
|
+
if (func2.type === "selector_expression") {
|
|
19318
|
+
const operand = func2.childForFieldName("operand");
|
|
19319
|
+
const field = func2.childForFieldName("field");
|
|
19320
|
+
receiver = operand?.text || null;
|
|
19321
|
+
methodName = field?.text || func2.text;
|
|
19322
|
+
} else {
|
|
19323
|
+
methodName = func2.text;
|
|
19324
|
+
}
|
|
19325
|
+
const args2 = call.childForFieldName("arguments");
|
|
19326
|
+
const argInfos = [];
|
|
19327
|
+
let argPos = 0;
|
|
19328
|
+
if (args2) {
|
|
19329
|
+
for (let i2 = 0; i2 < args2.childCount; i2++) {
|
|
19330
|
+
const arg = args2.child(i2);
|
|
19331
|
+
if (arg && arg.type !== "(" && arg.type !== ")" && arg.type !== ",") {
|
|
19332
|
+
argInfos.push({
|
|
19333
|
+
position: argPos++,
|
|
19334
|
+
expression: arg.text,
|
|
19335
|
+
variable: arg.type === "identifier" ? arg.text : null,
|
|
19336
|
+
literal: arg.type === "interpreted_string_literal" || arg.type === "int_literal" ? arg.text : null
|
|
19337
|
+
});
|
|
19338
|
+
}
|
|
19339
|
+
}
|
|
19340
|
+
}
|
|
19341
|
+
calls.push({
|
|
19342
|
+
method_name: methodName,
|
|
19343
|
+
receiver,
|
|
19344
|
+
arguments: argInfos,
|
|
19345
|
+
location: {
|
|
19346
|
+
line: call.startPosition.row + 1,
|
|
19347
|
+
column: call.startPosition.column
|
|
19348
|
+
}
|
|
19349
|
+
});
|
|
19350
|
+
}
|
|
19351
|
+
return calls;
|
|
18065
19352
|
}
|
|
18066
|
-
|
|
18067
|
-
|
|
19353
|
+
/**
|
|
19354
|
+
* Extract import information from Go source.
|
|
19355
|
+
*/
|
|
19356
|
+
extractImports(context) {
|
|
19357
|
+
const imports = [];
|
|
19358
|
+
const root = context.tree.rootNode;
|
|
19359
|
+
const importDecls = this.findNodes(root, "import_declaration");
|
|
19360
|
+
for (const decl of importDecls) {
|
|
19361
|
+
const singleSpec = this.findChildByType(decl, "import_spec");
|
|
19362
|
+
if (singleSpec) {
|
|
19363
|
+
const parsed = this.parseImportSpec(singleSpec);
|
|
19364
|
+
if (parsed) imports.push(parsed);
|
|
19365
|
+
continue;
|
|
19366
|
+
}
|
|
19367
|
+
const specList = this.findChildByType(decl, "import_spec_list");
|
|
19368
|
+
if (specList) {
|
|
19369
|
+
for (let i2 = 0; i2 < specList.childCount; i2++) {
|
|
19370
|
+
const spec = specList.child(i2);
|
|
19371
|
+
if (!spec || spec.type !== "import_spec") continue;
|
|
19372
|
+
const parsed = this.parseImportSpec(spec);
|
|
19373
|
+
if (parsed) imports.push(parsed);
|
|
19374
|
+
}
|
|
19375
|
+
}
|
|
19376
|
+
}
|
|
19377
|
+
return imports;
|
|
18068
19378
|
}
|
|
18069
|
-
|
|
19379
|
+
/**
|
|
19380
|
+
* Extract package name from Go source.
|
|
19381
|
+
*/
|
|
19382
|
+
extractPackage(context) {
|
|
19383
|
+
const root = context.tree.rootNode;
|
|
19384
|
+
const pkgClause = this.findChildByType(root, "package_clause");
|
|
19385
|
+
if (!pkgClause) return void 0;
|
|
19386
|
+
for (let i2 = 0; i2 < pkgClause.childCount; i2++) {
|
|
19387
|
+
const child = pkgClause.child(i2);
|
|
19388
|
+
if (child && child.type === "package_identifier") {
|
|
19389
|
+
return child.text;
|
|
19390
|
+
}
|
|
19391
|
+
}
|
|
18070
19392
|
return void 0;
|
|
18071
19393
|
}
|
|
18072
|
-
|
|
18073
|
-
|
|
18074
|
-
|
|
18075
|
-
|
|
18076
|
-
|
|
18077
|
-
|
|
18078
|
-
|
|
18079
|
-
|
|
19394
|
+
// ── Private helpers ─────────────────────────────────────────────────────
|
|
19395
|
+
parseImportSpec(spec) {
|
|
19396
|
+
let alias = null;
|
|
19397
|
+
let path;
|
|
19398
|
+
for (let i2 = 0; i2 < spec.childCount; i2++) {
|
|
19399
|
+
const child = spec.child(i2);
|
|
19400
|
+
if (!child) continue;
|
|
19401
|
+
if (child.type === "package_identifier" || child.type === "blank_identifier" || child.type === "dot") {
|
|
19402
|
+
alias = child.text;
|
|
19403
|
+
}
|
|
19404
|
+
if (child.type === "interpreted_string_literal") {
|
|
19405
|
+
path = child.text.slice(1, -1);
|
|
19406
|
+
}
|
|
18080
19407
|
}
|
|
18081
|
-
return
|
|
18082
|
-
|
|
18083
|
-
|
|
18084
|
-
|
|
18085
|
-
|
|
18086
|
-
|
|
18087
|
-
|
|
18088
|
-
|
|
18089
|
-
|
|
18090
|
-
return [];
|
|
19408
|
+
if (!path) return null;
|
|
19409
|
+
const shortName = alias || path.split("/").pop() || path;
|
|
19410
|
+
return {
|
|
19411
|
+
imported_name: shortName,
|
|
19412
|
+
from_package: path,
|
|
19413
|
+
alias,
|
|
19414
|
+
is_wildcard: alias === ".",
|
|
19415
|
+
line_number: spec.startPosition.row + 1
|
|
19416
|
+
};
|
|
18091
19417
|
}
|
|
18092
|
-
|
|
18093
|
-
|
|
19418
|
+
extractGoParams(params) {
|
|
19419
|
+
const result = [];
|
|
19420
|
+
for (let i2 = 0; i2 < params.childCount; i2++) {
|
|
19421
|
+
const param = params.child(i2);
|
|
19422
|
+
if (!param || param.type !== "parameter_declaration") continue;
|
|
19423
|
+
const nameNode = param.childForFieldName("name");
|
|
19424
|
+
const typeNode = param.childForFieldName("type");
|
|
19425
|
+
if (nameNode) {
|
|
19426
|
+
result.push({
|
|
19427
|
+
name: nameNode.text,
|
|
19428
|
+
type: typeNode?.text || null,
|
|
19429
|
+
annotations: []
|
|
19430
|
+
});
|
|
19431
|
+
}
|
|
19432
|
+
}
|
|
19433
|
+
return result;
|
|
18094
19434
|
}
|
|
18095
19435
|
};
|
|
18096
19436
|
|
|
@@ -18102,6 +19442,7 @@ function registerBuiltinPlugins() {
|
|
|
18102
19442
|
registerLanguage(new RustPlugin());
|
|
18103
19443
|
registerLanguage(new BashPlugin());
|
|
18104
19444
|
registerLanguage(new HtmlPlugin());
|
|
19445
|
+
registerLanguage(new GoPlugin());
|
|
18105
19446
|
}
|
|
18106
19447
|
|
|
18107
19448
|
// src/utils/logger.ts
|
|
@@ -24327,6 +25668,26 @@ function getNodeTypesForLanguage(language) {
|
|
|
24327
25668
|
"self_closing_tag",
|
|
24328
25669
|
"text"
|
|
24329
25670
|
]);
|
|
25671
|
+
case "go":
|
|
25672
|
+
return /* @__PURE__ */ new Set([
|
|
25673
|
+
"call_expression",
|
|
25674
|
+
"function_declaration",
|
|
25675
|
+
"method_declaration",
|
|
25676
|
+
"package_clause",
|
|
25677
|
+
"import_declaration",
|
|
25678
|
+
"import_spec",
|
|
25679
|
+
"var_declaration",
|
|
25680
|
+
"short_var_declaration",
|
|
25681
|
+
"assignment_statement",
|
|
25682
|
+
"type_declaration",
|
|
25683
|
+
"if_statement",
|
|
25684
|
+
"for_statement",
|
|
25685
|
+
"return_statement",
|
|
25686
|
+
"defer_statement",
|
|
25687
|
+
"go_statement",
|
|
25688
|
+
"selector_expression",
|
|
25689
|
+
"identifier"
|
|
25690
|
+
]);
|
|
24330
25691
|
default:
|
|
24331
25692
|
return /* @__PURE__ */ new Set([
|
|
24332
25693
|
"method_invocation",
|