circle-ir 3.21.0 → 3.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/configs/sinks/golang.json +144 -0
- package/configs/sources/golang.json +150 -0
- package/dist/analyzer.js +9 -0
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +1387 -183
- package/dist/core/circle-ir-core.cjs +610 -0
- package/dist/core/circle-ir-core.js +610 -0
- package/dist/core/extractors/calls.js +133 -0
- 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/package.json +1 -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
|
}
|
|
@@ -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 },
|
|
@@ -17886,211 +18496,784 @@ var BashPlugin = class extends BaseLanguagePlugin {
|
|
|
17886
18496
|
argPositions: [1]
|
|
17887
18497
|
},
|
|
17888
18498
|
{
|
|
17889
|
-
method: "zsh",
|
|
17890
|
-
type: "command_injection",
|
|
17891
|
-
cwe: "CWE-78",
|
|
17892
|
-
severity: "critical",
|
|
17893
|
-
argPositions: [1]
|
|
18499
|
+
method: "zsh",
|
|
18500
|
+
type: "command_injection",
|
|
18501
|
+
cwe: "CWE-78",
|
|
18502
|
+
severity: "critical",
|
|
18503
|
+
argPositions: [1]
|
|
18504
|
+
},
|
|
18505
|
+
{
|
|
18506
|
+
method: "ksh",
|
|
18507
|
+
type: "command_injection",
|
|
18508
|
+
cwe: "CWE-78",
|
|
18509
|
+
severity: "critical",
|
|
18510
|
+
argPositions: [1]
|
|
18511
|
+
},
|
|
18512
|
+
// SQL injection via DB CLI clients (first arg is query/expression)
|
|
18513
|
+
{
|
|
18514
|
+
method: "mysql",
|
|
18515
|
+
type: "sql_injection",
|
|
18516
|
+
cwe: "CWE-89",
|
|
18517
|
+
severity: "critical",
|
|
18518
|
+
argPositions: [1]
|
|
18519
|
+
},
|
|
18520
|
+
{
|
|
18521
|
+
method: "psql",
|
|
18522
|
+
type: "sql_injection",
|
|
18523
|
+
cwe: "CWE-89",
|
|
18524
|
+
severity: "critical",
|
|
18525
|
+
argPositions: [1]
|
|
18526
|
+
},
|
|
18527
|
+
{
|
|
18528
|
+
method: "sqlite3",
|
|
18529
|
+
type: "sql_injection",
|
|
18530
|
+
cwe: "CWE-89",
|
|
18531
|
+
severity: "critical",
|
|
18532
|
+
argPositions: [1]
|
|
18533
|
+
},
|
|
18534
|
+
// Path traversal via file operations (first arg is path)
|
|
18535
|
+
{
|
|
18536
|
+
method: "cat",
|
|
18537
|
+
type: "path_traversal",
|
|
18538
|
+
cwe: "CWE-22",
|
|
18539
|
+
severity: "high",
|
|
18540
|
+
argPositions: [0]
|
|
18541
|
+
},
|
|
18542
|
+
{
|
|
18543
|
+
method: "rm",
|
|
18544
|
+
type: "path_traversal",
|
|
18545
|
+
cwe: "CWE-22",
|
|
18546
|
+
severity: "high",
|
|
18547
|
+
argPositions: [0]
|
|
18548
|
+
},
|
|
18549
|
+
{
|
|
18550
|
+
method: "cp",
|
|
18551
|
+
type: "path_traversal",
|
|
18552
|
+
cwe: "CWE-22",
|
|
18553
|
+
severity: "high",
|
|
18554
|
+
argPositions: [0]
|
|
18555
|
+
},
|
|
18556
|
+
{
|
|
18557
|
+
method: "mv",
|
|
18558
|
+
type: "path_traversal",
|
|
18559
|
+
cwe: "CWE-22",
|
|
18560
|
+
severity: "high",
|
|
18561
|
+
argPositions: [0]
|
|
18562
|
+
},
|
|
18563
|
+
{
|
|
18564
|
+
method: "chmod",
|
|
18565
|
+
type: "path_traversal",
|
|
18566
|
+
cwe: "CWE-22",
|
|
18567
|
+
severity: "medium",
|
|
18568
|
+
argPositions: [1]
|
|
18569
|
+
},
|
|
18570
|
+
{
|
|
18571
|
+
method: "chown",
|
|
18572
|
+
type: "path_traversal",
|
|
18573
|
+
cwe: "CWE-22",
|
|
18574
|
+
severity: "medium",
|
|
18575
|
+
argPositions: [1]
|
|
18576
|
+
},
|
|
18577
|
+
// SSRF — curl/wget with externally-controlled URL
|
|
18578
|
+
{
|
|
18579
|
+
method: "curl",
|
|
18580
|
+
type: "ssrf",
|
|
18581
|
+
cwe: "CWE-918",
|
|
18582
|
+
severity: "high",
|
|
18583
|
+
argPositions: [0]
|
|
18584
|
+
},
|
|
18585
|
+
{
|
|
18586
|
+
method: "wget",
|
|
18587
|
+
type: "ssrf",
|
|
18588
|
+
cwe: "CWE-918",
|
|
18589
|
+
severity: "high",
|
|
18590
|
+
argPositions: [0]
|
|
18591
|
+
}
|
|
18592
|
+
];
|
|
18593
|
+
}
|
|
18594
|
+
/**
|
|
18595
|
+
* Shell has no OOP receiver types.
|
|
18596
|
+
*/
|
|
18597
|
+
getReceiverType(_node, _context) {
|
|
18598
|
+
return void 0;
|
|
18599
|
+
}
|
|
18600
|
+
/**
|
|
18601
|
+
* Bash string literals: quoted strings and raw ($'...') strings.
|
|
18602
|
+
*/
|
|
18603
|
+
isStringLiteral(node) {
|
|
18604
|
+
return node.type === "string" || node.type === "raw_string" || node.type === "ansi_c_string";
|
|
18605
|
+
}
|
|
18606
|
+
/**
|
|
18607
|
+
* Extract string value from bash string literal, stripping quotes.
|
|
18608
|
+
*/
|
|
18609
|
+
getStringValue(node) {
|
|
18610
|
+
if (!this.isStringLiteral(node)) return void 0;
|
|
18611
|
+
const text = node.text;
|
|
18612
|
+
if (node.type === "raw_string") {
|
|
18613
|
+
return text.slice(1, -1);
|
|
18614
|
+
}
|
|
18615
|
+
if (node.type === "ansi_c_string") {
|
|
18616
|
+
return text.slice(2, -1);
|
|
18617
|
+
}
|
|
18618
|
+
const match = text.match(/^"(.*)"$/s);
|
|
18619
|
+
if (match) return match[1];
|
|
18620
|
+
return text;
|
|
18621
|
+
}
|
|
18622
|
+
// Extraction methods — delegate to base extractors via generic walker
|
|
18623
|
+
extractTypes(_context) {
|
|
18624
|
+
return [];
|
|
18625
|
+
}
|
|
18626
|
+
extractCalls(_context) {
|
|
18627
|
+
return [];
|
|
18628
|
+
}
|
|
18629
|
+
extractImports(_context) {
|
|
18630
|
+
return [];
|
|
18631
|
+
}
|
|
18632
|
+
extractPackage(_context) {
|
|
18633
|
+
return void 0;
|
|
18634
|
+
}
|
|
18635
|
+
};
|
|
18636
|
+
|
|
18637
|
+
// src/languages/plugins/html.ts
|
|
18638
|
+
var HtmlPlugin = class extends BaseLanguagePlugin {
|
|
18639
|
+
id = "html";
|
|
18640
|
+
name = "HTML";
|
|
18641
|
+
extensions = [".html", ".htm", ".xhtml"];
|
|
18642
|
+
wasmPath = "tree-sitter-html.wasm";
|
|
18643
|
+
nodeTypes = {
|
|
18644
|
+
// HTML has no OOP constructs
|
|
18645
|
+
classDeclaration: [],
|
|
18646
|
+
interfaceDeclaration: [],
|
|
18647
|
+
enumDeclaration: [],
|
|
18648
|
+
functionDeclaration: [],
|
|
18649
|
+
methodDeclaration: [],
|
|
18650
|
+
// No expressions in HTML
|
|
18651
|
+
methodCall: [],
|
|
18652
|
+
functionCall: [],
|
|
18653
|
+
assignment: [],
|
|
18654
|
+
variableDeclaration: [],
|
|
18655
|
+
// No parameters
|
|
18656
|
+
parameter: [],
|
|
18657
|
+
argument: [],
|
|
18658
|
+
// No annotations
|
|
18659
|
+
annotation: [],
|
|
18660
|
+
decorator: [],
|
|
18661
|
+
// No imports
|
|
18662
|
+
importStatement: [],
|
|
18663
|
+
// No control flow
|
|
18664
|
+
ifStatement: [],
|
|
18665
|
+
forStatement: [],
|
|
18666
|
+
whileStatement: [],
|
|
18667
|
+
tryStatement: [],
|
|
18668
|
+
returnStatement: []
|
|
18669
|
+
};
|
|
18670
|
+
detectFramework(_context) {
|
|
18671
|
+
return void 0;
|
|
18672
|
+
}
|
|
18673
|
+
getBuiltinSources() {
|
|
18674
|
+
return [];
|
|
18675
|
+
}
|
|
18676
|
+
getBuiltinSinks() {
|
|
18677
|
+
return [];
|
|
18678
|
+
}
|
|
18679
|
+
getReceiverType(_node, _context) {
|
|
18680
|
+
return void 0;
|
|
18681
|
+
}
|
|
18682
|
+
isStringLiteral(node) {
|
|
18683
|
+
return node.type === "attribute_value" || node.type === "quoted_attribute_value";
|
|
18684
|
+
}
|
|
18685
|
+
getStringValue(node) {
|
|
18686
|
+
if (!this.isStringLiteral(node)) return void 0;
|
|
18687
|
+
const text = node.text;
|
|
18688
|
+
if (text.startsWith('"') && text.endsWith('"') || text.startsWith("'") && text.endsWith("'")) {
|
|
18689
|
+
return text.slice(1, -1);
|
|
18690
|
+
}
|
|
18691
|
+
return text;
|
|
18692
|
+
}
|
|
18693
|
+
extractTypes(_context) {
|
|
18694
|
+
return [];
|
|
18695
|
+
}
|
|
18696
|
+
extractCalls(_context) {
|
|
18697
|
+
return [];
|
|
18698
|
+
}
|
|
18699
|
+
extractImports(_context) {
|
|
18700
|
+
return [];
|
|
18701
|
+
}
|
|
18702
|
+
extractPackage(_context) {
|
|
18703
|
+
return void 0;
|
|
18704
|
+
}
|
|
18705
|
+
};
|
|
18706
|
+
|
|
18707
|
+
// src/languages/plugins/go.ts
|
|
18708
|
+
var GoPlugin = class extends BaseLanguagePlugin {
|
|
18709
|
+
id = "go";
|
|
18710
|
+
name = "Go";
|
|
18711
|
+
extensions = [".go"];
|
|
18712
|
+
wasmPath = "tree-sitter-go.wasm";
|
|
18713
|
+
nodeTypes = {
|
|
18714
|
+
// Type declarations
|
|
18715
|
+
classDeclaration: ["type_declaration"],
|
|
18716
|
+
interfaceDeclaration: ["type_declaration"],
|
|
18717
|
+
enumDeclaration: [],
|
|
18718
|
+
// Go has no enums (uses iota constants)
|
|
18719
|
+
functionDeclaration: ["function_declaration"],
|
|
18720
|
+
methodDeclaration: ["method_declaration"],
|
|
18721
|
+
// Expressions
|
|
18722
|
+
methodCall: ["call_expression"],
|
|
18723
|
+
functionCall: ["call_expression"],
|
|
18724
|
+
assignment: ["short_var_declaration", "assignment_statement", "var_declaration"],
|
|
18725
|
+
variableDeclaration: ["short_var_declaration", "var_declaration"],
|
|
18726
|
+
// Parameters and arguments
|
|
18727
|
+
parameter: ["parameter_declaration"],
|
|
18728
|
+
argument: ["argument_list"],
|
|
18729
|
+
// Annotations/decorators
|
|
18730
|
+
annotation: [],
|
|
18731
|
+
// Go has no annotations
|
|
18732
|
+
decorator: [],
|
|
18733
|
+
// Go has no decorators
|
|
18734
|
+
// Imports
|
|
18735
|
+
importStatement: ["import_declaration"],
|
|
18736
|
+
// Control flow
|
|
18737
|
+
ifStatement: ["if_statement"],
|
|
18738
|
+
forStatement: ["for_statement"],
|
|
18739
|
+
whileStatement: [],
|
|
18740
|
+
// Go uses for with condition
|
|
18741
|
+
tryStatement: [],
|
|
18742
|
+
// Go uses defer/recover
|
|
18743
|
+
returnStatement: ["return_statement"]
|
|
18744
|
+
};
|
|
18745
|
+
/**
|
|
18746
|
+
* Detect Go web frameworks from imports.
|
|
18747
|
+
*/
|
|
18748
|
+
detectFramework(context) {
|
|
18749
|
+
const indicators = [];
|
|
18750
|
+
let framework;
|
|
18751
|
+
let confidence = 0;
|
|
18752
|
+
for (const imp of context.imports) {
|
|
18753
|
+
const path = imp.from_package || imp.imported_name;
|
|
18754
|
+
if (path.includes("gin-gonic/gin")) {
|
|
18755
|
+
framework = "gin";
|
|
18756
|
+
confidence = Math.max(confidence, 0.95);
|
|
18757
|
+
indicators.push(`import: ${path}`);
|
|
18758
|
+
}
|
|
18759
|
+
if (path.includes("labstack/echo")) {
|
|
18760
|
+
framework = "echo";
|
|
18761
|
+
confidence = Math.max(confidence, 0.95);
|
|
18762
|
+
indicators.push(`import: ${path}`);
|
|
18763
|
+
}
|
|
18764
|
+
if (path.includes("gofiber/fiber")) {
|
|
18765
|
+
framework = "fiber";
|
|
18766
|
+
confidence = Math.max(confidence, 0.95);
|
|
18767
|
+
indicators.push(`import: ${path}`);
|
|
18768
|
+
}
|
|
18769
|
+
if (path.includes("go-chi/chi")) {
|
|
18770
|
+
framework = "chi";
|
|
18771
|
+
confidence = Math.max(confidence, 0.9);
|
|
18772
|
+
indicators.push(`import: ${path}`);
|
|
18773
|
+
}
|
|
18774
|
+
if (path.includes("gorm.io/gorm")) {
|
|
18775
|
+
indicators.push(`import: ${path}`);
|
|
18776
|
+
}
|
|
18777
|
+
if (path === "net/http") {
|
|
18778
|
+
framework = framework || "net/http";
|
|
18779
|
+
confidence = Math.max(confidence, 0.8);
|
|
18780
|
+
indicators.push(`import: ${path}`);
|
|
18781
|
+
}
|
|
18782
|
+
}
|
|
18783
|
+
if (framework) {
|
|
18784
|
+
return { name: framework, confidence, indicators };
|
|
18785
|
+
}
|
|
18786
|
+
return void 0;
|
|
18787
|
+
}
|
|
18788
|
+
/**
|
|
18789
|
+
* Go taint source patterns.
|
|
18790
|
+
*/
|
|
18791
|
+
getBuiltinSources() {
|
|
18792
|
+
return [
|
|
18793
|
+
// net/http request methods
|
|
18794
|
+
{
|
|
18795
|
+
method: "FormValue",
|
|
18796
|
+
class: "Request",
|
|
18797
|
+
type: "http_param",
|
|
18798
|
+
severity: "high",
|
|
18799
|
+
confidence: 0.95,
|
|
18800
|
+
returnTainted: true
|
|
18801
|
+
},
|
|
18802
|
+
{
|
|
18803
|
+
method: "PostFormValue",
|
|
18804
|
+
class: "Request",
|
|
18805
|
+
type: "http_param",
|
|
18806
|
+
severity: "high",
|
|
18807
|
+
confidence: 0.95,
|
|
18808
|
+
returnTainted: true
|
|
18809
|
+
},
|
|
18810
|
+
{
|
|
18811
|
+
method: "Query",
|
|
18812
|
+
class: "URL",
|
|
18813
|
+
type: "http_param",
|
|
18814
|
+
severity: "high",
|
|
18815
|
+
confidence: 0.95,
|
|
18816
|
+
returnTainted: true
|
|
18817
|
+
},
|
|
18818
|
+
{
|
|
18819
|
+
method: "Get",
|
|
18820
|
+
class: "Header",
|
|
18821
|
+
type: "http_header",
|
|
18822
|
+
severity: "high",
|
|
18823
|
+
confidence: 0.9,
|
|
18824
|
+
returnTainted: true
|
|
18825
|
+
},
|
|
18826
|
+
{
|
|
18827
|
+
method: "Cookie",
|
|
18828
|
+
class: "Request",
|
|
18829
|
+
type: "http_cookie",
|
|
18830
|
+
severity: "high",
|
|
18831
|
+
confidence: 0.9,
|
|
18832
|
+
returnTainted: true
|
|
18833
|
+
},
|
|
18834
|
+
// Gin framework
|
|
18835
|
+
{
|
|
18836
|
+
method: "Query",
|
|
18837
|
+
class: "Context",
|
|
18838
|
+
type: "http_param",
|
|
18839
|
+
severity: "high",
|
|
18840
|
+
confidence: 0.95,
|
|
18841
|
+
returnTainted: true
|
|
18842
|
+
},
|
|
18843
|
+
{
|
|
18844
|
+
method: "Param",
|
|
18845
|
+
class: "Context",
|
|
18846
|
+
type: "http_path",
|
|
18847
|
+
severity: "high",
|
|
18848
|
+
confidence: 0.95,
|
|
18849
|
+
returnTainted: true
|
|
18850
|
+
},
|
|
18851
|
+
{
|
|
18852
|
+
method: "PostForm",
|
|
18853
|
+
class: "Context",
|
|
18854
|
+
type: "http_param",
|
|
18855
|
+
severity: "high",
|
|
18856
|
+
confidence: 0.95,
|
|
18857
|
+
returnTainted: true
|
|
18858
|
+
},
|
|
18859
|
+
{
|
|
18860
|
+
method: "GetRawData",
|
|
18861
|
+
class: "Context",
|
|
18862
|
+
type: "http_body",
|
|
18863
|
+
severity: "high",
|
|
18864
|
+
confidence: 0.95,
|
|
18865
|
+
returnTainted: true
|
|
18866
|
+
},
|
|
18867
|
+
// Standard library I/O
|
|
18868
|
+
{
|
|
18869
|
+
method: "Getenv",
|
|
18870
|
+
class: "os",
|
|
18871
|
+
type: "env_var",
|
|
18872
|
+
severity: "medium",
|
|
18873
|
+
confidence: 0.85,
|
|
18874
|
+
returnTainted: true
|
|
18875
|
+
},
|
|
18876
|
+
{
|
|
18877
|
+
method: "ReadAll",
|
|
18878
|
+
class: "io",
|
|
18879
|
+
type: "io_input",
|
|
18880
|
+
severity: "medium",
|
|
18881
|
+
confidence: 0.8,
|
|
18882
|
+
returnTainted: true
|
|
18883
|
+
},
|
|
18884
|
+
{
|
|
18885
|
+
method: "ReadFile",
|
|
18886
|
+
class: "os",
|
|
18887
|
+
type: "file_input",
|
|
18888
|
+
severity: "medium",
|
|
18889
|
+
confidence: 0.8,
|
|
18890
|
+
returnTainted: true
|
|
17894
18891
|
},
|
|
17895
18892
|
{
|
|
17896
|
-
method: "
|
|
17897
|
-
|
|
17898
|
-
|
|
18893
|
+
method: "Text",
|
|
18894
|
+
class: "Scanner",
|
|
18895
|
+
type: "io_input",
|
|
18896
|
+
severity: "medium",
|
|
18897
|
+
confidence: 0.8,
|
|
18898
|
+
returnTainted: true
|
|
18899
|
+
}
|
|
18900
|
+
];
|
|
18901
|
+
}
|
|
18902
|
+
/**
|
|
18903
|
+
* Go taint sink patterns.
|
|
18904
|
+
*/
|
|
18905
|
+
getBuiltinSinks() {
|
|
18906
|
+
return [
|
|
18907
|
+
// SQL Injection
|
|
18908
|
+
{
|
|
18909
|
+
method: "Query",
|
|
18910
|
+
class: "DB",
|
|
18911
|
+
type: "sql_injection",
|
|
18912
|
+
cwe: "CWE-89",
|
|
17899
18913
|
severity: "critical",
|
|
17900
|
-
argPositions: [
|
|
18914
|
+
argPositions: [0]
|
|
17901
18915
|
},
|
|
17902
|
-
// SQL injection via DB CLI clients (first arg is query/expression)
|
|
17903
18916
|
{
|
|
17904
|
-
method: "
|
|
18917
|
+
method: "QueryRow",
|
|
18918
|
+
class: "DB",
|
|
17905
18919
|
type: "sql_injection",
|
|
17906
18920
|
cwe: "CWE-89",
|
|
17907
18921
|
severity: "critical",
|
|
17908
|
-
argPositions: [
|
|
18922
|
+
argPositions: [0]
|
|
17909
18923
|
},
|
|
17910
18924
|
{
|
|
17911
|
-
method: "
|
|
18925
|
+
method: "Exec",
|
|
18926
|
+
class: "DB",
|
|
17912
18927
|
type: "sql_injection",
|
|
17913
18928
|
cwe: "CWE-89",
|
|
17914
18929
|
severity: "critical",
|
|
17915
|
-
argPositions: [
|
|
18930
|
+
argPositions: [0]
|
|
17916
18931
|
},
|
|
17917
18932
|
{
|
|
17918
|
-
method: "
|
|
18933
|
+
method: "Query",
|
|
18934
|
+
class: "Tx",
|
|
17919
18935
|
type: "sql_injection",
|
|
17920
18936
|
cwe: "CWE-89",
|
|
17921
18937
|
severity: "critical",
|
|
17922
|
-
argPositions: [
|
|
18938
|
+
argPositions: [0]
|
|
17923
18939
|
},
|
|
17924
|
-
//
|
|
18940
|
+
// Command Injection
|
|
17925
18941
|
{
|
|
17926
|
-
method: "
|
|
17927
|
-
|
|
17928
|
-
|
|
17929
|
-
|
|
18942
|
+
method: "Command",
|
|
18943
|
+
class: "exec",
|
|
18944
|
+
type: "command_injection",
|
|
18945
|
+
cwe: "CWE-78",
|
|
18946
|
+
severity: "critical",
|
|
17930
18947
|
argPositions: [0]
|
|
17931
18948
|
},
|
|
17932
18949
|
{
|
|
17933
|
-
method: "
|
|
18950
|
+
method: "CommandContext",
|
|
18951
|
+
class: "exec",
|
|
18952
|
+
type: "command_injection",
|
|
18953
|
+
cwe: "CWE-78",
|
|
18954
|
+
severity: "critical",
|
|
18955
|
+
argPositions: [1]
|
|
18956
|
+
},
|
|
18957
|
+
// Path Traversal
|
|
18958
|
+
{
|
|
18959
|
+
method: "Open",
|
|
18960
|
+
class: "os",
|
|
17934
18961
|
type: "path_traversal",
|
|
17935
18962
|
cwe: "CWE-22",
|
|
17936
18963
|
severity: "high",
|
|
17937
18964
|
argPositions: [0]
|
|
17938
18965
|
},
|
|
17939
18966
|
{
|
|
17940
|
-
method: "
|
|
18967
|
+
method: "ReadFile",
|
|
18968
|
+
class: "os",
|
|
17941
18969
|
type: "path_traversal",
|
|
17942
18970
|
cwe: "CWE-22",
|
|
17943
18971
|
severity: "high",
|
|
17944
18972
|
argPositions: [0]
|
|
17945
18973
|
},
|
|
17946
18974
|
{
|
|
17947
|
-
method: "
|
|
18975
|
+
method: "WriteFile",
|
|
18976
|
+
class: "os",
|
|
17948
18977
|
type: "path_traversal",
|
|
17949
18978
|
cwe: "CWE-22",
|
|
17950
18979
|
severity: "high",
|
|
17951
18980
|
argPositions: [0]
|
|
17952
18981
|
},
|
|
18982
|
+
// XSS (writing to http.ResponseWriter without escaping)
|
|
17953
18983
|
{
|
|
17954
|
-
method: "
|
|
17955
|
-
|
|
17956
|
-
|
|
17957
|
-
|
|
18984
|
+
method: "Fprintf",
|
|
18985
|
+
class: "fmt",
|
|
18986
|
+
type: "xss",
|
|
18987
|
+
cwe: "CWE-79",
|
|
18988
|
+
severity: "high",
|
|
17958
18989
|
argPositions: [1]
|
|
17959
18990
|
},
|
|
17960
18991
|
{
|
|
17961
|
-
method: "
|
|
17962
|
-
|
|
17963
|
-
|
|
17964
|
-
|
|
17965
|
-
|
|
18992
|
+
method: "Write",
|
|
18993
|
+
class: "ResponseWriter",
|
|
18994
|
+
type: "xss",
|
|
18995
|
+
cwe: "CWE-79",
|
|
18996
|
+
severity: "high",
|
|
18997
|
+
argPositions: [0]
|
|
17966
18998
|
},
|
|
17967
|
-
// SSRF
|
|
18999
|
+
// SSRF
|
|
17968
19000
|
{
|
|
17969
|
-
method: "
|
|
19001
|
+
method: "Get",
|
|
19002
|
+
class: "http",
|
|
17970
19003
|
type: "ssrf",
|
|
17971
19004
|
cwe: "CWE-918",
|
|
17972
19005
|
severity: "high",
|
|
17973
19006
|
argPositions: [0]
|
|
17974
19007
|
},
|
|
17975
19008
|
{
|
|
17976
|
-
method: "
|
|
19009
|
+
method: "Post",
|
|
19010
|
+
class: "http",
|
|
17977
19011
|
type: "ssrf",
|
|
17978
19012
|
cwe: "CWE-918",
|
|
17979
19013
|
severity: "high",
|
|
19014
|
+
argPositions: [0, 2]
|
|
19015
|
+
},
|
|
19016
|
+
// Deserialization
|
|
19017
|
+
{
|
|
19018
|
+
method: "Unmarshal",
|
|
19019
|
+
class: "json",
|
|
19020
|
+
type: "deserialization",
|
|
19021
|
+
cwe: "CWE-502",
|
|
19022
|
+
severity: "medium",
|
|
19023
|
+
argPositions: [0]
|
|
19024
|
+
},
|
|
19025
|
+
{
|
|
19026
|
+
method: "Decode",
|
|
19027
|
+
class: "Decoder",
|
|
19028
|
+
type: "deserialization",
|
|
19029
|
+
cwe: "CWE-502",
|
|
19030
|
+
severity: "medium",
|
|
17980
19031
|
argPositions: [0]
|
|
17981
19032
|
}
|
|
17982
19033
|
];
|
|
17983
19034
|
}
|
|
17984
19035
|
/**
|
|
17985
|
-
*
|
|
19036
|
+
* Get receiver type from a Go call expression.
|
|
17986
19037
|
*/
|
|
17987
|
-
getReceiverType(
|
|
19038
|
+
getReceiverType(node, _context) {
|
|
19039
|
+
if (node.type !== "call_expression") return void 0;
|
|
19040
|
+
const func2 = node.childForFieldName("function");
|
|
19041
|
+
if (!func2) return void 0;
|
|
19042
|
+
if (func2.type === "selector_expression") {
|
|
19043
|
+
const operand = func2.childForFieldName("operand");
|
|
19044
|
+
if (operand) {
|
|
19045
|
+
return operand.text;
|
|
19046
|
+
}
|
|
19047
|
+
}
|
|
17988
19048
|
return void 0;
|
|
17989
19049
|
}
|
|
17990
19050
|
/**
|
|
17991
|
-
*
|
|
19051
|
+
* Check if node is a Go string literal.
|
|
17992
19052
|
*/
|
|
17993
19053
|
isStringLiteral(node) {
|
|
17994
|
-
return node.type === "
|
|
19054
|
+
return node.type === "interpreted_string_literal" || node.type === "raw_string_literal";
|
|
17995
19055
|
}
|
|
17996
19056
|
/**
|
|
17997
|
-
*
|
|
19057
|
+
* Get string value from Go string literal.
|
|
17998
19058
|
*/
|
|
17999
19059
|
getStringValue(node) {
|
|
18000
19060
|
if (!this.isStringLiteral(node)) return void 0;
|
|
18001
19061
|
const text = node.text;
|
|
18002
|
-
if (
|
|
19062
|
+
if (text.startsWith("`") && text.endsWith("`")) {
|
|
18003
19063
|
return text.slice(1, -1);
|
|
18004
19064
|
}
|
|
18005
|
-
if (
|
|
18006
|
-
return text.slice(
|
|
19065
|
+
if (text.startsWith('"') && text.endsWith('"')) {
|
|
19066
|
+
return text.slice(1, -1);
|
|
18007
19067
|
}
|
|
18008
|
-
const match = text.match(/^"(.*)"$/s);
|
|
18009
|
-
if (match) return match[1];
|
|
18010
19068
|
return text;
|
|
18011
19069
|
}
|
|
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
|
-
|
|
19070
|
+
/**
|
|
19071
|
+
* Extract type information from Go source.
|
|
19072
|
+
*/
|
|
19073
|
+
extractTypes(context) {
|
|
19074
|
+
const types = [];
|
|
19075
|
+
const root = context.tree.rootNode;
|
|
19076
|
+
const typeDecls = this.findNodes(root, "type_declaration");
|
|
19077
|
+
for (const decl of typeDecls) {
|
|
19078
|
+
for (let i2 = 0; i2 < decl.childCount; i2++) {
|
|
19079
|
+
const spec = decl.child(i2);
|
|
19080
|
+
if (!spec || spec.type !== "type_spec") continue;
|
|
19081
|
+
const nameNode = spec.childForFieldName("name");
|
|
19082
|
+
const typeNode = spec.childForFieldName("type");
|
|
19083
|
+
if (!nameNode || !typeNode) continue;
|
|
19084
|
+
const name2 = nameNode.text;
|
|
19085
|
+
const isInterface = typeNode.type === "interface_type";
|
|
19086
|
+
const isStruct = typeNode.type === "struct_type";
|
|
19087
|
+
if (isStruct || isInterface) {
|
|
19088
|
+
const fields = [];
|
|
19089
|
+
const methods = [];
|
|
19090
|
+
if (isStruct) {
|
|
19091
|
+
const fieldList = typeNode.childForFieldName("fields") ?? this.findChildByType(typeNode, "field_declaration_list");
|
|
19092
|
+
if (fieldList) {
|
|
19093
|
+
for (let j = 0; j < fieldList.childCount; j++) {
|
|
19094
|
+
const field = fieldList.child(j);
|
|
19095
|
+
if (!field || field.type !== "field_declaration") continue;
|
|
19096
|
+
const fieldName = field.childForFieldName("name");
|
|
19097
|
+
const fieldType = field.childForFieldName("type");
|
|
19098
|
+
if (fieldName) {
|
|
19099
|
+
fields.push({
|
|
19100
|
+
name: fieldName.text,
|
|
19101
|
+
type: fieldType?.text || null,
|
|
19102
|
+
modifiers: [],
|
|
19103
|
+
annotations: []
|
|
19104
|
+
});
|
|
19105
|
+
}
|
|
19106
|
+
}
|
|
19107
|
+
}
|
|
19108
|
+
}
|
|
19109
|
+
const methodDecls = this.findNodes(root, "method_declaration");
|
|
19110
|
+
for (const md of methodDecls) {
|
|
19111
|
+
const receiver = md.childForFieldName("receiver");
|
|
19112
|
+
if (!receiver) continue;
|
|
19113
|
+
const receiverText = receiver.text;
|
|
19114
|
+
if (receiverText.includes(name2)) {
|
|
19115
|
+
const methodName = md.childForFieldName("name");
|
|
19116
|
+
const params = md.childForFieldName("parameters");
|
|
19117
|
+
const result = md.childForFieldName("result");
|
|
19118
|
+
if (methodName) {
|
|
19119
|
+
methods.push({
|
|
19120
|
+
name: methodName.text,
|
|
19121
|
+
return_type: result?.text || null,
|
|
19122
|
+
parameters: params ? this.extractGoParams(params) : [],
|
|
19123
|
+
annotations: [],
|
|
19124
|
+
modifiers: [],
|
|
19125
|
+
start_line: md.startPosition.row + 1,
|
|
19126
|
+
end_line: md.endPosition.row + 1
|
|
19127
|
+
});
|
|
19128
|
+
}
|
|
19129
|
+
}
|
|
19130
|
+
}
|
|
19131
|
+
types.push({
|
|
19132
|
+
name: name2,
|
|
19133
|
+
kind: isInterface ? "interface" : "class",
|
|
19134
|
+
package: context.package || null,
|
|
19135
|
+
extends: null,
|
|
19136
|
+
implements: [],
|
|
19137
|
+
annotations: [],
|
|
19138
|
+
methods,
|
|
19139
|
+
fields,
|
|
19140
|
+
start_line: decl.startPosition.row + 1,
|
|
19141
|
+
end_line: decl.endPosition.row + 1
|
|
19142
|
+
});
|
|
19143
|
+
}
|
|
19144
|
+
}
|
|
19145
|
+
}
|
|
19146
|
+
return types;
|
|
18062
19147
|
}
|
|
18063
|
-
|
|
18064
|
-
|
|
19148
|
+
/**
|
|
19149
|
+
* Extract call information from Go source.
|
|
19150
|
+
*/
|
|
19151
|
+
extractCalls(context) {
|
|
19152
|
+
const calls = [];
|
|
19153
|
+
const root = context.tree.rootNode;
|
|
19154
|
+
const callExprs = this.findNodes(root, "call_expression");
|
|
19155
|
+
for (const call of callExprs) {
|
|
19156
|
+
const func2 = call.childForFieldName("function");
|
|
19157
|
+
if (!func2) continue;
|
|
19158
|
+
let methodName;
|
|
19159
|
+
let receiver = null;
|
|
19160
|
+
if (func2.type === "selector_expression") {
|
|
19161
|
+
const operand = func2.childForFieldName("operand");
|
|
19162
|
+
const field = func2.childForFieldName("field");
|
|
19163
|
+
receiver = operand?.text || null;
|
|
19164
|
+
methodName = field?.text || func2.text;
|
|
19165
|
+
} else {
|
|
19166
|
+
methodName = func2.text;
|
|
19167
|
+
}
|
|
19168
|
+
const args2 = call.childForFieldName("arguments");
|
|
19169
|
+
const argInfos = [];
|
|
19170
|
+
let argPos = 0;
|
|
19171
|
+
if (args2) {
|
|
19172
|
+
for (let i2 = 0; i2 < args2.childCount; i2++) {
|
|
19173
|
+
const arg = args2.child(i2);
|
|
19174
|
+
if (arg && arg.type !== "(" && arg.type !== ")" && arg.type !== ",") {
|
|
19175
|
+
argInfos.push({
|
|
19176
|
+
position: argPos++,
|
|
19177
|
+
expression: arg.text,
|
|
19178
|
+
variable: arg.type === "identifier" ? arg.text : null,
|
|
19179
|
+
literal: arg.type === "interpreted_string_literal" || arg.type === "int_literal" ? arg.text : null
|
|
19180
|
+
});
|
|
19181
|
+
}
|
|
19182
|
+
}
|
|
19183
|
+
}
|
|
19184
|
+
calls.push({
|
|
19185
|
+
method_name: methodName,
|
|
19186
|
+
receiver,
|
|
19187
|
+
arguments: argInfos,
|
|
19188
|
+
location: {
|
|
19189
|
+
line: call.startPosition.row + 1,
|
|
19190
|
+
column: call.startPosition.column
|
|
19191
|
+
}
|
|
19192
|
+
});
|
|
19193
|
+
}
|
|
19194
|
+
return calls;
|
|
18065
19195
|
}
|
|
18066
|
-
|
|
18067
|
-
|
|
19196
|
+
/**
|
|
19197
|
+
* Extract import information from Go source.
|
|
19198
|
+
*/
|
|
19199
|
+
extractImports(context) {
|
|
19200
|
+
const imports = [];
|
|
19201
|
+
const root = context.tree.rootNode;
|
|
19202
|
+
const importDecls = this.findNodes(root, "import_declaration");
|
|
19203
|
+
for (const decl of importDecls) {
|
|
19204
|
+
const singleSpec = this.findChildByType(decl, "import_spec");
|
|
19205
|
+
if (singleSpec) {
|
|
19206
|
+
const parsed = this.parseImportSpec(singleSpec);
|
|
19207
|
+
if (parsed) imports.push(parsed);
|
|
19208
|
+
continue;
|
|
19209
|
+
}
|
|
19210
|
+
const specList = this.findChildByType(decl, "import_spec_list");
|
|
19211
|
+
if (specList) {
|
|
19212
|
+
for (let i2 = 0; i2 < specList.childCount; i2++) {
|
|
19213
|
+
const spec = specList.child(i2);
|
|
19214
|
+
if (!spec || spec.type !== "import_spec") continue;
|
|
19215
|
+
const parsed = this.parseImportSpec(spec);
|
|
19216
|
+
if (parsed) imports.push(parsed);
|
|
19217
|
+
}
|
|
19218
|
+
}
|
|
19219
|
+
}
|
|
19220
|
+
return imports;
|
|
18068
19221
|
}
|
|
18069
|
-
|
|
19222
|
+
/**
|
|
19223
|
+
* Extract package name from Go source.
|
|
19224
|
+
*/
|
|
19225
|
+
extractPackage(context) {
|
|
19226
|
+
const root = context.tree.rootNode;
|
|
19227
|
+
const pkgClause = this.findChildByType(root, "package_clause");
|
|
19228
|
+
if (!pkgClause) return void 0;
|
|
19229
|
+
for (let i2 = 0; i2 < pkgClause.childCount; i2++) {
|
|
19230
|
+
const child = pkgClause.child(i2);
|
|
19231
|
+
if (child && child.type === "package_identifier") {
|
|
19232
|
+
return child.text;
|
|
19233
|
+
}
|
|
19234
|
+
}
|
|
18070
19235
|
return void 0;
|
|
18071
19236
|
}
|
|
18072
|
-
|
|
18073
|
-
|
|
18074
|
-
|
|
18075
|
-
|
|
18076
|
-
|
|
18077
|
-
|
|
18078
|
-
|
|
18079
|
-
|
|
19237
|
+
// ── Private helpers ─────────────────────────────────────────────────────
|
|
19238
|
+
parseImportSpec(spec) {
|
|
19239
|
+
let alias = null;
|
|
19240
|
+
let path;
|
|
19241
|
+
for (let i2 = 0; i2 < spec.childCount; i2++) {
|
|
19242
|
+
const child = spec.child(i2);
|
|
19243
|
+
if (!child) continue;
|
|
19244
|
+
if (child.type === "package_identifier" || child.type === "blank_identifier" || child.type === "dot") {
|
|
19245
|
+
alias = child.text;
|
|
19246
|
+
}
|
|
19247
|
+
if (child.type === "interpreted_string_literal") {
|
|
19248
|
+
path = child.text.slice(1, -1);
|
|
19249
|
+
}
|
|
18080
19250
|
}
|
|
18081
|
-
return
|
|
18082
|
-
|
|
18083
|
-
|
|
18084
|
-
|
|
18085
|
-
|
|
18086
|
-
|
|
18087
|
-
|
|
18088
|
-
|
|
18089
|
-
|
|
18090
|
-
return [];
|
|
19251
|
+
if (!path) return null;
|
|
19252
|
+
const shortName = alias || path.split("/").pop() || path;
|
|
19253
|
+
return {
|
|
19254
|
+
imported_name: shortName,
|
|
19255
|
+
from_package: path,
|
|
19256
|
+
alias,
|
|
19257
|
+
is_wildcard: alias === ".",
|
|
19258
|
+
line_number: spec.startPosition.row + 1
|
|
19259
|
+
};
|
|
18091
19260
|
}
|
|
18092
|
-
|
|
18093
|
-
|
|
19261
|
+
extractGoParams(params) {
|
|
19262
|
+
const result = [];
|
|
19263
|
+
for (let i2 = 0; i2 < params.childCount; i2++) {
|
|
19264
|
+
const param = params.child(i2);
|
|
19265
|
+
if (!param || param.type !== "parameter_declaration") continue;
|
|
19266
|
+
const nameNode = param.childForFieldName("name");
|
|
19267
|
+
const typeNode = param.childForFieldName("type");
|
|
19268
|
+
if (nameNode) {
|
|
19269
|
+
result.push({
|
|
19270
|
+
name: nameNode.text,
|
|
19271
|
+
type: typeNode?.text || null,
|
|
19272
|
+
annotations: []
|
|
19273
|
+
});
|
|
19274
|
+
}
|
|
19275
|
+
}
|
|
19276
|
+
return result;
|
|
18094
19277
|
}
|
|
18095
19278
|
};
|
|
18096
19279
|
|
|
@@ -18102,6 +19285,7 @@ function registerBuiltinPlugins() {
|
|
|
18102
19285
|
registerLanguage(new RustPlugin());
|
|
18103
19286
|
registerLanguage(new BashPlugin());
|
|
18104
19287
|
registerLanguage(new HtmlPlugin());
|
|
19288
|
+
registerLanguage(new GoPlugin());
|
|
18105
19289
|
}
|
|
18106
19290
|
|
|
18107
19291
|
// src/utils/logger.ts
|
|
@@ -24327,6 +25511,26 @@ function getNodeTypesForLanguage(language) {
|
|
|
24327
25511
|
"self_closing_tag",
|
|
24328
25512
|
"text"
|
|
24329
25513
|
]);
|
|
25514
|
+
case "go":
|
|
25515
|
+
return /* @__PURE__ */ new Set([
|
|
25516
|
+
"call_expression",
|
|
25517
|
+
"function_declaration",
|
|
25518
|
+
"method_declaration",
|
|
25519
|
+
"package_clause",
|
|
25520
|
+
"import_declaration",
|
|
25521
|
+
"import_spec",
|
|
25522
|
+
"var_declaration",
|
|
25523
|
+
"short_var_declaration",
|
|
25524
|
+
"assignment_statement",
|
|
25525
|
+
"type_declaration",
|
|
25526
|
+
"if_statement",
|
|
25527
|
+
"for_statement",
|
|
25528
|
+
"return_statement",
|
|
25529
|
+
"defer_statement",
|
|
25530
|
+
"go_statement",
|
|
25531
|
+
"selector_expression",
|
|
25532
|
+
"identifier"
|
|
25533
|
+
]);
|
|
24330
25534
|
default:
|
|
24331
25535
|
return /* @__PURE__ */ new Set([
|
|
24332
25536
|
"method_invocation",
|