circle-ir 3.21.0 → 3.22.1

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