circle-ir 3.20.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/analysis/passes/language-sources-pass.js +106 -1
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analyzer.js +9 -0
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +1444 -179
- package/dist/core/circle-ir-core.cjs +621 -36
- package/dist/core/circle-ir-core.js +621 -36
- 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 +285 -42
- 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) {
|
|
@@ -8708,6 +9065,17 @@ function buildBashDFG(tree) {
|
|
|
8708
9065
|
let defIdCounter = 1;
|
|
8709
9066
|
let useIdCounter = 1;
|
|
8710
9067
|
const scopeStack = [/* @__PURE__ */ new Map()];
|
|
9068
|
+
const positionalParams = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*"];
|
|
9069
|
+
for (const p of positionalParams) {
|
|
9070
|
+
const def = {
|
|
9071
|
+
id: defIdCounter++,
|
|
9072
|
+
variable: p,
|
|
9073
|
+
line: 0,
|
|
9074
|
+
kind: "param"
|
|
9075
|
+
};
|
|
9076
|
+
defs.push(def);
|
|
9077
|
+
currentScope(scopeStack).set(p, def.id);
|
|
9078
|
+
}
|
|
8711
9079
|
walkTree(tree.rootNode, (node) => {
|
|
8712
9080
|
if (node.type === "variable_assignment") {
|
|
8713
9081
|
const nameNode = node.childForFieldName("name");
|
|
@@ -8721,11 +9089,6 @@ function buildBashDFG(tree) {
|
|
|
8721
9089
|
};
|
|
8722
9090
|
defs.push(def);
|
|
8723
9091
|
currentScope(scopeStack).set(varName, def.id);
|
|
8724
|
-
const valueNode = node.childForFieldName("value");
|
|
8725
|
-
if (valueNode) {
|
|
8726
|
-
extractBashUses(valueNode, uses, useIdCounter, scopeStack);
|
|
8727
|
-
useIdCounter = uses.length + 1;
|
|
8728
|
-
}
|
|
8729
9092
|
}
|
|
8730
9093
|
} else if (node.type === "command") {
|
|
8731
9094
|
const nameNode = node.childForFieldName("name");
|
|
@@ -8791,37 +9154,6 @@ function buildBashDFG(tree) {
|
|
|
8791
9154
|
const chains = computeChains(defs, uses);
|
|
8792
9155
|
return { defs, uses, chains };
|
|
8793
9156
|
}
|
|
8794
|
-
function extractBashUses(node, uses, _startId, scopeStack) {
|
|
8795
|
-
walkTree(node, (child) => {
|
|
8796
|
-
if (child.type === "simple_expansion") {
|
|
8797
|
-
const varNameNode = child.namedChildCount > 0 ? child.namedChild(0) : null;
|
|
8798
|
-
if (varNameNode) {
|
|
8799
|
-
const varName = getNodeText(varNameNode);
|
|
8800
|
-
if (varName && !varName.startsWith("?") && !varName.startsWith("#")) {
|
|
8801
|
-
const reachingDef = findReachingDef(varName, scopeStack);
|
|
8802
|
-
uses.push({
|
|
8803
|
-
id: uses.length + 1,
|
|
8804
|
-
variable: varName,
|
|
8805
|
-
line: child.startPosition.row + 1,
|
|
8806
|
-
def_id: reachingDef
|
|
8807
|
-
});
|
|
8808
|
-
}
|
|
8809
|
-
}
|
|
8810
|
-
} else if (child.type === "expansion") {
|
|
8811
|
-
const varNameNode = child.namedChildCount > 0 ? child.namedChild(0) : null;
|
|
8812
|
-
if (varNameNode && varNameNode.type === "variable_name") {
|
|
8813
|
-
const varName = getNodeText(varNameNode);
|
|
8814
|
-
const reachingDef = findReachingDef(varName, scopeStack);
|
|
8815
|
-
uses.push({
|
|
8816
|
-
id: uses.length + 1,
|
|
8817
|
-
variable: varName,
|
|
8818
|
-
line: child.startPosition.row + 1,
|
|
8819
|
-
def_id: reachingDef
|
|
8820
|
-
});
|
|
8821
|
-
}
|
|
8822
|
-
}
|
|
8823
|
-
});
|
|
8824
|
-
}
|
|
8825
9157
|
function buildRustDFG(tree, cache) {
|
|
8826
9158
|
const defs = [];
|
|
8827
9159
|
const uses = [];
|
|
@@ -8984,41 +9316,294 @@ function isRustKeyword(name2) {
|
|
|
8984
9316
|
]);
|
|
8985
9317
|
return keywords.has(name2);
|
|
8986
9318
|
}
|
|
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
|
-
|
|
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 },
|
|
9022
9607
|
{ method: "getName", class: "Cookie", type: "http_cookie", severity: "high", return_tainted: true },
|
|
9023
9608
|
// I/O Sources (Scanner, BufferedReader, etc.)
|
|
9024
9609
|
{ method: "readLine", class: "BufferedReader", type: "io_input", severity: "high", return_tainted: true },
|
|
@@ -17937,185 +18522,758 @@ var BashPlugin = class extends BaseLanguagePlugin {
|
|
|
17937
18522
|
type: "sql_injection",
|
|
17938
18523
|
cwe: "CWE-89",
|
|
17939
18524
|
severity: "critical",
|
|
17940
|
-
argPositions: [1]
|
|
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
|
|
18891
|
+
},
|
|
18892
|
+
{
|
|
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",
|
|
18913
|
+
severity: "critical",
|
|
18914
|
+
argPositions: [0]
|
|
18915
|
+
},
|
|
18916
|
+
{
|
|
18917
|
+
method: "QueryRow",
|
|
18918
|
+
class: "DB",
|
|
18919
|
+
type: "sql_injection",
|
|
18920
|
+
cwe: "CWE-89",
|
|
18921
|
+
severity: "critical",
|
|
18922
|
+
argPositions: [0]
|
|
18923
|
+
},
|
|
18924
|
+
{
|
|
18925
|
+
method: "Exec",
|
|
18926
|
+
class: "DB",
|
|
18927
|
+
type: "sql_injection",
|
|
18928
|
+
cwe: "CWE-89",
|
|
18929
|
+
severity: "critical",
|
|
18930
|
+
argPositions: [0]
|
|
17941
18931
|
},
|
|
17942
18932
|
{
|
|
17943
|
-
method: "
|
|
18933
|
+
method: "Query",
|
|
18934
|
+
class: "Tx",
|
|
17944
18935
|
type: "sql_injection",
|
|
17945
18936
|
cwe: "CWE-89",
|
|
17946
18937
|
severity: "critical",
|
|
17947
|
-
argPositions: [
|
|
18938
|
+
argPositions: [0]
|
|
17948
18939
|
},
|
|
17949
|
-
//
|
|
18940
|
+
// Command Injection
|
|
17950
18941
|
{
|
|
17951
|
-
method: "
|
|
17952
|
-
|
|
17953
|
-
|
|
17954
|
-
|
|
18942
|
+
method: "Command",
|
|
18943
|
+
class: "exec",
|
|
18944
|
+
type: "command_injection",
|
|
18945
|
+
cwe: "CWE-78",
|
|
18946
|
+
severity: "critical",
|
|
17955
18947
|
argPositions: [0]
|
|
17956
18948
|
},
|
|
17957
18949
|
{
|
|
17958
|
-
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",
|
|
17959
18961
|
type: "path_traversal",
|
|
17960
18962
|
cwe: "CWE-22",
|
|
17961
18963
|
severity: "high",
|
|
17962
18964
|
argPositions: [0]
|
|
17963
18965
|
},
|
|
17964
18966
|
{
|
|
17965
|
-
method: "
|
|
18967
|
+
method: "ReadFile",
|
|
18968
|
+
class: "os",
|
|
17966
18969
|
type: "path_traversal",
|
|
17967
18970
|
cwe: "CWE-22",
|
|
17968
18971
|
severity: "high",
|
|
17969
18972
|
argPositions: [0]
|
|
17970
18973
|
},
|
|
17971
18974
|
{
|
|
17972
|
-
method: "
|
|
18975
|
+
method: "WriteFile",
|
|
18976
|
+
class: "os",
|
|
17973
18977
|
type: "path_traversal",
|
|
17974
18978
|
cwe: "CWE-22",
|
|
17975
18979
|
severity: "high",
|
|
17976
18980
|
argPositions: [0]
|
|
17977
18981
|
},
|
|
18982
|
+
// XSS (writing to http.ResponseWriter without escaping)
|
|
17978
18983
|
{
|
|
17979
|
-
method: "
|
|
17980
|
-
|
|
17981
|
-
|
|
17982
|
-
|
|
18984
|
+
method: "Fprintf",
|
|
18985
|
+
class: "fmt",
|
|
18986
|
+
type: "xss",
|
|
18987
|
+
cwe: "CWE-79",
|
|
18988
|
+
severity: "high",
|
|
17983
18989
|
argPositions: [1]
|
|
17984
18990
|
},
|
|
17985
18991
|
{
|
|
17986
|
-
method: "
|
|
17987
|
-
|
|
17988
|
-
|
|
17989
|
-
|
|
17990
|
-
|
|
18992
|
+
method: "Write",
|
|
18993
|
+
class: "ResponseWriter",
|
|
18994
|
+
type: "xss",
|
|
18995
|
+
cwe: "CWE-79",
|
|
18996
|
+
severity: "high",
|
|
18997
|
+
argPositions: [0]
|
|
17991
18998
|
},
|
|
17992
|
-
// SSRF
|
|
18999
|
+
// SSRF
|
|
17993
19000
|
{
|
|
17994
|
-
method: "
|
|
19001
|
+
method: "Get",
|
|
19002
|
+
class: "http",
|
|
17995
19003
|
type: "ssrf",
|
|
17996
19004
|
cwe: "CWE-918",
|
|
17997
19005
|
severity: "high",
|
|
17998
19006
|
argPositions: [0]
|
|
17999
19007
|
},
|
|
18000
19008
|
{
|
|
18001
|
-
method: "
|
|
19009
|
+
method: "Post",
|
|
19010
|
+
class: "http",
|
|
18002
19011
|
type: "ssrf",
|
|
18003
19012
|
cwe: "CWE-918",
|
|
18004
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",
|
|
18005
19031
|
argPositions: [0]
|
|
18006
19032
|
}
|
|
18007
19033
|
];
|
|
18008
19034
|
}
|
|
18009
19035
|
/**
|
|
18010
|
-
*
|
|
19036
|
+
* Get receiver type from a Go call expression.
|
|
18011
19037
|
*/
|
|
18012
|
-
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
|
+
}
|
|
18013
19048
|
return void 0;
|
|
18014
19049
|
}
|
|
18015
19050
|
/**
|
|
18016
|
-
*
|
|
19051
|
+
* Check if node is a Go string literal.
|
|
18017
19052
|
*/
|
|
18018
19053
|
isStringLiteral(node) {
|
|
18019
|
-
return node.type === "
|
|
19054
|
+
return node.type === "interpreted_string_literal" || node.type === "raw_string_literal";
|
|
18020
19055
|
}
|
|
18021
19056
|
/**
|
|
18022
|
-
*
|
|
19057
|
+
* Get string value from Go string literal.
|
|
18023
19058
|
*/
|
|
18024
19059
|
getStringValue(node) {
|
|
18025
19060
|
if (!this.isStringLiteral(node)) return void 0;
|
|
18026
19061
|
const text = node.text;
|
|
18027
|
-
if (
|
|
19062
|
+
if (text.startsWith("`") && text.endsWith("`")) {
|
|
18028
19063
|
return text.slice(1, -1);
|
|
18029
19064
|
}
|
|
18030
|
-
if (
|
|
18031
|
-
return text.slice(
|
|
19065
|
+
if (text.startsWith('"') && text.endsWith('"')) {
|
|
19066
|
+
return text.slice(1, -1);
|
|
18032
19067
|
}
|
|
18033
|
-
const match = text.match(/^"(.*)"$/s);
|
|
18034
|
-
if (match) return match[1];
|
|
18035
19068
|
return text;
|
|
18036
19069
|
}
|
|
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
|
-
|
|
18062
|
-
|
|
18063
|
-
|
|
18064
|
-
|
|
18065
|
-
|
|
18066
|
-
|
|
18067
|
-
|
|
18068
|
-
|
|
18069
|
-
|
|
18070
|
-
|
|
18071
|
-
|
|
18072
|
-
|
|
18073
|
-
|
|
18074
|
-
|
|
18075
|
-
|
|
18076
|
-
|
|
18077
|
-
|
|
18078
|
-
|
|
18079
|
-
|
|
18080
|
-
|
|
18081
|
-
|
|
18082
|
-
|
|
18083
|
-
|
|
18084
|
-
|
|
18085
|
-
|
|
18086
|
-
|
|
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;
|
|
18087
19147
|
}
|
|
18088
|
-
|
|
18089
|
-
|
|
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;
|
|
18090
19195
|
}
|
|
18091
|
-
|
|
18092
|
-
|
|
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;
|
|
18093
19221
|
}
|
|
18094
|
-
|
|
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
|
+
}
|
|
18095
19235
|
return void 0;
|
|
18096
19236
|
}
|
|
18097
|
-
|
|
18098
|
-
|
|
18099
|
-
|
|
18100
|
-
|
|
18101
|
-
|
|
18102
|
-
|
|
18103
|
-
|
|
18104
|
-
|
|
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
|
+
}
|
|
18105
19250
|
}
|
|
18106
|
-
return
|
|
18107
|
-
|
|
18108
|
-
|
|
18109
|
-
|
|
18110
|
-
|
|
18111
|
-
|
|
18112
|
-
|
|
18113
|
-
|
|
18114
|
-
|
|
18115
|
-
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
|
+
};
|
|
18116
19260
|
}
|
|
18117
|
-
|
|
18118
|
-
|
|
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;
|
|
18119
19277
|
}
|
|
18120
19278
|
};
|
|
18121
19279
|
|
|
@@ -18127,6 +19285,7 @@ function registerBuiltinPlugins() {
|
|
|
18127
19285
|
registerLanguage(new RustPlugin());
|
|
18128
19286
|
registerLanguage(new BashPlugin());
|
|
18129
19287
|
registerLanguage(new HtmlPlugin());
|
|
19288
|
+
registerLanguage(new GoPlugin());
|
|
18130
19289
|
}
|
|
18131
19290
|
|
|
18132
19291
|
// src/utils/logger.ts
|
|
@@ -18908,6 +20067,7 @@ var LanguageSourcesPass = class {
|
|
|
18908
20067
|
}
|
|
18909
20068
|
const jsTaintedVars = buildJavaScriptTaintedVars(code, language);
|
|
18910
20069
|
if (language === "bash") {
|
|
20070
|
+
additionalSources.push(...findBashTaintSources(code, graph.ir.dfg));
|
|
18911
20071
|
const bashFindings = findBashPatternFindings(code, graph.ir.meta.file);
|
|
18912
20072
|
for (const finding of bashFindings) {
|
|
18913
20073
|
ctx.addFinding(finding);
|
|
@@ -19215,6 +20375,91 @@ function buildJavaScriptTaintedVars(sourceCode, language) {
|
|
|
19215
20375
|
}
|
|
19216
20376
|
return tainted;
|
|
19217
20377
|
}
|
|
20378
|
+
var BASH_UNTRUSTED_ENV_PATTERNS = [
|
|
20379
|
+
/^USER_INPUT$/i,
|
|
20380
|
+
/^QUERY_STRING$/i,
|
|
20381
|
+
/^REQUEST_/i,
|
|
20382
|
+
/^HTTP_/i,
|
|
20383
|
+
/^REMOTE_/i,
|
|
20384
|
+
/^CONTENT_TYPE$/i,
|
|
20385
|
+
/^CONTENT_LENGTH$/i,
|
|
20386
|
+
/^PATH_INFO$/i,
|
|
20387
|
+
/^SCRIPT_NAME$/i,
|
|
20388
|
+
/^SERVER_NAME$/i
|
|
20389
|
+
];
|
|
20390
|
+
var BASH_NETWORK_COMMANDS = /* @__PURE__ */ new Set(["curl", "wget", "nc", "ncat"]);
|
|
20391
|
+
var BASH_FILE_COMMANDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "awk", "sed", "cut", "grep"]);
|
|
20392
|
+
function findBashTaintSources(sourceCode, dfg) {
|
|
20393
|
+
const sources = [];
|
|
20394
|
+
const lines = sourceCode.split("\n");
|
|
20395
|
+
const definedVars = new Set(dfg.defs.filter((d) => d.kind === "local").map((d) => d.variable));
|
|
20396
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
20397
|
+
const line = lines[i2];
|
|
20398
|
+
const trimmed = line.trim();
|
|
20399
|
+
const lineNumber = i2 + 1;
|
|
20400
|
+
if (trimmed.startsWith("#")) continue;
|
|
20401
|
+
const positionalRe = /\$([1-9@*])|\$\{([1-9@*])\}/g;
|
|
20402
|
+
let m;
|
|
20403
|
+
while ((m = positionalRe.exec(line)) !== null) {
|
|
20404
|
+
const param = m[1] ?? m[2];
|
|
20405
|
+
const alreadyExists = sources.some((s) => s.line === lineNumber && s.variable === param);
|
|
20406
|
+
if (!alreadyExists) {
|
|
20407
|
+
sources.push({
|
|
20408
|
+
type: "io_input",
|
|
20409
|
+
location: `positional parameter $${param}`,
|
|
20410
|
+
severity: "high",
|
|
20411
|
+
line: lineNumber,
|
|
20412
|
+
confidence: 1,
|
|
20413
|
+
variable: param
|
|
20414
|
+
});
|
|
20415
|
+
}
|
|
20416
|
+
}
|
|
20417
|
+
const cmdSubAssign = trimmed.match(/^(\w+)=\$\((\w+)\s/);
|
|
20418
|
+
const cmdSubBacktick = trimmed.match(/^(\w+)=`(\w+)\s/);
|
|
20419
|
+
const csMatch = cmdSubAssign ?? cmdSubBacktick;
|
|
20420
|
+
if (csMatch) {
|
|
20421
|
+
const [, varName, cmd] = csMatch;
|
|
20422
|
+
if (BASH_NETWORK_COMMANDS.has(cmd)) {
|
|
20423
|
+
sources.push({
|
|
20424
|
+
type: "network_input",
|
|
20425
|
+
location: `${varName}=$(${cmd} ...) \u2014 network command output`,
|
|
20426
|
+
severity: "high",
|
|
20427
|
+
line: lineNumber,
|
|
20428
|
+
confidence: 0.9,
|
|
20429
|
+
variable: varName
|
|
20430
|
+
});
|
|
20431
|
+
} else if (BASH_FILE_COMMANDS.has(cmd)) {
|
|
20432
|
+
sources.push({
|
|
20433
|
+
type: "file_input",
|
|
20434
|
+
location: `${varName}=$(${cmd} ...) \u2014 file command output`,
|
|
20435
|
+
severity: "medium",
|
|
20436
|
+
line: lineNumber,
|
|
20437
|
+
confidence: 0.7,
|
|
20438
|
+
variable: varName
|
|
20439
|
+
});
|
|
20440
|
+
}
|
|
20441
|
+
}
|
|
20442
|
+
const envRe = /\$([A-Z][A-Z0-9_]{2,})|\$\{([A-Z][A-Z0-9_]{2,})\}/g;
|
|
20443
|
+
let em;
|
|
20444
|
+
while ((em = envRe.exec(line)) !== null) {
|
|
20445
|
+
const envVar = em[1] ?? em[2];
|
|
20446
|
+
if (!definedVars.has(envVar) && BASH_UNTRUSTED_ENV_PATTERNS.some((p) => p.test(envVar))) {
|
|
20447
|
+
const alreadyExists = sources.some((s) => s.line === lineNumber && s.variable === envVar);
|
|
20448
|
+
if (!alreadyExists) {
|
|
20449
|
+
sources.push({
|
|
20450
|
+
type: "env_input",
|
|
20451
|
+
location: `environment variable $${envVar}`,
|
|
20452
|
+
severity: "medium",
|
|
20453
|
+
line: lineNumber,
|
|
20454
|
+
confidence: 0.8,
|
|
20455
|
+
variable: envVar
|
|
20456
|
+
});
|
|
20457
|
+
}
|
|
20458
|
+
}
|
|
20459
|
+
}
|
|
20460
|
+
}
|
|
20461
|
+
return sources;
|
|
20462
|
+
}
|
|
19218
20463
|
var BASH_CREDENTIAL_PATTERN = /^(.*?)(password|passwd|secret|api_?key|token|auth_token|private_key|access_key)\s*=\s*["']?([^"'\s$][^"'\s]*)["']?\s*$/i;
|
|
19219
20464
|
function findBashPatternFindings(sourceCode, file) {
|
|
19220
20465
|
const findings = [];
|
|
@@ -24266,6 +25511,26 @@ function getNodeTypesForLanguage(language) {
|
|
|
24266
25511
|
"self_closing_tag",
|
|
24267
25512
|
"text"
|
|
24268
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
|
+
]);
|
|
24269
25534
|
default:
|
|
24270
25535
|
return /* @__PURE__ */ new Set([
|
|
24271
25536
|
"method_invocation",
|