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.
@@ -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
- // src/analysis/config-loader.ts
8989
- var DEFAULT_SOURCES = [
8990
- // HTTP Sources (Servlet API)
8991
- { method: "getParameter", class: "HttpServletRequest", type: "http_param", severity: "high", return_tainted: true },
8992
- { method: "getParameterValues", class: "HttpServletRequest", type: "http_param", severity: "high", return_tainted: true },
8993
- { method: "getParameterMap", class: "HttpServletRequest", type: "http_param", severity: "high", return_tainted: true },
8994
- { method: "getParameterNames", class: "HttpServletRequest", type: "http_param", severity: "high", return_tainted: true },
8995
- { method: "getHeader", class: "HttpServletRequest", type: "http_header", severity: "high", return_tainted: true },
8996
- { method: "getHeaders", class: "HttpServletRequest", type: "http_header", severity: "high", return_tainted: true },
8997
- { method: "getHeaderNames", class: "HttpServletRequest", type: "http_header", severity: "high", return_tainted: true },
8998
- { method: "getQueryString", class: "HttpServletRequest", type: "http_query", severity: "high", return_tainted: true },
8999
- { method: "getCookies", class: "HttpServletRequest", type: "http_cookie", severity: "high", return_tainted: true },
9000
- { method: "getInputStream", class: "HttpServletRequest", type: "http_body", severity: "high", return_tainted: true },
9001
- { method: "getReader", class: "HttpServletRequest", type: "http_body", severity: "high", return_tainted: true },
9002
- { method: "getPathInfo", class: "HttpServletRequest", type: "http_path", severity: "high", return_tainted: true },
9003
- { method: "getRequestURI", class: "HttpServletRequest", type: "http_path", severity: "high", return_tainted: true },
9004
- { method: "getRequestURL", class: "HttpServletRequest", type: "http_path", severity: "high", return_tainted: true },
9005
- { method: "getServletPath", class: "HttpServletRequest", type: "http_path", severity: "high", return_tainted: true },
9006
- { method: "getContextPath", class: "HttpServletRequest", type: "http_path", severity: "medium", return_tainted: true },
9007
- { method: "getRemoteHost", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
9008
- { method: "getRemoteAddr", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
9009
- // Additional HTTP request methods that can be attacker-controlled
9010
- { method: "getProtocol", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
9011
- { method: "getScheme", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
9012
- { method: "getAuthType", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
9013
- { method: "getRemoteUser", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
9014
- { method: "getMethod", class: "HttpServletRequest", type: "http_header", severity: "low", return_tainted: true },
9015
- { method: "getContentType", class: "HttpServletRequest", type: "http_header", severity: "medium", return_tainted: true },
9016
- { method: "getCharacterEncoding", class: "HttpServletRequest", type: "http_header", severity: "low", return_tainted: true },
9017
- // Enumeration/Iterator sources (from request.getHeaders(), etc.)
9018
- { method: "nextElement", class: "Enumeration", type: "http_header", severity: "high", return_tainted: true },
9019
- { method: "nextElement", type: "http_header", severity: "high", return_tainted: true },
9020
- // Cookie sources
9021
- { method: "getValue", class: "Cookie", type: "http_cookie", severity: "high", return_tainted: true },
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: "sqlite3",
18933
+ method: "Query",
18934
+ class: "Tx",
17944
18935
  type: "sql_injection",
17945
18936
  cwe: "CWE-89",
17946
18937
  severity: "critical",
17947
- argPositions: [1]
18938
+ argPositions: [0]
17948
18939
  },
17949
- // Path traversal via file operations (first arg is path)
18940
+ // Command Injection
17950
18941
  {
17951
- method: "cat",
17952
- type: "path_traversal",
17953
- cwe: "CWE-22",
17954
- severity: "high",
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: "rm",
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: "cp",
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: "mv",
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: "chmod",
17980
- type: "path_traversal",
17981
- cwe: "CWE-22",
17982
- severity: "medium",
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: "chown",
17987
- type: "path_traversal",
17988
- cwe: "CWE-22",
17989
- severity: "medium",
17990
- argPositions: [1]
18992
+ method: "Write",
18993
+ class: "ResponseWriter",
18994
+ type: "xss",
18995
+ cwe: "CWE-79",
18996
+ severity: "high",
18997
+ argPositions: [0]
17991
18998
  },
17992
- // SSRF — curl/wget with externally-controlled URL
18999
+ // SSRF
17993
19000
  {
17994
- method: "curl",
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: "wget",
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
- * Shell has no OOP receiver types.
19036
+ * Get receiver type from a Go call expression.
18011
19037
  */
18012
- getReceiverType(_node, _context) {
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
- * Bash string literals: quoted strings and raw ($'...') strings.
19051
+ * Check if node is a Go string literal.
18017
19052
  */
18018
19053
  isStringLiteral(node) {
18019
- return node.type === "string" || node.type === "raw_string" || node.type === "ansi_c_string";
19054
+ return node.type === "interpreted_string_literal" || node.type === "raw_string_literal";
18020
19055
  }
18021
19056
  /**
18022
- * Extract string value from bash string literal, stripping quotes.
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 (node.type === "raw_string") {
19062
+ if (text.startsWith("`") && text.endsWith("`")) {
18028
19063
  return text.slice(1, -1);
18029
19064
  }
18030
- if (node.type === "ansi_c_string") {
18031
- return text.slice(2, -1);
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
- // Extraction methods — delegate to base extractors via generic walker
18038
- extractTypes(_context) {
18039
- return [];
18040
- }
18041
- extractCalls(_context) {
18042
- return [];
18043
- }
18044
- extractImports(_context) {
18045
- return [];
18046
- }
18047
- extractPackage(_context) {
18048
- return void 0;
18049
- }
18050
- };
18051
-
18052
- // src/languages/plugins/html.ts
18053
- var HtmlPlugin = class extends BaseLanguagePlugin {
18054
- id = "html";
18055
- name = "HTML";
18056
- extensions = [".html", ".htm", ".xhtml"];
18057
- wasmPath = "tree-sitter-html.wasm";
18058
- nodeTypes = {
18059
- // HTML has no OOP constructs
18060
- classDeclaration: [],
18061
- interfaceDeclaration: [],
18062
- enumDeclaration: [],
18063
- functionDeclaration: [],
18064
- methodDeclaration: [],
18065
- // No expressions in HTML
18066
- methodCall: [],
18067
- functionCall: [],
18068
- assignment: [],
18069
- variableDeclaration: [],
18070
- // No parameters
18071
- parameter: [],
18072
- argument: [],
18073
- // No annotations
18074
- annotation: [],
18075
- decorator: [],
18076
- // No imports
18077
- importStatement: [],
18078
- // No control flow
18079
- ifStatement: [],
18080
- forStatement: [],
18081
- whileStatement: [],
18082
- tryStatement: [],
18083
- returnStatement: []
18084
- };
18085
- detectFramework(_context) {
18086
- return void 0;
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
- getBuiltinSources() {
18089
- return [];
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
- getBuiltinSinks() {
18092
- return [];
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
- getReceiverType(_node, _context) {
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
- isStringLiteral(node) {
18098
- return node.type === "attribute_value" || node.type === "quoted_attribute_value";
18099
- }
18100
- getStringValue(node) {
18101
- if (!this.isStringLiteral(node)) return void 0;
18102
- const text = node.text;
18103
- if (text.startsWith('"') && text.endsWith('"') || text.startsWith("'") && text.endsWith("'")) {
18104
- return text.slice(1, -1);
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 text;
18107
- }
18108
- extractTypes(_context) {
18109
- return [];
18110
- }
18111
- extractCalls(_context) {
18112
- return [];
18113
- }
18114
- extractImports(_context) {
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
- extractPackage(_context) {
18118
- return void 0;
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",