circle-ir 4.7.2 → 4.9.7

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.
Files changed (40) hide show
  1. package/dist/analysis/config-loader.d.ts.map +1 -1
  2. package/dist/analysis/config-loader.js +34 -0
  3. package/dist/analysis/config-loader.js.map +1 -1
  4. package/dist/analysis/passes/insecure-cookie-pass.d.ts +1 -0
  5. package/dist/analysis/passes/insecure-cookie-pass.d.ts.map +1 -1
  6. package/dist/analysis/passes/insecure-cookie-pass.js +37 -3
  7. package/dist/analysis/passes/insecure-cookie-pass.js.map +1 -1
  8. package/dist/analysis/passes/insecure-deserialization-config-pass.d.ts +2 -0
  9. package/dist/analysis/passes/insecure-deserialization-config-pass.d.ts.map +1 -1
  10. package/dist/analysis/passes/insecure-deserialization-config-pass.js +45 -2
  11. package/dist/analysis/passes/insecure-deserialization-config-pass.js.map +1 -1
  12. package/dist/analysis/passes/jwt-verify-disabled-pass.d.ts.map +1 -1
  13. package/dist/analysis/passes/jwt-verify-disabled-pass.js +48 -21
  14. package/dist/analysis/passes/jwt-verify-disabled-pass.js.map +1 -1
  15. package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
  16. package/dist/analysis/passes/language-sources-pass.js +131 -0
  17. package/dist/analysis/passes/language-sources-pass.js.map +1 -1
  18. package/dist/analysis/passes/tls-verify-disabled-pass.d.ts.map +1 -1
  19. package/dist/analysis/passes/tls-verify-disabled-pass.js +34 -0
  20. package/dist/analysis/passes/tls-verify-disabled-pass.js.map +1 -1
  21. package/dist/analysis/passes/weak-crypto-pass.d.ts.map +1 -1
  22. package/dist/analysis/passes/weak-crypto-pass.js +32 -19
  23. package/dist/analysis/passes/weak-crypto-pass.js.map +1 -1
  24. package/dist/analysis/taint-matcher.d.ts.map +1 -1
  25. package/dist/analysis/taint-matcher.js +293 -25
  26. package/dist/analysis/taint-matcher.js.map +1 -1
  27. package/dist/analyzer.d.ts.map +1 -1
  28. package/dist/analyzer.js +14 -0
  29. package/dist/analyzer.js.map +1 -1
  30. package/dist/browser/circle-ir.js +660 -109
  31. package/dist/core/circle-ir-core.cjs +411 -68
  32. package/dist/core/circle-ir-core.js +411 -68
  33. package/dist/core/extractors/calls.js +17 -3
  34. package/dist/core/extractors/calls.js.map +1 -1
  35. package/dist/core/extractors/cfg.d.ts.map +1 -1
  36. package/dist/core/extractors/cfg.js +140 -40
  37. package/dist/core/extractors/cfg.js.map +1 -1
  38. package/dist/core/extractors/types.js +77 -2
  39. package/dist/core/extractors/types.js.map +1 -1
  40. package/package.json +1 -1
@@ -4375,6 +4375,7 @@ function extractCSharpTypes(tree, cache) {
4375
4375
  const nameNode = node.childForFieldName("name");
4376
4376
  const body2 = node.childForFieldName("body");
4377
4377
  const methods = [];
4378
+ const fields = extractCSharpFields(body2);
4378
4379
  if (body2) {
4379
4380
  for (let i2 = 0; i2 < body2.childCount; i2++) {
4380
4381
  const m = body2.child(i2);
@@ -4392,7 +4393,7 @@ function extractCSharpTypes(tree, cache) {
4392
4393
  parameters.push({
4393
4394
  name: getNodeText(pName),
4394
4395
  type: pType ? getNodeText(pType) : null,
4395
- annotations: [],
4396
+ annotations: extractCSharpParamAnnotations(pnode),
4396
4397
  line: pnode.startPosition.row + 1
4397
4398
  });
4398
4399
  }
@@ -4418,7 +4419,7 @@ function extractCSharpTypes(tree, cache) {
4418
4419
  implements: [],
4419
4420
  annotations: [],
4420
4421
  methods,
4421
- fields: [],
4422
+ fields,
4422
4423
  start_line: node.startPosition.row + 1,
4423
4424
  end_line: node.endPosition.row + 1
4424
4425
  });
@@ -4426,6 +4427,59 @@ function extractCSharpTypes(tree, cache) {
4426
4427
  }
4427
4428
  return types;
4428
4429
  }
4430
+ function extractCSharpFields(body2) {
4431
+ const fields = [];
4432
+ if (!body2) return fields;
4433
+ for (let i2 = 0; i2 < body2.childCount; i2++) {
4434
+ const c = body2.child(i2);
4435
+ if (!c) continue;
4436
+ if (c.type === "field_declaration") {
4437
+ let varDecl = null;
4438
+ for (let k = 0; k < c.childCount; k++) {
4439
+ const cc = c.child(k);
4440
+ if (cc?.type === "variable_declaration") {
4441
+ varDecl = cc;
4442
+ break;
4443
+ }
4444
+ }
4445
+ if (!varDecl) continue;
4446
+ const typeNode = varDecl.childForFieldName("type");
4447
+ const type = typeNode ? getNodeText(typeNode) : null;
4448
+ const modifiers = extractCSharpModifiers(c);
4449
+ for (let k = 0; k < varDecl.childCount; k++) {
4450
+ const decl = varDecl.child(k);
4451
+ if (decl?.type !== "variable_declarator") continue;
4452
+ const nameNode = decl.childForFieldName("name");
4453
+ fields.push({ name: nameNode ? getNodeText(nameNode) : "unknown", type, modifiers, annotations: [] });
4454
+ }
4455
+ } else if (c.type === "property_declaration") {
4456
+ const nameNode = c.childForFieldName("name");
4457
+ if (!nameNode) continue;
4458
+ const typeNode = c.childForFieldName("type");
4459
+ fields.push({
4460
+ name: getNodeText(nameNode),
4461
+ type: typeNode ? getNodeText(typeNode) : null,
4462
+ modifiers: extractCSharpModifiers(c),
4463
+ annotations: []
4464
+ });
4465
+ }
4466
+ }
4467
+ return fields;
4468
+ }
4469
+ function extractCSharpParamAnnotations(param) {
4470
+ const out2 = [];
4471
+ for (let i2 = 0; i2 < param.childCount; i2++) {
4472
+ const list = param.child(i2);
4473
+ if (list?.type !== "attribute_list") continue;
4474
+ for (let j = 0; j < list.childCount; j++) {
4475
+ const attr = list.child(j);
4476
+ if (attr?.type !== "attribute") continue;
4477
+ const name2 = attr.childForFieldName("name");
4478
+ if (name2) out2.push(getNodeText(name2));
4479
+ }
4480
+ }
4481
+ return out2;
4482
+ }
4429
4483
  function extractCSharpModifiers(node) {
4430
4484
  const mods = [];
4431
4485
  for (let i2 = 0; i2 < node.childCount; i2++) {
@@ -6000,13 +6054,21 @@ function extractCSharpCalls(tree, cache) {
6000
6054
  const left = asn.childForFieldName("left");
6001
6055
  if (left?.type !== "member_access_expression") continue;
6002
6056
  const nameNode = left.childForFieldName("name");
6003
- if (!nameNode || getNodeText(nameNode) !== "CommandText") continue;
6057
+ if (!nameNode) continue;
6058
+ const propName = getNodeText(nameNode);
6059
+ const exprNode = left.childForFieldName("expression");
6060
+ if (propName === "Filter") {
6061
+ const recv = exprNode ? getNodeText(exprNode) : null;
6062
+ const recvType = recv ? typeMap.get(recv) : void 0;
6063
+ if (recvType !== "DirectorySearcher") continue;
6064
+ } else if (propName !== "CommandText") {
6065
+ continue;
6066
+ }
6004
6067
  const right = asn.childForFieldName("right");
6005
6068
  if (!right) continue;
6006
6069
  const rhsText = getNodeText(right);
6007
- const exprNode = left.childForFieldName("expression");
6008
6070
  calls.push({
6009
- method_name: "CommandText",
6071
+ method_name: propName,
6010
6072
  receiver: exprNode ? getNodeText(exprNode) : null,
6011
6073
  receiver_type: null,
6012
6074
  receiver_type_fqn: null,
@@ -8572,6 +8634,9 @@ function buildCFG(tree, language, cache) {
8572
8634
  if (effectiveLanguage === "go") {
8573
8635
  return buildGoCFG(tree, blockIdCounter, cache);
8574
8636
  }
8637
+ if (effectiveLanguage === "csharp") {
8638
+ return buildCSharpCFG(tree, blockIdCounter, cache);
8639
+ }
8575
8640
  if (isJavaScript) {
8576
8641
  const functions = [
8577
8642
  ...getNodesFromCache(tree.rootNode, "function_declaration", cache),
@@ -8584,7 +8649,7 @@ function buildCFG(tree, language, cache) {
8584
8649
  const body2 = func2.childForFieldName("body");
8585
8650
  if (!body2) continue;
8586
8651
  if (body2.type === "statement_block") {
8587
- const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, true);
8652
+ const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "js");
8588
8653
  allBlocks.push(...blocks);
8589
8654
  allEdges.push(...edges);
8590
8655
  blockIdCounter = nextId;
@@ -8606,7 +8671,7 @@ function buildCFG(tree, language, cache) {
8606
8671
  for (const method of methods) {
8607
8672
  const body2 = method.childForFieldName("body");
8608
8673
  if (!body2) continue;
8609
- const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, false);
8674
+ const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
8610
8675
  allBlocks.push(...blocks);
8611
8676
  allEdges.push(...edges);
8612
8677
  blockIdCounter = nextId;
@@ -8614,7 +8679,28 @@ function buildCFG(tree, language, cache) {
8614
8679
  }
8615
8680
  return { blocks: allBlocks, edges: allEdges };
8616
8681
  }
8617
- function buildMethodCFG(body2, startId, isJavaScript) {
8682
+ function buildCSharpCFG(tree, blockIdCounter, cache) {
8683
+ const allBlocks = [];
8684
+ const allEdges = [];
8685
+ const containers = [
8686
+ ...getNodesFromCache(tree.rootNode, "method_declaration", cache),
8687
+ ...getNodesFromCache(tree.rootNode, "constructor_declaration", cache),
8688
+ ...getNodesFromCache(tree.rootNode, "destructor_declaration", cache),
8689
+ ...getNodesFromCache(tree.rootNode, "operator_declaration", cache),
8690
+ ...getNodesFromCache(tree.rootNode, "local_function_statement", cache),
8691
+ ...getNodesFromCache(tree.rootNode, "accessor_declaration", cache)
8692
+ ];
8693
+ for (const container of containers) {
8694
+ const body2 = container.childForFieldName("body");
8695
+ if (!body2 || body2.type !== "block") continue;
8696
+ const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "csharp");
8697
+ allBlocks.push(...blocks);
8698
+ allEdges.push(...edges);
8699
+ blockIdCounter = nextId;
8700
+ }
8701
+ return { blocks: allBlocks, edges: allEdges };
8702
+ }
8703
+ function buildMethodCFG(body2, startId, dialect) {
8618
8704
  const blocks = [];
8619
8705
  const edges = [];
8620
8706
  let currentId = startId;
@@ -8625,7 +8711,7 @@ function buildMethodCFG(body2, startId, isJavaScript) {
8625
8711
  end_line: body2.startPosition.row + 1
8626
8712
  };
8627
8713
  blocks.push(entryBlock);
8628
- const result = processStatements(body2, currentId, blocks, edges, isJavaScript);
8714
+ const result = processStatements(body2, currentId, blocks, edges, dialect);
8629
8715
  currentId = result.nextId;
8630
8716
  if (result.entryId !== -1) {
8631
8717
  edges.push({
@@ -8657,15 +8743,15 @@ function buildMethodCFG(body2, startId, isJavaScript) {
8657
8743
  }
8658
8744
  return { blocks, edges, nextId: currentId };
8659
8745
  }
8660
- function processStatements(container, startId, blocks, edges, isJavaScript) {
8746
+ function processStatements(container, startId, blocks, edges, dialect) {
8661
8747
  let currentId = startId;
8662
8748
  let firstBlockId = -1;
8663
8749
  let lastExitIds = [];
8664
8750
  for (let i2 = 0; i2 < container.childCount; i2++) {
8665
8751
  const stmt = container.child(i2);
8666
8752
  if (!stmt) continue;
8667
- if (!isStatement(stmt, isJavaScript)) continue;
8668
- const result = processStatement(stmt, currentId, blocks, edges, isJavaScript);
8753
+ if (!isStatement(stmt, dialect)) continue;
8754
+ const result = processStatement(stmt, currentId, blocks, edges, dialect);
8669
8755
  currentId = result.nextId;
8670
8756
  if (firstBlockId === -1) {
8671
8757
  firstBlockId = result.entryId;
@@ -8686,31 +8772,49 @@ function processStatements(container, startId, blocks, edges, isJavaScript) {
8686
8772
  nextId: currentId
8687
8773
  };
8688
8774
  }
8689
- function processStatement(stmt, startId, blocks, edges, isJavaScript) {
8775
+ function processStatement(stmt, startId, blocks, edges, dialect) {
8690
8776
  switch (stmt.type) {
8691
8777
  case "if_statement":
8692
- return processIfStatement(stmt, startId, blocks, edges, isJavaScript);
8778
+ return processIfStatement(stmt, startId, blocks, edges, dialect);
8693
8779
  case "for_statement":
8694
8780
  case "enhanced_for_statement":
8695
8781
  case "for_in_statement":
8696
8782
  case "for_of_statement":
8697
- return processForStatement(stmt, startId, blocks, edges, isJavaScript);
8783
+ case "foreach_statement":
8784
+ return processForStatement(stmt, startId, blocks, edges, dialect);
8698
8785
  case "while_statement":
8699
- return processWhileStatement(stmt, startId, blocks, edges, isJavaScript);
8786
+ return processWhileStatement(stmt, startId, blocks, edges, dialect);
8700
8787
  case "do_statement":
8701
- return processDoWhileStatement(stmt, startId, blocks, edges, isJavaScript);
8788
+ return processDoWhileStatement(stmt, startId, blocks, edges, dialect);
8702
8789
  case "try_statement":
8703
- return processTryStatement(stmt, startId, blocks, edges, isJavaScript);
8790
+ return processTryStatement(stmt, startId, blocks, edges, dialect);
8704
8791
  case "switch_expression":
8705
8792
  case "switch_statement":
8706
- return processSwitchStatement(stmt, startId, blocks, edges, isJavaScript);
8793
+ return processSwitchStatement(stmt, startId, blocks, edges, dialect);
8794
+ // C# scoped blocks add no branching — flow through the inner block so its
8795
+ // nested statements are still captured.
8796
+ case "using_statement":
8797
+ case "lock_statement":
8798
+ case "checked_statement":
8799
+ case "unsafe_statement": {
8800
+ const inner = stmt.childForFieldName("body") ?? lastBlockChild(stmt);
8801
+ if (inner) return processStatement(inner, startId, blocks, edges, dialect);
8802
+ return processSimpleStatement(stmt, startId, blocks);
8803
+ }
8707
8804
  case "block":
8708
8805
  case "statement_block":
8709
- return processStatements(stmt, startId, blocks, edges, isJavaScript);
8806
+ return processStatements(stmt, startId, blocks, edges, dialect);
8710
8807
  default:
8711
8808
  return processSimpleStatement(stmt, startId, blocks);
8712
8809
  }
8713
8810
  }
8811
+ function lastBlockChild(node) {
8812
+ for (let i2 = node.childCount - 1; i2 >= 0; i2--) {
8813
+ const c = node.child(i2);
8814
+ if (c && c.type === "block") return c;
8815
+ }
8816
+ return null;
8817
+ }
8714
8818
  function processSimpleStatement(stmt, startId, blocks) {
8715
8819
  const block = {
8716
8820
  id: startId,
@@ -8725,7 +8829,7 @@ function processSimpleStatement(stmt, startId, blocks) {
8725
8829
  nextId: startId + 1
8726
8830
  };
8727
8831
  }
8728
- function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
8832
+ function processIfStatement(stmt, startId, blocks, edges, dialect) {
8729
8833
  let currentId = startId;
8730
8834
  const condBlock = {
8731
8835
  id: currentId++,
@@ -8737,7 +8841,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
8737
8841
  const exitIds = [];
8738
8842
  const consequence = stmt.childForFieldName("consequence");
8739
8843
  if (consequence) {
8740
- const thenResult = processStatement(consequence, currentId, blocks, edges, isJavaScript);
8844
+ const thenResult = processStatement(consequence, currentId, blocks, edges, dialect);
8741
8845
  currentId = thenResult.nextId;
8742
8846
  edges.push({
8743
8847
  from: condBlock.id,
@@ -8748,7 +8852,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
8748
8852
  }
8749
8853
  const alternative = stmt.childForFieldName("alternative");
8750
8854
  if (alternative) {
8751
- const elseResult = processStatement(alternative, currentId, blocks, edges, isJavaScript);
8855
+ const elseResult = processStatement(alternative, currentId, blocks, edges, dialect);
8752
8856
  currentId = elseResult.nextId;
8753
8857
  edges.push({
8754
8858
  from: condBlock.id,
@@ -8765,7 +8869,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
8765
8869
  nextId: currentId
8766
8870
  };
8767
8871
  }
8768
- function processForStatement(stmt, startId, blocks, edges, isJavaScript) {
8872
+ function processForStatement(stmt, startId, blocks, edges, dialect) {
8769
8873
  let currentId = startId;
8770
8874
  const loopBlock = {
8771
8875
  id: currentId++,
@@ -8776,7 +8880,7 @@ function processForStatement(stmt, startId, blocks, edges, isJavaScript) {
8776
8880
  blocks.push(loopBlock);
8777
8881
  const body2 = stmt.childForFieldName("body");
8778
8882
  if (body2) {
8779
- const bodyResult = processStatement(body2, currentId, blocks, edges, isJavaScript);
8883
+ const bodyResult = processStatement(body2, currentId, blocks, edges, dialect);
8780
8884
  currentId = bodyResult.nextId;
8781
8885
  edges.push({
8782
8886
  from: loopBlock.id,
@@ -8798,16 +8902,16 @@ function processForStatement(stmt, startId, blocks, edges, isJavaScript) {
8798
8902
  nextId: currentId
8799
8903
  };
8800
8904
  }
8801
- function processWhileStatement(stmt, startId, blocks, edges, isJavaScript) {
8802
- return processForStatement(stmt, startId, blocks, edges, isJavaScript);
8905
+ function processWhileStatement(stmt, startId, blocks, edges, dialect) {
8906
+ return processForStatement(stmt, startId, blocks, edges, dialect);
8803
8907
  }
8804
- function processDoWhileStatement(stmt, startId, blocks, edges, isJavaScript) {
8908
+ function processDoWhileStatement(stmt, startId, blocks, edges, dialect) {
8805
8909
  let currentId = startId;
8806
8910
  const body2 = stmt.childForFieldName("body");
8807
8911
  let bodyEntryId = currentId;
8808
8912
  let bodyExitIds = [];
8809
8913
  if (body2) {
8810
- const bodyResult = processStatement(body2, currentId, blocks, edges, isJavaScript);
8914
+ const bodyResult = processStatement(body2, currentId, blocks, edges, dialect);
8811
8915
  currentId = bodyResult.nextId;
8812
8916
  bodyEntryId = bodyResult.entryId;
8813
8917
  bodyExitIds = bodyResult.exitIds;
@@ -8837,13 +8941,13 @@ function processDoWhileStatement(stmt, startId, blocks, edges, isJavaScript) {
8837
8941
  nextId: currentId
8838
8942
  };
8839
8943
  }
8840
- function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
8944
+ function processTryStatement(stmt, startId, blocks, edges, dialect) {
8841
8945
  let currentId = startId;
8842
8946
  const exitIds = [];
8843
8947
  const body2 = stmt.childForFieldName("body");
8844
8948
  let tryEntryId = -1;
8845
8949
  if (body2) {
8846
- const bodyResult = processStatements(body2, currentId, blocks, edges, isJavaScript);
8950
+ const bodyResult = processStatements(body2, currentId, blocks, edges, dialect);
8847
8951
  currentId = bodyResult.nextId;
8848
8952
  tryEntryId = bodyResult.entryId;
8849
8953
  exitIds.push(...bodyResult.exitIds);
@@ -8853,7 +8957,7 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
8853
8957
  if (child?.type === "catch_clause") {
8854
8958
  const catchBody = child.childForFieldName("body");
8855
8959
  if (catchBody) {
8856
- const catchResult = processStatements(catchBody, currentId, blocks, edges, isJavaScript);
8960
+ const catchResult = processStatements(catchBody, currentId, blocks, edges, dialect);
8857
8961
  currentId = catchResult.nextId;
8858
8962
  if (tryEntryId !== -1) {
8859
8963
  edges.push({
@@ -8866,9 +8970,18 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
8866
8970
  }
8867
8971
  }
8868
8972
  }
8869
- const finallyClause = stmt.childForFieldName("finally") ?? stmt.childForFieldName("finalizer");
8973
+ let finallyClause = stmt.childForFieldName("finally") ?? stmt.childForFieldName("finalizer");
8974
+ if (!finallyClause && dialect === "csharp") {
8975
+ for (let i2 = 0; i2 < stmt.childCount; i2++) {
8976
+ const child = stmt.child(i2);
8977
+ if (child?.type === "finally_clause") {
8978
+ finallyClause = lastBlockChild(child);
8979
+ break;
8980
+ }
8981
+ }
8982
+ }
8870
8983
  if (finallyClause) {
8871
- const finallyResult = processStatements(finallyClause, currentId, blocks, edges, isJavaScript);
8984
+ const finallyResult = processStatements(finallyClause, currentId, blocks, edges, dialect);
8872
8985
  currentId = finallyResult.nextId;
8873
8986
  for (const exitId of exitIds) {
8874
8987
  edges.push({
@@ -8889,7 +9002,7 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
8889
9002
  nextId: currentId
8890
9003
  };
8891
9004
  }
8892
- function processSwitchStatement(stmt, startId, blocks, edges, isJavaScript) {
9005
+ function processSwitchStatement(stmt, startId, blocks, edges, dialect) {
8893
9006
  let currentId = startId;
8894
9007
  const switchBlock = {
8895
9008
  id: currentId++,
@@ -8901,11 +9014,11 @@ function processSwitchStatement(stmt, startId, blocks, edges, isJavaScript) {
8901
9014
  const exitIds = [];
8902
9015
  const body2 = stmt.childForFieldName("body");
8903
9016
  if (body2) {
8904
- const caseTypes = isJavaScript ? ["switch_case", "switch_default"] : ["switch_block_statement_group", "switch_rule"];
9017
+ const caseTypes = dialect === "js" ? ["switch_case", "switch_default"] : dialect === "csharp" ? ["switch_section"] : ["switch_block_statement_group", "switch_rule"];
8905
9018
  for (let i2 = 0; i2 < body2.childCount; i2++) {
8906
9019
  const child = body2.child(i2);
8907
9020
  if (child && caseTypes.includes(child.type)) {
8908
- const caseResult = processStatements(child, currentId, blocks, edges, isJavaScript);
9021
+ const caseResult = processStatements(child, currentId, blocks, edges, dialect);
8909
9022
  currentId = caseResult.nextId;
8910
9023
  if (caseResult.entryId !== -1) {
8911
9024
  edges.push({
@@ -8935,7 +9048,7 @@ function buildBashCFG(tree, startId, cache) {
8935
9048
  for (const func2 of functions) {
8936
9049
  const body2 = func2.childForFieldName("body");
8937
9050
  if (!body2) continue;
8938
- const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, false);
9051
+ const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
8939
9052
  allBlocks.push(...blocks);
8940
9053
  allEdges.push(...edges);
8941
9054
  blockIdCounter = nextId;
@@ -8958,7 +9071,7 @@ function buildBashCFG(tree, startId, cache) {
8958
9071
  let lastExitIds = [];
8959
9072
  let firstBlockId = -1;
8960
9073
  for (const stmt of topLevelStatements) {
8961
- const result = processStatement(stmt, blockIdCounter, allBlocks, allEdges, false);
9074
+ const result = processStatement(stmt, blockIdCounter, allBlocks, allEdges, "java");
8962
9075
  blockIdCounter = result.nextId;
8963
9076
  if (firstBlockId === -1) {
8964
9077
  firstBlockId = result.entryId;
@@ -9002,7 +9115,8 @@ function isBashStatement(node) {
9002
9115
  ]);
9003
9116
  return bashStatementTypes.has(node.type);
9004
9117
  }
9005
- function isStatement(node, isJavaScript) {
9118
+ function isStatement(node, dialect) {
9119
+ if (dialect === "csharp") return csharpStatementTypes.has(node.type);
9006
9120
  const javaStatementTypes = /* @__PURE__ */ new Set([
9007
9121
  "local_variable_declaration",
9008
9122
  "expression_statement",
@@ -9046,8 +9160,30 @@ function isStatement(node, isJavaScript) {
9046
9160
  "export_statement",
9047
9161
  "import_statement"
9048
9162
  ]);
9049
- return isJavaScript ? jsStatementTypes.has(node.type) : javaStatementTypes.has(node.type);
9050
- }
9163
+ return dialect === "js" ? jsStatementTypes.has(node.type) : javaStatementTypes.has(node.type);
9164
+ }
9165
+ var csharpStatementTypes = /* @__PURE__ */ new Set([
9166
+ "local_declaration_statement",
9167
+ "expression_statement",
9168
+ "if_statement",
9169
+ "for_statement",
9170
+ "foreach_statement",
9171
+ "while_statement",
9172
+ "do_statement",
9173
+ "try_statement",
9174
+ "switch_statement",
9175
+ "return_statement",
9176
+ "throw_statement",
9177
+ "break_statement",
9178
+ "continue_statement",
9179
+ "using_statement",
9180
+ "lock_statement",
9181
+ "checked_statement",
9182
+ "unsafe_statement",
9183
+ "yield_statement",
9184
+ "goto_statement",
9185
+ "block"
9186
+ ]);
9051
9187
  function buildGoCFG(tree, blockIdCounter, cache) {
9052
9188
  const allBlocks = [];
9053
9189
  const allEdges = [];
@@ -9058,7 +9194,7 @@ function buildGoCFG(tree, blockIdCounter, cache) {
9058
9194
  for (const func2 of functions) {
9059
9195
  const body2 = func2.childForFieldName("body");
9060
9196
  if (!body2 || body2.type !== "block") continue;
9061
- const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, false);
9197
+ const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
9062
9198
  allBlocks.push(...blocks);
9063
9199
  allEdges.push(...edges);
9064
9200
  blockIdCounter = nextId;
@@ -13789,6 +13925,11 @@ var DEFAULT_SINKS = [
13789
13925
  // ADO.NET `cmd.CommandText = "…" + x` — emitted as a synthetic call by
13790
13926
  // extractCSharpCalls (property-assignment sink; the ctor-arg sink misses it).
13791
13927
  { method: "CommandText", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13928
+ // `DirectorySearcher.Filter = "(uid=" + x + ")"` — LDAP injection (cognium-ai#272).
13929
+ // Emitted as a synthetic call by extractCSharpCalls ONLY when the receiver
13930
+ // resolves to a DirectorySearcher, so this classless entry never over-matches
13931
+ // other `.Filter =` assignments (DataView/BindingSource/collection filters).
13932
+ { method: "Filter", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
13792
13933
  // ADO.NET `cmd.Execute*()` — object-carried sink (cognium-dev#271). The taint
13793
13934
  // rides the SqlCommand receiver (CommandText set from tainted data); the
13794
13935
  // receiver is surfaced as arg[0] by extractCSharpCalls, and the command object
@@ -13805,6 +13946,9 @@ var DEFAULT_SINKS = [
13805
13946
  // injectable — the second is the argv path where taint rides arg[1] (#276).
13806
13947
  { method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
13807
13948
  { method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
13949
+ // P/Invoke of libc `system(cmd)` — lowercase `system` in C# is virtually
13950
+ // always the imported shell entry point (cognium-ai#275 interop/IlPinvoke).
13951
+ { method: "system", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13808
13952
  // C# path traversal — System.IO file APIs (CWE-22). Distinctive method names.
13809
13953
  { method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13810
13954
  { method: "ReadAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13833,6 +13977,9 @@ var DEFAULT_SINKS = [
13833
13977
  { method: "Delete", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13834
13978
  { method: "GetFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13835
13979
  { method: "EnumerateFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13980
+ // ASP.NET `ControllerBase.PhysicalFile(path, contentType)` serves a file from
13981
+ // an absolute disk path — attacker-controllable path is CWE-22 (cognium-ai#326/#275).
13982
+ { method: "PhysicalFile", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13836
13983
  // C# SSRF — HttpClient / WebClient / WebRequest (CWE-918).
13837
13984
  { method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
13838
13985
  { method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13857,6 +14004,17 @@ var DEFAULT_SINKS = [
13857
14004
  { method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13858
14005
  { method: "LoadFrom", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13859
14006
  { method: "LoadFile", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
14007
+ // C# server-side template injection (SSTI). Compiling an attacker-controlled
14008
+ // template string is arbitrary code execution — classified as code_injection
14009
+ // (CWE-94), matching how Python (Jinja2/Mako) and Node SSTI are modelled.
14010
+ // Restricted to DISTINCTIVE template-compile APIs so the generic
14011
+ // `Template.Parse` / `.Compile` names (int.Parse, Regex.Compile, …) are not
14012
+ // over-matched. Taint-gated: a constant template never fires. `RunCompile`
14013
+ // = RazorEngine; `CompileRenderStringAsync` = RazorLight; `Compile` is
14014
+ // class-scoped to Handlebars. (cognium-dev#273)
14015
+ { method: "RunCompile", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
14016
+ { method: "CompileRenderStringAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
14017
+ { method: "Compile", class: "Handlebars", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13860
14018
  // C# insecure deserialization — the polymorphic BCL formatters that can
13861
14019
  // instantiate arbitrary types named in the payload (CWE-502, cognium-ai#318).
13862
14020
  // Each of these classes exists only to deserialize and is unsafe on untrusted
@@ -13871,6 +14029,14 @@ var DEFAULT_SINKS = [
13871
14029
  { method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
13872
14030
  { method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
13873
14031
  { method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
14032
+ // Blazor `new MarkupString(x)` renders its argument as raw HTML (the framework's
14033
+ // documented "trusted markup" escape hatch) — attacker-controlled input is XSS. (ca#275)
14034
+ { method: "MarkupString", class: "constructor", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
14035
+ // `String.Format(fmt, …)` with an attacker-controlled FORMAT string — CWE-134.
14036
+ // Taint-gated on arg 0 (the format), so ordinary `String.Format("...", user)` where
14037
+ // only an argument is tainted never fires. .NET composite formatting can't corrupt
14038
+ // memory, so medium (FormatException DoS / unintended arg access), unlike C printf.
14039
+ { method: "Format", class: "String", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["csharp"] },
13874
14040
  // C# LDAP injection — System.DirectoryServices (CWE-90). The user-built
13875
14041
  // filter is the constructor argument.
13876
14042
  { method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13909,6 +14075,10 @@ var DEFAULT_SINKS = [
13909
14075
  { method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
13910
14076
  { method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
13911
14077
  { method: "Compile", class: "XPathExpression", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
14078
+ // XPathNavigator.Select/Evaluate — class-scoped (both names collide with LINQ
14079
+ // `.Select`/`.Evaluate`, so they must resolve to an XPathNavigator receiver).
14080
+ { method: "Select", class: "XPathNavigator", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
14081
+ { method: "Evaluate", class: "XPathNavigator", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
13912
14082
  // C# XXE — untrusted XML into a parser without DTD hardening (CWE-611).
13913
14083
  { method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13914
14084
  { method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -14972,7 +15142,8 @@ function findSources(calls, types, patterns, sourceLines, language) {
14972
15142
  const skipMethods = ["toString", "hashCode", "equals", "compareTo"];
14973
15143
  if (skipMethods.includes(method.name)) continue;
14974
15144
  for (const param of method.parameters) {
14975
- const isTaintable = param.type ? isInterproceduralTaintableType(param.type, language) : true;
15145
+ const hasCSharpBindingAttr = language === "csharp" && param.annotations.some((a) => CSHARP_BINDING_ATTRS.has(a));
15146
+ const isTaintable = hasCSharpBindingAttr ? true : param.type ? isInterproceduralTaintableType(param.type, language) : true;
14976
15147
  if (isTaintable) {
14977
15148
  const paramLine = param.line ?? method.start_line;
14978
15149
  sources.push({
@@ -15092,6 +15263,13 @@ function findSources(calls, types, patterns, sourceLines, language) {
15092
15263
  }
15093
15264
  return result;
15094
15265
  }
15266
+ var CSHARP_BINDING_ATTRS = /* @__PURE__ */ new Set([
15267
+ "FromBody",
15268
+ "FromQuery",
15269
+ "FromRoute",
15270
+ "FromForm",
15271
+ "FromHeader"
15272
+ ]);
15095
15273
  function isInterproceduralTaintableType(typeName, language) {
15096
15274
  const baseType = typeName.split("<")[0].trim();
15097
15275
  const excludedTypes = [
@@ -15330,33 +15508,195 @@ function isSafeJSChildProcessCall(call, pattern, language) {
15330
15508
  if (SHELL_PROGRAMS.has(program)) return false;
15331
15509
  return true;
15332
15510
  }
15333
- function isSafeCSharpProcessStartCall(call, pattern, language) {
15511
+ var CSHARP_SHELL_PROGRAMS = /* @__PURE__ */ new Set([
15512
+ "sh",
15513
+ "bash",
15514
+ "zsh",
15515
+ "dash",
15516
+ "ash",
15517
+ "ksh",
15518
+ "cmd",
15519
+ "powershell",
15520
+ "pwsh"
15521
+ ]);
15522
+ function isConstNonShellExe(raw) {
15523
+ if (!raw) return false;
15524
+ const t = raw.trim();
15525
+ if (!/^@?"[^"]*"$/.test(t)) return false;
15526
+ const program = (t.replace(/^@?"|"$/g, "").split(/[\\/]/).pop() ?? "").toLowerCase().replace(/\.exe$/, "");
15527
+ return !CSHARP_SHELL_PROGRAMS.has(program);
15528
+ }
15529
+ var PSI_CTOR_EXE_RE = /\bnew\s+ProcessStartInfo\s*(?:<[^>]*>)?\s*\(\s*(@?"[^"]*")/;
15530
+ function processStartInfoExe(expr, sourceLines) {
15531
+ const inline = PSI_CTOR_EXE_RE.exec(expr);
15532
+ if (inline) return inline[1];
15533
+ if (sourceLines && /^[A-Za-z_]\w*$/.test(expr.trim())) {
15534
+ const varName = expr.trim();
15535
+ const assignRe = new RegExp(
15536
+ `\\b${varName}\\s*=\\s*new\\s+ProcessStartInfo\\s*(?:<[^>]*>)?\\s*\\(\\s*(@?"[^"]*")`
15537
+ );
15538
+ for (const line of sourceLines) {
15539
+ const m = assignRe.exec(line);
15540
+ if (m) return m[1];
15541
+ }
15542
+ }
15543
+ return null;
15544
+ }
15545
+ function isSafeCSharpProcessStartCall(call, pattern, language, sourceLines) {
15334
15546
  if (language !== "csharp") return false;
15335
15547
  if (pattern.type !== "command_injection") return false;
15336
- if (call.method_name !== "Start") return false;
15337
- if (call.arguments.length < 2) return false;
15338
- const fileArg = call.arguments.find((a) => a.position === 0);
15339
- if (!fileArg) return false;
15340
- let raw;
15341
- if (fileArg.literal !== null && fileArg.literal !== void 0) {
15342
- raw = String(fileArg.literal).trim();
15548
+ const method = call.method_name;
15549
+ if (method !== "Start" && method !== "ProcessStartInfo") return false;
15550
+ if (call.arguments.length >= 2) {
15551
+ const fileArg = call.arguments.find((a) => a.position === 0);
15552
+ const raw = fileArg?.literal != null ? String(fileArg.literal) : fileArg?.expression;
15553
+ return isConstNonShellExe(raw);
15554
+ }
15555
+ if (method === "Start" && call.arguments.length === 1) {
15556
+ const arg0 = call.arguments.find((a) => a.position === 0);
15557
+ const expr = (arg0?.expression ?? "").trim();
15558
+ return isConstNonShellExe(processStartInfoExe(expr, sourceLines));
15559
+ }
15560
+ return false;
15561
+ }
15562
+ function stripCsLiterals(line) {
15563
+ return line.replace(/\/\/.*$/, "").replace(/@?"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)'/g, "''");
15564
+ }
15565
+ function csBraceDepthBefore(lines, idx) {
15566
+ let depth = 0;
15567
+ for (let i2 = 0; i2 < idx && i2 < lines.length; i2++) {
15568
+ for (const ch of stripCsLiterals(lines[i2])) {
15569
+ if (ch === "{") depth++;
15570
+ else if (ch === "}") depth--;
15571
+ }
15572
+ }
15573
+ return depth;
15574
+ }
15575
+ function netBraces(fragment) {
15576
+ let n = 0;
15577
+ for (const ch of fragment) {
15578
+ if (ch === "{") n++;
15579
+ else if (ch === "}") n--;
15580
+ }
15581
+ return n;
15582
+ }
15583
+ function splitCsIf(line) {
15584
+ const m = /\bif\s*\(/.exec(line);
15585
+ if (!m) return null;
15586
+ const ifCol = m.index;
15587
+ let i2 = m.index + m[0].length;
15588
+ let depth = 1;
15589
+ let inStr = false;
15590
+ let strCh = "";
15591
+ for (; i2 < line.length; i2++) {
15592
+ const c = line[i2];
15593
+ if (inStr) {
15594
+ if (c === "\\") {
15595
+ i2++;
15596
+ continue;
15597
+ }
15598
+ if (c === strCh) inStr = false;
15599
+ continue;
15600
+ }
15601
+ if (c === '"' || c === "'") {
15602
+ inStr = true;
15603
+ strCh = c;
15604
+ continue;
15605
+ }
15606
+ if (c === "(") depth++;
15607
+ else if (c === ")") {
15608
+ depth--;
15609
+ if (depth === 0) break;
15610
+ }
15611
+ }
15612
+ if (depth !== 0) return null;
15613
+ return { cond: line.slice(m.index + m[0].length, i2), rest: line.slice(i2 + 1), ifCol };
15614
+ }
15615
+ function csEqualityGuard(cond) {
15616
+ const c = cond.trim();
15617
+ let m = /^(.*?)\s*(==|!=)\s*@?"[^"]*"\s*$/.exec(c);
15618
+ if (m) return { op: m[2], exprSide: m[1].trim() };
15619
+ m = /^@?"[^"]*"\s*(==|!=)\s*(.*)$/.exec(c);
15620
+ if (m) return { op: m[1], exprSide: m[2].trim() };
15621
+ return null;
15622
+ }
15623
+ var CS_IDENT_RE = /[A-Za-z_]\w*/g;
15624
+ function csIdentifiers(expr) {
15625
+ return new Set(expr.match(CS_IDENT_RE) ?? []);
15626
+ }
15627
+ function csThenBlock(lines, ifIdx, rest) {
15628
+ const isExit = (s) => /^\s*(?:return|throw|continue|break)\b/.test(s);
15629
+ let firstIdx;
15630
+ let firstText;
15631
+ if (rest.trim()) {
15632
+ firstIdx = ifIdx;
15633
+ firstText = rest;
15343
15634
  } else {
15344
- raw = (fileArg.expression ?? "").trim();
15635
+ let j = ifIdx + 1;
15636
+ while (j < lines.length && stripCsLiterals(lines[j]).trim() === "") j++;
15637
+ firstIdx = j;
15638
+ firstText = lines[j] ?? "";
15345
15639
  }
15346
- if (!/^@?"[^"]*"$/.test(raw)) return false;
15347
- const program = (raw.replace(/^@?"|"$/g, "").split(/[\\/]/).pop() ?? "").toLowerCase().replace(/\.exe$/, "");
15348
- const SHELL_PROGRAMS = /* @__PURE__ */ new Set([
15349
- "sh",
15350
- "bash",
15351
- "zsh",
15352
- "dash",
15353
- "ash",
15354
- "ksh",
15355
- "cmd",
15356
- "powershell",
15357
- "pwsh"
15358
- ]);
15359
- return !SHELL_PROGRAMS.has(program);
15640
+ if (firstText.trim().startsWith("{")) {
15641
+ let depth = 0;
15642
+ let started = false;
15643
+ let endLine = lines.length - 1;
15644
+ let body2 = "";
15645
+ for (let i2 = firstIdx; i2 < lines.length; i2++) {
15646
+ const stripped = stripCsLiterals(i2 === firstIdx ? firstText : lines[i2]);
15647
+ let broke = false;
15648
+ for (const ch of stripped) {
15649
+ if (ch === "{") {
15650
+ depth++;
15651
+ started = true;
15652
+ if (depth === 1) continue;
15653
+ } else if (ch === "}") {
15654
+ depth--;
15655
+ if (depth === 0) {
15656
+ endLine = i2;
15657
+ broke = true;
15658
+ break;
15659
+ }
15660
+ }
15661
+ if (started && depth >= 1) body2 += ch;
15662
+ }
15663
+ if (broke) break;
15664
+ if (started) body2 += " ";
15665
+ }
15666
+ return { start: ifIdx, end: endLine, earlyExit: isExit(body2.trim()) };
15667
+ }
15668
+ return { start: ifIdx, end: firstIdx, earlyExit: isExit(firstText.trim()) };
15669
+ }
15670
+ function isCSharpSsrfHostAllowlistGuarded(call, pattern, language, sourceLines) {
15671
+ if (language !== "csharp") return false;
15672
+ if (pattern.type !== "ssrf") return false;
15673
+ if (!sourceLines || sourceLines.length === 0) return false;
15674
+ const candidates = /* @__PURE__ */ new Set();
15675
+ for (const a of call.arguments) {
15676
+ if (a.variable) candidates.add(a.variable);
15677
+ for (const id of csIdentifiers(a.expression ?? "")) candidates.add(id);
15678
+ }
15679
+ if (candidates.size === 0) return false;
15680
+ const sinkIdx = call.location.line - 1;
15681
+ const sinkDepth = csBraceDepthBefore(sourceLines, sinkIdx);
15682
+ for (let i2 = 0; i2 < sinkIdx; i2++) {
15683
+ const parts2 = splitCsIf(sourceLines[i2]);
15684
+ if (!parts2) continue;
15685
+ const guard = csEqualityGuard(parts2.cond);
15686
+ if (!guard) continue;
15687
+ const guardIds = csIdentifiers(guard.exprSide);
15688
+ if (![...guardIds].some((id) => candidates.has(id))) continue;
15689
+ const block = csThenBlock(sourceLines, i2, parts2.rest);
15690
+ if (guard.op === "==") {
15691
+ if (sinkIdx >= block.start && sinkIdx <= block.end) return true;
15692
+ } else {
15693
+ const guardDepth = csBraceDepthBefore(sourceLines, i2) + netBraces(stripCsLiterals(sourceLines[i2]).slice(0, parts2.ifCol));
15694
+ if (block.earlyExit && sinkIdx > block.end && guardDepth === sinkDepth) {
15695
+ return true;
15696
+ }
15697
+ }
15698
+ }
15699
+ return false;
15360
15700
  }
15361
15701
  function isSafeRustCommandCall(call, pattern, language) {
15362
15702
  if (language !== "rust") return false;
@@ -15613,7 +15953,10 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
15613
15953
  if (isSafeJSChildProcessCall(call, pattern, language)) {
15614
15954
  continue;
15615
15955
  }
15616
- if (isSafeCSharpProcessStartCall(call, pattern, language)) {
15956
+ if (isSafeCSharpProcessStartCall(call, pattern, language, sourceLines)) {
15957
+ continue;
15958
+ }
15959
+ if (isCSharpSsrfHostAllowlistGuarded(call, pattern, language, sourceLines)) {
15617
15960
  continue;
15618
15961
  }
15619
15962
  if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at, types)) {
@@ -26502,6 +26845,8 @@ var LanguageSourcesPass = class {
26502
26845
  additionalSources.push(...findSetterChainSources(types, code, language));
26503
26846
  additionalSources.push(...findJavaScriptAssignmentSources(code, language));
26504
26847
  additionalSources.push(...findCSharpRequestSources(code, language));
26848
+ additionalSources.push(...findCSharpBindingAttributeSources(code, language));
26849
+ additionalSources.push(...findCSharpMinimalApiSources(code, language));
26505
26850
  additionalSources.push(...findGoArgvSources(code, language));
26506
26851
  const jsDOMSinks = findJavaScriptDOMSinks(code, language);
26507
26852
  for (const s of jsDOMSinks) {
@@ -27209,6 +27554,89 @@ function findCSharpRequestSources(sourceCode, language) {
27209
27554
  }
27210
27555
  return sources;
27211
27556
  }
27557
+ var CSHARP_MINIMAL_API_BINDING = /* @__PURE__ */ new Map([
27558
+ ["FromBody", "http_body"],
27559
+ ["FromForm", "http_body"],
27560
+ ["FromQuery", "http_param"],
27561
+ ["FromRoute", "http_path"],
27562
+ ["FromHeader", "http_param"]
27563
+ ]);
27564
+ var CSHARP_NON_INPUT_TYPES = /* @__PURE__ */ new Set([
27565
+ "HttpContext",
27566
+ "HttpRequest",
27567
+ "HttpResponse",
27568
+ "CancellationToken",
27569
+ "ClaimsPrincipal",
27570
+ "ILogger",
27571
+ "IFormFileCollection"
27572
+ ]);
27573
+ function findCSharpBindingAttributeSources(sourceCode, language) {
27574
+ if (language !== "csharp") return [];
27575
+ const sources = [];
27576
+ const lines = sourceCode.split("\n");
27577
+ const re = /\[\s*(FromQuery|FromBody|FromForm|FromRoute|FromHeader)(?:\s*\([^)]*\))?\s*\]\s*(?:\[[^\]]*\]\s*)*[\w.<>[\]?]+\s+([A-Za-z_]\w*)/g;
27578
+ for (let i2 = 0; i2 < lines.length; i2++) {
27579
+ const lineRe = new RegExp(re.source, "g");
27580
+ let m;
27581
+ while ((m = lineRe.exec(lines[i2])) !== null) {
27582
+ const attr = m[1];
27583
+ const name2 = m[2];
27584
+ const type = attr === "FromBody" || attr === "FromForm" ? "http_body" : attr === "FromRoute" ? "http_path" : "http_param";
27585
+ if (sources.some((s) => s.line === i2 + 1 && s.variable === name2)) continue;
27586
+ sources.push({
27587
+ type,
27588
+ location: `[${attr}] ${name2}`,
27589
+ severity: "high",
27590
+ line: i2 + 1,
27591
+ confidence: 1,
27592
+ variable: name2
27593
+ });
27594
+ }
27595
+ }
27596
+ return sources;
27597
+ }
27598
+ function findCSharpMinimalApiSources(sourceCode, language) {
27599
+ if (language !== "csharp") return [];
27600
+ const sources = [];
27601
+ const lines = sourceCode.split("\n");
27602
+ const mapRe = /\bMap(?:Get|Post|Put|Delete|Patch)\s*\(\s*(?:@?"[^"]*"|[\w.]+)\s*,\s*(?:\[[^\]]*\]\s*)?(?:async\s*)?\(([^)]*)\)\s*=>/;
27603
+ for (let i2 = 0; i2 < lines.length; i2++) {
27604
+ const m = mapRe.exec(lines[i2]);
27605
+ if (!m || !m[1].trim()) continue;
27606
+ for (const rawParam of m[1].split(",")) {
27607
+ const seed = classifyCSharpLambdaParam(rawParam);
27608
+ if (!seed) continue;
27609
+ if (sources.some((s) => s.line === i2 + 1 && s.variable === seed.name)) continue;
27610
+ sources.push({
27611
+ type: seed.type,
27612
+ location: `${seed.name} (Minimal API ${seed.via})`,
27613
+ severity: "high",
27614
+ line: i2 + 1,
27615
+ confidence: 1,
27616
+ variable: seed.name
27617
+ });
27618
+ }
27619
+ }
27620
+ return sources;
27621
+ }
27622
+ function classifyCSharpLambdaParam(raw) {
27623
+ const attrs = [...raw.matchAll(/\[([^\]]*)\]/g)].map((a) => a[1].split("(")[0].trim());
27624
+ if (attrs.includes("FromServices")) return null;
27625
+ if (attrs.some((a) => CSHARP_MINIMAL_API_BINDING.has(a))) return null;
27626
+ const noAttr = raw.replace(/\[[^\]]*\]/g, "").trim();
27627
+ const parts2 = noAttr.split(/\s+/).filter(Boolean);
27628
+ if (parts2.length < 2) return null;
27629
+ const name2 = parts2[parts2.length - 1];
27630
+ if (!/^[A-Za-z_]\w*$/.test(name2)) return null;
27631
+ const baseType = (parts2[parts2.length - 2] ?? "").replace(/[?\[\]]/g, "").split("<")[0].split(".").pop() ?? "";
27632
+ if (CSHARP_NON_INPUT_TYPES.has(baseType)) return null;
27633
+ for (const a of attrs) {
27634
+ const t = CSHARP_MINIMAL_API_BINDING.get(a);
27635
+ if (t) return { type: t, name: name2, via: `[${a}]` };
27636
+ }
27637
+ if (baseType === "string") return { type: "http_param", name: name2, via: "string param" };
27638
+ return null;
27639
+ }
27212
27640
  function findJavaScriptAssignmentSources(sourceCode, language) {
27213
27641
  if (!["javascript", "typescript"].includes(language)) return [];
27214
27642
  const sources = [];
@@ -41534,6 +41962,9 @@ var JAVA_SET_HTTPONLY_TRUE_RE = /\.setHttpOnly\s*\(\s*true\s*\)/;
41534
41962
  var GO_SECURE_TRUE_RE = /\bSecure\s*:\s*true\b/;
41535
41963
  var GO_HTTPONLY_TRUE_RE = /\bHttpOnly\s*:\s*true\b/;
41536
41964
  var RUST_SET_COOKIE_MACRO_RE = /(format!|write!|writeln!)\s*\(([^()]*Set-Cookie[^()]*)\)/gis;
41965
+ var CS_COOKIE_OPTIONS_RE = /\bnew\s+CookieOptions\s*\{([^{}]*)\}/gs;
41966
+ var CS_SECURE_FALSE_RE = /\bSecure\s*=\s*false\b/;
41967
+ var CS_HTTPONLY_FALSE_RE = /\bHttpOnly\s*=\s*false\b/;
41537
41968
  var InsecureCookiePass = class {
41538
41969
  name = "insecure-cookie";
41539
41970
  category = "security";
@@ -41577,9 +42008,29 @@ var InsecureCookiePass = class {
41577
42008
  insecureCookies.push(det);
41578
42009
  this.emit(ctx, file, det, "rust");
41579
42010
  }
42011
+ } else if (language === "csharp") {
42012
+ for (const det of this.detectCSharpCookieOptions(code)) {
42013
+ insecureCookies.push(det);
42014
+ this.emit(ctx, file, det, "csharp");
42015
+ }
41580
42016
  }
41581
42017
  return { insecureCookies };
41582
42018
  }
42019
+ // ---------------- C# ----------------
42020
+ detectCSharpCookieOptions(code) {
42021
+ const out2 = [];
42022
+ const re = new RegExp(CS_COOKIE_OPTIONS_RE.source, CS_COOKIE_OPTIONS_RE.flags);
42023
+ let m;
42024
+ while ((m = re.exec(code)) !== null) {
42025
+ const body2 = m[1] ?? "";
42026
+ const missingSecure = CS_SECURE_FALSE_RE.test(body2);
42027
+ const missingHttpOnly = CS_HTTPONLY_FALSE_RE.test(body2);
42028
+ if (!missingSecure && !missingHttpOnly) continue;
42029
+ const line = code.slice(0, m.index).split("\n").length;
42030
+ out2.push({ line, receiver: "CookieOptions", missingSecure, missingHttpOnly, optionsPresent: true });
42031
+ }
42032
+ return out2;
42033
+ }
41583
42034
  // ---------------- JS / TS ----------------
41584
42035
  detectJs(call) {
41585
42036
  if (call.method_name !== "cookie") return null;
@@ -41681,15 +42132,15 @@ var InsecureCookiePass = class {
41681
42132
  const missing = [];
41682
42133
  if (det.missingSecure) {
41683
42134
  missing.push(
41684
- flavor === "js" ? "`secure: true`" : flavor === "python" ? "`secure=True`" : flavor === "java" ? "`setSecure(true)`" : flavor === "go" ? "`Secure: true`" : "`Secure` attribute"
42135
+ flavor === "js" ? "`secure: true`" : flavor === "python" ? "`secure=True`" : flavor === "java" ? "`setSecure(true)`" : flavor === "go" ? "`Secure: true`" : flavor === "csharp" ? "`Secure = true`" : "`Secure` attribute"
41685
42136
  );
41686
42137
  }
41687
42138
  if (det.missingHttpOnly) {
41688
42139
  missing.push(
41689
- flavor === "js" ? "`httpOnly: true`" : flavor === "python" ? "`httponly=True`" : flavor === "java" ? "`setHttpOnly(true)`" : flavor === "go" ? "`HttpOnly: true`" : "`HttpOnly` attribute"
42140
+ flavor === "js" ? "`httpOnly: true`" : flavor === "python" ? "`httponly=True`" : flavor === "java" ? "`setHttpOnly(true)`" : flavor === "go" ? "`HttpOnly: true`" : flavor === "csharp" ? "`HttpOnly = true`" : "`HttpOnly` attribute"
41690
42141
  );
41691
42142
  }
41692
- const fix = flavor === "js" ? 'Pass `{ secure: true, httpOnly: true, sameSite: "lax" }` as the third argument to `res.cookie()`.' : flavor === "python" ? 'Pass `secure=True, httponly=True, samesite="Lax"` to `response.set_cookie(...)`.' : flavor === "java" ? "After constructing the cookie, call `cookie.setSecure(true)` and `cookie.setHttpOnly(true)` before adding it to the response." : flavor === "go" ? "Set `Secure: true` and `HttpOnly: true` on the `http.Cookie` struct literal passed to `http.SetCookie`." : "Append `; Secure; HttpOnly` to the `Set-Cookie` header string.";
42143
+ const fix = flavor === "js" ? 'Pass `{ secure: true, httpOnly: true, sameSite: "lax" }` as the third argument to `res.cookie()`.' : flavor === "python" ? 'Pass `secure=True, httponly=True, samesite="Lax"` to `response.set_cookie(...)`.' : flavor === "java" ? "After constructing the cookie, call `cookie.setSecure(true)` and `cookie.setHttpOnly(true)` before adding it to the response." : flavor === "go" ? "Set `Secure: true` and `HttpOnly: true` on the `http.Cookie` struct literal passed to `http.SetCookie`." : flavor === "csharp" ? "Set `Secure = true` and `HttpOnly = true` on the `CookieOptions` (or enforce them globally via `CookiePolicyOptions`)." : "Append `; Secure; HttpOnly` to the `Set-Cookie` header string.";
41693
42144
  ctx.addFinding({
41694
42145
  id: `${this.name}-${file}-${det.line}`,
41695
42146
  pass: this.name,
@@ -42160,6 +42611,7 @@ var ISSUE_CWE = {
42160
42611
  "hardcoded-key": "CWE-321",
42161
42612
  "weak-rsa-key": "CWE-326"
42162
42613
  };
42614
+ var CS_CIPHER_MODE_ECB_RE = /\bCipherMode\s*\.\s*ECB\b/;
42163
42615
  var WeakCryptoPass = class {
42164
42616
  name = "weak-crypto";
42165
42617
  category = "security";
@@ -42175,26 +42627,34 @@ var WeakCryptoPass = class {
42175
42627
  const findings = [];
42176
42628
  const constProp = ctx.hasResult("constant-propagation") ? ctx.getResult("constant-propagation") : null;
42177
42629
  const literalBindings = scanLiteralBindings(code, language);
42630
+ const emit = (line, det) => {
42631
+ findings.push({ line, language, ...det });
42632
+ ctx.addFinding({
42633
+ id: `${this.name}-${file}-${line}-${det.issue}`,
42634
+ pass: this.name,
42635
+ category: this.category,
42636
+ rule_id: this.name,
42637
+ cwe: ISSUE_CWE[det.issue],
42638
+ severity: "high",
42639
+ level: "error",
42640
+ message: this.buildMessage(det),
42641
+ file,
42642
+ line,
42643
+ fix: this.buildFix(det.issue),
42644
+ evidence: { ...det, language }
42645
+ });
42646
+ };
42178
42647
  for (const call of graph.ir.calls) {
42179
- const detections = this.detect(call, language, constProp, literalBindings);
42180
- for (const det of detections) {
42181
- const line = call.location.line;
42182
- findings.push({ line, language, ...det });
42183
- const message = this.buildMessage(det);
42184
- ctx.addFinding({
42185
- id: `${this.name}-${file}-${line}-${det.issue}`,
42186
- pass: this.name,
42187
- category: this.category,
42188
- rule_id: this.name,
42189
- cwe: ISSUE_CWE[det.issue],
42190
- severity: "high",
42191
- level: "error",
42192
- message,
42193
- file,
42194
- line,
42195
- fix: this.buildFix(det.issue),
42196
- evidence: { ...det, language }
42197
- });
42648
+ for (const det of this.detect(call, language, constProp, literalBindings)) {
42649
+ emit(call.location.line, det);
42650
+ }
42651
+ }
42652
+ if (language === "csharp") {
42653
+ const lines = code.split("\n");
42654
+ for (let i2 = 0; i2 < lines.length; i2++) {
42655
+ if (CS_CIPHER_MODE_ECB_RE.test(lines[i2])) {
42656
+ emit(i2 + 1, { issue: "ecb-mode", detail: "CipherMode.ECB", api: "SymmetricAlgorithm.Mode" });
42657
+ }
42198
42658
  }
42199
42659
  }
42200
42660
  return { findings };
@@ -43434,8 +43894,12 @@ var InsecureDeserializationConfigPass = class {
43434
43894
  name = "insecure-deserialization-config";
43435
43895
  category = "security";
43436
43896
  run(ctx) {
43897
+ if (ctx.language === "java") return this.runJava(ctx);
43898
+ if (ctx.language === "csharp") return this.runCSharp(ctx);
43899
+ return { findings: [] };
43900
+ }
43901
+ runJava(ctx) {
43437
43902
  const { graph, language } = ctx;
43438
- if (language !== "java") return { findings: [] };
43439
43903
  const file = graph.ir.meta.file;
43440
43904
  const findings = [];
43441
43905
  for (const call of graph.ir.calls) {
@@ -43460,12 +43924,46 @@ var InsecureDeserializationConfigPass = class {
43460
43924
  }
43461
43925
  return { findings };
43462
43926
  }
43927
+ // C# — Json.NET `TypeNameHandling` set to a value other than `None` (ca#318).
43928
+ // `TypeNameHandling.All/Auto/Objects/Arrays` makes Json.NET honour a `$type`
43929
+ // field in the payload and instantiate the named .NET type, so
43930
+ // `JsonConvert.DeserializeObject(untrusted, settings)` becomes a gadget-chain
43931
+ // RCE (the .NET analogue of XStream's grant-all). The vulnerability is the
43932
+ // constant setting, independent of flow — the secure value `None` never matches.
43933
+ runCSharp(ctx) {
43934
+ const file = ctx.graph.ir.meta.file;
43935
+ const findings = [];
43936
+ const lines = ctx.code.split("\n");
43937
+ for (let i2 = 0; i2 < lines.length; i2++) {
43938
+ const m = INSECURE_TYPE_NAME_HANDLING_RE.exec(lines[i2]);
43939
+ if (!m) continue;
43940
+ const line = i2 + 1;
43941
+ const api = `TypeNameHandling = TypeNameHandling.${m[1]}`;
43942
+ findings.push({ line, api });
43943
+ ctx.addFinding({
43944
+ id: `${this.name}-${file}-${line}`,
43945
+ pass: this.name,
43946
+ category: this.category,
43947
+ rule_id: this.name,
43948
+ cwe: "CWE-502",
43949
+ severity: "high",
43950
+ level: "error",
43951
+ message: `Json.NET configured with TypeNameHandling.${m[1]}: a $type field in untrusted JSON can instantiate arbitrary .NET types (deserialization RCE)`,
43952
+ file,
43953
+ line,
43954
+ fix: "Use TypeNameHandling.None (the default), or bind a SerializationBinder that allow-lists the exact types you deserialize.",
43955
+ evidence: { api, language: "csharp" }
43956
+ });
43957
+ }
43958
+ return { findings };
43959
+ }
43463
43960
  isPermissiveXStreamConfig(call) {
43464
43961
  if (call.method_name !== "addPermission") return false;
43465
43962
  const arg0 = call.arguments[0]?.expression;
43466
43963
  return typeof arg0 === "string" && ANY_TYPE_PERMISSION_RE.test(arg0);
43467
43964
  }
43468
43965
  };
43966
+ var INSECURE_TYPE_NAME_HANDLING_RE = /\bTypeNameHandling\s*=\s*(?:Newtonsoft\.Json\.)?TypeNameHandling\.(All|Auto|Objects|Arrays)\b/;
43469
43967
 
43470
43968
  // src/analysis/passes/plaintext-password-storage-pass.ts
43471
43969
  function isWriteStorageCall(call, language) {
@@ -43708,6 +44206,8 @@ var VERIFY_FALSE_RE = /\bverify\s*=\s*False\b/;
43708
44206
  var REJECT_UNAUTHORIZED_FALSE_RE = /\brejectUnauthorized\s*:\s*false\b/;
43709
44207
  var INSECURE_SKIP_VERIFY_TRUE_RE = /\bInsecureSkipVerify\s*:\s*true\b/;
43710
44208
  var HOSTNAME_LAMBDA_TRUE_RE = /\(\s*\w+\s*,\s*\w+\s*\)\s*->\s*true\b/;
44209
+ var CS_CERT_CALLBACK_TRUE_RE = /\b(ServerCertificateValidationCallback|ServerCertificateCustomValidationCallback|RemoteCertificateValidationCallback)\s*(?:\+?=|\()\s*(?:\([^)]*\)|\w+)\s*=>\s*(?:true\b|\{\s*return\s+true\b)/;
44210
+ var CS_DANGEROUS_ACCEPT_RE = /\bDangerousAcceptAnyServerCertificateValidator\b/;
43711
44211
  var ALLOW_ALL_HOSTNAME_VERIFIERS = /* @__PURE__ */ new Set([
43712
44212
  "NoopHostnameVerifier.INSTANCE",
43713
44213
  "new AllowAllHostnameVerifier()",
@@ -43852,6 +44352,21 @@ var TlsVerifyDisabledPass = class {
43852
44352
  }
43853
44353
  }
43854
44354
  }
44355
+ if (language === "csharp") {
44356
+ for (let i2 = 0; i2 < lines.length; i2++) {
44357
+ const l = lines[i2];
44358
+ const m = CS_CERT_CALLBACK_TRUE_RE.exec(l);
44359
+ if (m) {
44360
+ out2.push({ line: i2 + 1, pattern: `${m[1]} => true`, api: m[1] });
44361
+ } else if (CS_DANGEROUS_ACCEPT_RE.test(l)) {
44362
+ out2.push({
44363
+ line: i2 + 1,
44364
+ pattern: "DangerousAcceptAnyServerCertificateValidator",
44365
+ api: "HttpClientHandler"
44366
+ });
44367
+ }
44368
+ }
44369
+ }
43855
44370
  return out2;
43856
44371
  }
43857
44372
  fixFor(language, pattern) {
@@ -43873,6 +44388,9 @@ var TlsVerifyDisabledPass = class {
43873
44388
  if (pattern.includes("ssl._create_unverified_context")) {
43874
44389
  return "Do not use `_create_unverified_context()`. Use `ssl.create_default_context()`.";
43875
44390
  }
44391
+ if (language === "csharp") {
44392
+ return "Do not accept every certificate. Remove the always-true validation callback (and `DangerousAcceptAnyServerCertificateValidator`); rely on the platform default. To trust a private CA, validate the chain against it in the callback instead of returning true.";
44393
+ }
43876
44394
  return "Restore TLS certificate and hostname verification.";
43877
44395
  }
43878
44396
  };
@@ -44376,6 +44894,8 @@ var PY_VERIFY_SIGNATURE_FALSE_RE = /["']verify_signature["']\s*:\s*False\b/;
44376
44894
  var PY_VERIFY_KW_FALSE_RE = /\bverify\s*=\s*False\b/;
44377
44895
  var PY_ALG_NONE_RE = /\balgorithms\s*=\s*[\[\(]\s*["']none["']/i;
44378
44896
  var JS_ALG_NONE_RE = /\balgorithms\s*:\s*\[\s*["']none["']/i;
44897
+ var CS_REQUIRE_SIGNED_FALSE_RE = /\bRequireSignedTokens\s*=\s*false\b/;
44898
+ var CS_SIGNATURE_VALIDATOR_BYPASS_RE = /\bSignatureValidator\s*=\s*[^;]*=>\s*new\s+JwtSecurityToken\b/;
44379
44899
  var JwtVerifyDisabledPass = class {
44380
44900
  name = "jwt-verify-disabled";
44381
44901
  category = "security";
@@ -44383,25 +44903,34 @@ var JwtVerifyDisabledPass = class {
44383
44903
  const { graph, language } = ctx;
44384
44904
  const file = graph.ir.meta.file;
44385
44905
  const findings = [];
44906
+ const emit = (line, det) => {
44907
+ findings.push({ line, language, ...det });
44908
+ ctx.addFinding({
44909
+ id: `${this.name}-${file}-${line}-${det.pattern}`,
44910
+ pass: this.name,
44911
+ category: this.category,
44912
+ rule_id: this.name,
44913
+ cwe: "CWE-347",
44914
+ severity: "critical",
44915
+ level: "error",
44916
+ message: `JWT signature verification disabled via \`${det.pattern}\` in \`${det.api}\`. Any attacker can forge a token with arbitrary claims (user id, roles, expiry) since the signature is not checked.`,
44917
+ file,
44918
+ line,
44919
+ fix: this.fixFor(language),
44920
+ evidence: { ...det, language }
44921
+ });
44922
+ };
44386
44923
  for (const call of graph.ir.calls) {
44387
- const detections = this.detect(call, language);
44388
- for (const det of detections) {
44389
- const line = call.location.line;
44390
- findings.push({ line, language, ...det });
44391
- ctx.addFinding({
44392
- id: `${this.name}-${file}-${line}-${det.pattern}`,
44393
- pass: this.name,
44394
- category: this.category,
44395
- rule_id: this.name,
44396
- cwe: "CWE-347",
44397
- severity: "critical",
44398
- level: "error",
44399
- message: `JWT signature verification disabled via \`${det.pattern}\` in \`${det.api}\`. Any attacker can forge a token with arbitrary claims (user id, roles, expiry) since the signature is not checked.`,
44400
- file,
44401
- line,
44402
- fix: this.fixFor(language),
44403
- evidence: { ...det, language }
44404
- });
44924
+ for (const det of this.detect(call, language)) emit(call.location.line, det);
44925
+ }
44926
+ if (language === "csharp") {
44927
+ const lines = ctx.code.split("\n");
44928
+ for (let i2 = 0; i2 < lines.length; i2++) {
44929
+ if (CS_REQUIRE_SIGNED_FALSE_RE.test(lines[i2])) {
44930
+ emit(i2 + 1, { pattern: "RequireSignedTokens = false", api: "TokenValidationParameters" });
44931
+ } else if (CS_SIGNATURE_VALIDATOR_BYPASS_RE.test(lines[i2])) {
44932
+ emit(i2 + 1, { pattern: "SignatureValidator returns an unvalidated token", api: "TokenValidationParameters" });
44933
+ }
44405
44934
  }
44406
44935
  }
44407
44936
  return { findings };
@@ -44473,6 +45002,9 @@ var JwtVerifyDisabledPass = class {
44473
45002
  if (language === "java") {
44474
45003
  return "For auth0/java-jwt: use `JWT.require(Algorithm.HMAC256(secret))` or an RSA algorithm. For jjwt: call `parseClaimsJws(token)` (signature enforced) rather than `parse(token)` (signature ignored).";
44475
45004
  }
45005
+ if (language === "csharp") {
45006
+ return "Leave `RequireSignedTokens = true` and do not install a custom `SignatureValidator` that returns the token unverified. Configure `IssuerSigningKey`/`IssuerSigningKeys` and let the handler validate the signature.";
45007
+ }
44476
45008
  return "Enforce JWT signature verification with a concrete algorithm (HS256/RS256/ES256). Never accept `alg: none`.";
44477
45009
  }
44478
45010
  };
@@ -45629,6 +46161,25 @@ function getNodeTypesForLanguage(language) {
45629
46161
  "selector_expression",
45630
46162
  "identifier"
45631
46163
  ]);
46164
+ case "csharp":
46165
+ return /* @__PURE__ */ new Set([
46166
+ "method_invocation",
46167
+ "object_creation_expression",
46168
+ "class_declaration",
46169
+ "method_declaration",
46170
+ "constructor_declaration",
46171
+ "field_declaration",
46172
+ "import_declaration",
46173
+ "interface_declaration",
46174
+ "enum_declaration",
46175
+ "package_declaration",
46176
+ "local_variable_declaration",
46177
+ // buildCSharpCFG method-like containers
46178
+ "destructor_declaration",
46179
+ "operator_declaration",
46180
+ "local_function_statement",
46181
+ "accessor_declaration"
46182
+ ]);
45632
46183
  default:
45633
46184
  return /* @__PURE__ */ new Set([
45634
46185
  "method_invocation",