circle-ir 4.7.1 → 4.8.2

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 (30) hide show
  1. package/dist/analysis/config-loader.d.ts.map +1 -1
  2. package/dist/analysis/config-loader.js +79 -2
  3. package/dist/analysis/config-loader.js.map +1 -1
  4. package/dist/analysis/findings.js +1 -1
  5. package/dist/analysis/findings.js.map +1 -1
  6. package/dist/analysis/passes/insecure-deserialization-config-pass.d.ts +2 -0
  7. package/dist/analysis/passes/insecure-deserialization-config-pass.d.ts.map +1 -1
  8. package/dist/analysis/passes/insecure-deserialization-config-pass.js +45 -2
  9. package/dist/analysis/passes/insecure-deserialization-config-pass.js.map +1 -1
  10. package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
  11. package/dist/analysis/passes/language-sources-pass.js +11 -1
  12. package/dist/analysis/passes/language-sources-pass.js.map +1 -1
  13. package/dist/analysis/passes/scan-secrets-pass.d.ts.map +1 -1
  14. package/dist/analysis/passes/scan-secrets-pass.js +10 -0
  15. package/dist/analysis/passes/scan-secrets-pass.js.map +1 -1
  16. package/dist/analysis/taint-matcher.d.ts.map +1 -1
  17. package/dist/analysis/taint-matcher.js +80 -0
  18. package/dist/analysis/taint-matcher.js.map +1 -1
  19. package/dist/analyzer.d.ts.map +1 -1
  20. package/dist/analyzer.js +14 -0
  21. package/dist/analyzer.js.map +1 -1
  22. package/dist/browser/circle-ir.js +320 -47
  23. package/dist/core/circle-ir-core.cjs +256 -43
  24. package/dist/core/circle-ir-core.js +256 -43
  25. package/dist/core/extractors/calls.js +17 -3
  26. package/dist/core/extractors/calls.js.map +1 -1
  27. package/dist/core/extractors/cfg.d.ts.map +1 -1
  28. package/dist/core/extractors/cfg.js +140 -40
  29. package/dist/core/extractors/cfg.js.map +1 -1
  30. package/package.json +1 -1
@@ -6000,13 +6000,21 @@ function extractCSharpCalls(tree, cache) {
6000
6000
  const left = asn.childForFieldName("left");
6001
6001
  if (left?.type !== "member_access_expression") continue;
6002
6002
  const nameNode = left.childForFieldName("name");
6003
- if (!nameNode || getNodeText(nameNode) !== "CommandText") continue;
6003
+ if (!nameNode) continue;
6004
+ const propName = getNodeText(nameNode);
6005
+ const exprNode = left.childForFieldName("expression");
6006
+ if (propName === "Filter") {
6007
+ const recv = exprNode ? getNodeText(exprNode) : null;
6008
+ const recvType = recv ? typeMap.get(recv) : void 0;
6009
+ if (recvType !== "DirectorySearcher") continue;
6010
+ } else if (propName !== "CommandText") {
6011
+ continue;
6012
+ }
6004
6013
  const right = asn.childForFieldName("right");
6005
6014
  if (!right) continue;
6006
6015
  const rhsText = getNodeText(right);
6007
- const exprNode = left.childForFieldName("expression");
6008
6016
  calls.push({
6009
- method_name: "CommandText",
6017
+ method_name: propName,
6010
6018
  receiver: exprNode ? getNodeText(exprNode) : null,
6011
6019
  receiver_type: null,
6012
6020
  receiver_type_fqn: null,
@@ -8572,6 +8580,9 @@ function buildCFG(tree, language, cache) {
8572
8580
  if (effectiveLanguage === "go") {
8573
8581
  return buildGoCFG(tree, blockIdCounter, cache);
8574
8582
  }
8583
+ if (effectiveLanguage === "csharp") {
8584
+ return buildCSharpCFG(tree, blockIdCounter, cache);
8585
+ }
8575
8586
  if (isJavaScript) {
8576
8587
  const functions = [
8577
8588
  ...getNodesFromCache(tree.rootNode, "function_declaration", cache),
@@ -8584,7 +8595,7 @@ function buildCFG(tree, language, cache) {
8584
8595
  const body2 = func2.childForFieldName("body");
8585
8596
  if (!body2) continue;
8586
8597
  if (body2.type === "statement_block") {
8587
- const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, true);
8598
+ const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "js");
8588
8599
  allBlocks.push(...blocks);
8589
8600
  allEdges.push(...edges);
8590
8601
  blockIdCounter = nextId;
@@ -8606,7 +8617,7 @@ function buildCFG(tree, language, cache) {
8606
8617
  for (const method of methods) {
8607
8618
  const body2 = method.childForFieldName("body");
8608
8619
  if (!body2) continue;
8609
- const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, false);
8620
+ const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
8610
8621
  allBlocks.push(...blocks);
8611
8622
  allEdges.push(...edges);
8612
8623
  blockIdCounter = nextId;
@@ -8614,7 +8625,28 @@ function buildCFG(tree, language, cache) {
8614
8625
  }
8615
8626
  return { blocks: allBlocks, edges: allEdges };
8616
8627
  }
8617
- function buildMethodCFG(body2, startId, isJavaScript) {
8628
+ function buildCSharpCFG(tree, blockIdCounter, cache) {
8629
+ const allBlocks = [];
8630
+ const allEdges = [];
8631
+ const containers = [
8632
+ ...getNodesFromCache(tree.rootNode, "method_declaration", cache),
8633
+ ...getNodesFromCache(tree.rootNode, "constructor_declaration", cache),
8634
+ ...getNodesFromCache(tree.rootNode, "destructor_declaration", cache),
8635
+ ...getNodesFromCache(tree.rootNode, "operator_declaration", cache),
8636
+ ...getNodesFromCache(tree.rootNode, "local_function_statement", cache),
8637
+ ...getNodesFromCache(tree.rootNode, "accessor_declaration", cache)
8638
+ ];
8639
+ for (const container of containers) {
8640
+ const body2 = container.childForFieldName("body");
8641
+ if (!body2 || body2.type !== "block") continue;
8642
+ const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "csharp");
8643
+ allBlocks.push(...blocks);
8644
+ allEdges.push(...edges);
8645
+ blockIdCounter = nextId;
8646
+ }
8647
+ return { blocks: allBlocks, edges: allEdges };
8648
+ }
8649
+ function buildMethodCFG(body2, startId, dialect) {
8618
8650
  const blocks = [];
8619
8651
  const edges = [];
8620
8652
  let currentId = startId;
@@ -8625,7 +8657,7 @@ function buildMethodCFG(body2, startId, isJavaScript) {
8625
8657
  end_line: body2.startPosition.row + 1
8626
8658
  };
8627
8659
  blocks.push(entryBlock);
8628
- const result = processStatements(body2, currentId, blocks, edges, isJavaScript);
8660
+ const result = processStatements(body2, currentId, blocks, edges, dialect);
8629
8661
  currentId = result.nextId;
8630
8662
  if (result.entryId !== -1) {
8631
8663
  edges.push({
@@ -8657,15 +8689,15 @@ function buildMethodCFG(body2, startId, isJavaScript) {
8657
8689
  }
8658
8690
  return { blocks, edges, nextId: currentId };
8659
8691
  }
8660
- function processStatements(container, startId, blocks, edges, isJavaScript) {
8692
+ function processStatements(container, startId, blocks, edges, dialect) {
8661
8693
  let currentId = startId;
8662
8694
  let firstBlockId = -1;
8663
8695
  let lastExitIds = [];
8664
8696
  for (let i2 = 0; i2 < container.childCount; i2++) {
8665
8697
  const stmt = container.child(i2);
8666
8698
  if (!stmt) continue;
8667
- if (!isStatement(stmt, isJavaScript)) continue;
8668
- const result = processStatement(stmt, currentId, blocks, edges, isJavaScript);
8699
+ if (!isStatement(stmt, dialect)) continue;
8700
+ const result = processStatement(stmt, currentId, blocks, edges, dialect);
8669
8701
  currentId = result.nextId;
8670
8702
  if (firstBlockId === -1) {
8671
8703
  firstBlockId = result.entryId;
@@ -8686,31 +8718,49 @@ function processStatements(container, startId, blocks, edges, isJavaScript) {
8686
8718
  nextId: currentId
8687
8719
  };
8688
8720
  }
8689
- function processStatement(stmt, startId, blocks, edges, isJavaScript) {
8721
+ function processStatement(stmt, startId, blocks, edges, dialect) {
8690
8722
  switch (stmt.type) {
8691
8723
  case "if_statement":
8692
- return processIfStatement(stmt, startId, blocks, edges, isJavaScript);
8724
+ return processIfStatement(stmt, startId, blocks, edges, dialect);
8693
8725
  case "for_statement":
8694
8726
  case "enhanced_for_statement":
8695
8727
  case "for_in_statement":
8696
8728
  case "for_of_statement":
8697
- return processForStatement(stmt, startId, blocks, edges, isJavaScript);
8729
+ case "foreach_statement":
8730
+ return processForStatement(stmt, startId, blocks, edges, dialect);
8698
8731
  case "while_statement":
8699
- return processWhileStatement(stmt, startId, blocks, edges, isJavaScript);
8732
+ return processWhileStatement(stmt, startId, blocks, edges, dialect);
8700
8733
  case "do_statement":
8701
- return processDoWhileStatement(stmt, startId, blocks, edges, isJavaScript);
8734
+ return processDoWhileStatement(stmt, startId, blocks, edges, dialect);
8702
8735
  case "try_statement":
8703
- return processTryStatement(stmt, startId, blocks, edges, isJavaScript);
8736
+ return processTryStatement(stmt, startId, blocks, edges, dialect);
8704
8737
  case "switch_expression":
8705
8738
  case "switch_statement":
8706
- return processSwitchStatement(stmt, startId, blocks, edges, isJavaScript);
8739
+ return processSwitchStatement(stmt, startId, blocks, edges, dialect);
8740
+ // C# scoped blocks add no branching — flow through the inner block so its
8741
+ // nested statements are still captured.
8742
+ case "using_statement":
8743
+ case "lock_statement":
8744
+ case "checked_statement":
8745
+ case "unsafe_statement": {
8746
+ const inner = stmt.childForFieldName("body") ?? lastBlockChild(stmt);
8747
+ if (inner) return processStatement(inner, startId, blocks, edges, dialect);
8748
+ return processSimpleStatement(stmt, startId, blocks);
8749
+ }
8707
8750
  case "block":
8708
8751
  case "statement_block":
8709
- return processStatements(stmt, startId, blocks, edges, isJavaScript);
8752
+ return processStatements(stmt, startId, blocks, edges, dialect);
8710
8753
  default:
8711
8754
  return processSimpleStatement(stmt, startId, blocks);
8712
8755
  }
8713
8756
  }
8757
+ function lastBlockChild(node) {
8758
+ for (let i2 = node.childCount - 1; i2 >= 0; i2--) {
8759
+ const c = node.child(i2);
8760
+ if (c && c.type === "block") return c;
8761
+ }
8762
+ return null;
8763
+ }
8714
8764
  function processSimpleStatement(stmt, startId, blocks) {
8715
8765
  const block = {
8716
8766
  id: startId,
@@ -8725,7 +8775,7 @@ function processSimpleStatement(stmt, startId, blocks) {
8725
8775
  nextId: startId + 1
8726
8776
  };
8727
8777
  }
8728
- function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
8778
+ function processIfStatement(stmt, startId, blocks, edges, dialect) {
8729
8779
  let currentId = startId;
8730
8780
  const condBlock = {
8731
8781
  id: currentId++,
@@ -8737,7 +8787,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
8737
8787
  const exitIds = [];
8738
8788
  const consequence = stmt.childForFieldName("consequence");
8739
8789
  if (consequence) {
8740
- const thenResult = processStatement(consequence, currentId, blocks, edges, isJavaScript);
8790
+ const thenResult = processStatement(consequence, currentId, blocks, edges, dialect);
8741
8791
  currentId = thenResult.nextId;
8742
8792
  edges.push({
8743
8793
  from: condBlock.id,
@@ -8748,7 +8798,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
8748
8798
  }
8749
8799
  const alternative = stmt.childForFieldName("alternative");
8750
8800
  if (alternative) {
8751
- const elseResult = processStatement(alternative, currentId, blocks, edges, isJavaScript);
8801
+ const elseResult = processStatement(alternative, currentId, blocks, edges, dialect);
8752
8802
  currentId = elseResult.nextId;
8753
8803
  edges.push({
8754
8804
  from: condBlock.id,
@@ -8765,7 +8815,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
8765
8815
  nextId: currentId
8766
8816
  };
8767
8817
  }
8768
- function processForStatement(stmt, startId, blocks, edges, isJavaScript) {
8818
+ function processForStatement(stmt, startId, blocks, edges, dialect) {
8769
8819
  let currentId = startId;
8770
8820
  const loopBlock = {
8771
8821
  id: currentId++,
@@ -8776,7 +8826,7 @@ function processForStatement(stmt, startId, blocks, edges, isJavaScript) {
8776
8826
  blocks.push(loopBlock);
8777
8827
  const body2 = stmt.childForFieldName("body");
8778
8828
  if (body2) {
8779
- const bodyResult = processStatement(body2, currentId, blocks, edges, isJavaScript);
8829
+ const bodyResult = processStatement(body2, currentId, blocks, edges, dialect);
8780
8830
  currentId = bodyResult.nextId;
8781
8831
  edges.push({
8782
8832
  from: loopBlock.id,
@@ -8798,16 +8848,16 @@ function processForStatement(stmt, startId, blocks, edges, isJavaScript) {
8798
8848
  nextId: currentId
8799
8849
  };
8800
8850
  }
8801
- function processWhileStatement(stmt, startId, blocks, edges, isJavaScript) {
8802
- return processForStatement(stmt, startId, blocks, edges, isJavaScript);
8851
+ function processWhileStatement(stmt, startId, blocks, edges, dialect) {
8852
+ return processForStatement(stmt, startId, blocks, edges, dialect);
8803
8853
  }
8804
- function processDoWhileStatement(stmt, startId, blocks, edges, isJavaScript) {
8854
+ function processDoWhileStatement(stmt, startId, blocks, edges, dialect) {
8805
8855
  let currentId = startId;
8806
8856
  const body2 = stmt.childForFieldName("body");
8807
8857
  let bodyEntryId = currentId;
8808
8858
  let bodyExitIds = [];
8809
8859
  if (body2) {
8810
- const bodyResult = processStatement(body2, currentId, blocks, edges, isJavaScript);
8860
+ const bodyResult = processStatement(body2, currentId, blocks, edges, dialect);
8811
8861
  currentId = bodyResult.nextId;
8812
8862
  bodyEntryId = bodyResult.entryId;
8813
8863
  bodyExitIds = bodyResult.exitIds;
@@ -8837,13 +8887,13 @@ function processDoWhileStatement(stmt, startId, blocks, edges, isJavaScript) {
8837
8887
  nextId: currentId
8838
8888
  };
8839
8889
  }
8840
- function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
8890
+ function processTryStatement(stmt, startId, blocks, edges, dialect) {
8841
8891
  let currentId = startId;
8842
8892
  const exitIds = [];
8843
8893
  const body2 = stmt.childForFieldName("body");
8844
8894
  let tryEntryId = -1;
8845
8895
  if (body2) {
8846
- const bodyResult = processStatements(body2, currentId, blocks, edges, isJavaScript);
8896
+ const bodyResult = processStatements(body2, currentId, blocks, edges, dialect);
8847
8897
  currentId = bodyResult.nextId;
8848
8898
  tryEntryId = bodyResult.entryId;
8849
8899
  exitIds.push(...bodyResult.exitIds);
@@ -8853,7 +8903,7 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
8853
8903
  if (child?.type === "catch_clause") {
8854
8904
  const catchBody = child.childForFieldName("body");
8855
8905
  if (catchBody) {
8856
- const catchResult = processStatements(catchBody, currentId, blocks, edges, isJavaScript);
8906
+ const catchResult = processStatements(catchBody, currentId, blocks, edges, dialect);
8857
8907
  currentId = catchResult.nextId;
8858
8908
  if (tryEntryId !== -1) {
8859
8909
  edges.push({
@@ -8866,9 +8916,18 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
8866
8916
  }
8867
8917
  }
8868
8918
  }
8869
- const finallyClause = stmt.childForFieldName("finally") ?? stmt.childForFieldName("finalizer");
8919
+ let finallyClause = stmt.childForFieldName("finally") ?? stmt.childForFieldName("finalizer");
8920
+ if (!finallyClause && dialect === "csharp") {
8921
+ for (let i2 = 0; i2 < stmt.childCount; i2++) {
8922
+ const child = stmt.child(i2);
8923
+ if (child?.type === "finally_clause") {
8924
+ finallyClause = lastBlockChild(child);
8925
+ break;
8926
+ }
8927
+ }
8928
+ }
8870
8929
  if (finallyClause) {
8871
- const finallyResult = processStatements(finallyClause, currentId, blocks, edges, isJavaScript);
8930
+ const finallyResult = processStatements(finallyClause, currentId, blocks, edges, dialect);
8872
8931
  currentId = finallyResult.nextId;
8873
8932
  for (const exitId of exitIds) {
8874
8933
  edges.push({
@@ -8889,7 +8948,7 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
8889
8948
  nextId: currentId
8890
8949
  };
8891
8950
  }
8892
- function processSwitchStatement(stmt, startId, blocks, edges, isJavaScript) {
8951
+ function processSwitchStatement(stmt, startId, blocks, edges, dialect) {
8893
8952
  let currentId = startId;
8894
8953
  const switchBlock = {
8895
8954
  id: currentId++,
@@ -8901,11 +8960,11 @@ function processSwitchStatement(stmt, startId, blocks, edges, isJavaScript) {
8901
8960
  const exitIds = [];
8902
8961
  const body2 = stmt.childForFieldName("body");
8903
8962
  if (body2) {
8904
- const caseTypes = isJavaScript ? ["switch_case", "switch_default"] : ["switch_block_statement_group", "switch_rule"];
8963
+ const caseTypes = dialect === "js" ? ["switch_case", "switch_default"] : dialect === "csharp" ? ["switch_section"] : ["switch_block_statement_group", "switch_rule"];
8905
8964
  for (let i2 = 0; i2 < body2.childCount; i2++) {
8906
8965
  const child = body2.child(i2);
8907
8966
  if (child && caseTypes.includes(child.type)) {
8908
- const caseResult = processStatements(child, currentId, blocks, edges, isJavaScript);
8967
+ const caseResult = processStatements(child, currentId, blocks, edges, dialect);
8909
8968
  currentId = caseResult.nextId;
8910
8969
  if (caseResult.entryId !== -1) {
8911
8970
  edges.push({
@@ -8935,7 +8994,7 @@ function buildBashCFG(tree, startId, cache) {
8935
8994
  for (const func2 of functions) {
8936
8995
  const body2 = func2.childForFieldName("body");
8937
8996
  if (!body2) continue;
8938
- const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, false);
8997
+ const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
8939
8998
  allBlocks.push(...blocks);
8940
8999
  allEdges.push(...edges);
8941
9000
  blockIdCounter = nextId;
@@ -8958,7 +9017,7 @@ function buildBashCFG(tree, startId, cache) {
8958
9017
  let lastExitIds = [];
8959
9018
  let firstBlockId = -1;
8960
9019
  for (const stmt of topLevelStatements) {
8961
- const result = processStatement(stmt, blockIdCounter, allBlocks, allEdges, false);
9020
+ const result = processStatement(stmt, blockIdCounter, allBlocks, allEdges, "java");
8962
9021
  blockIdCounter = result.nextId;
8963
9022
  if (firstBlockId === -1) {
8964
9023
  firstBlockId = result.entryId;
@@ -9002,7 +9061,8 @@ function isBashStatement(node) {
9002
9061
  ]);
9003
9062
  return bashStatementTypes.has(node.type);
9004
9063
  }
9005
- function isStatement(node, isJavaScript) {
9064
+ function isStatement(node, dialect) {
9065
+ if (dialect === "csharp") return csharpStatementTypes.has(node.type);
9006
9066
  const javaStatementTypes = /* @__PURE__ */ new Set([
9007
9067
  "local_variable_declaration",
9008
9068
  "expression_statement",
@@ -9046,8 +9106,30 @@ function isStatement(node, isJavaScript) {
9046
9106
  "export_statement",
9047
9107
  "import_statement"
9048
9108
  ]);
9049
- return isJavaScript ? jsStatementTypes.has(node.type) : javaStatementTypes.has(node.type);
9050
- }
9109
+ return dialect === "js" ? jsStatementTypes.has(node.type) : javaStatementTypes.has(node.type);
9110
+ }
9111
+ var csharpStatementTypes = /* @__PURE__ */ new Set([
9112
+ "local_declaration_statement",
9113
+ "expression_statement",
9114
+ "if_statement",
9115
+ "for_statement",
9116
+ "foreach_statement",
9117
+ "while_statement",
9118
+ "do_statement",
9119
+ "try_statement",
9120
+ "switch_statement",
9121
+ "return_statement",
9122
+ "throw_statement",
9123
+ "break_statement",
9124
+ "continue_statement",
9125
+ "using_statement",
9126
+ "lock_statement",
9127
+ "checked_statement",
9128
+ "unsafe_statement",
9129
+ "yield_statement",
9130
+ "goto_statement",
9131
+ "block"
9132
+ ]);
9051
9133
  function buildGoCFG(tree, blockIdCounter, cache) {
9052
9134
  const allBlocks = [];
9053
9135
  const allEdges = [];
@@ -9058,7 +9140,7 @@ function buildGoCFG(tree, blockIdCounter, cache) {
9058
9140
  for (const func2 of functions) {
9059
9141
  const body2 = func2.childForFieldName("body");
9060
9142
  if (!body2 || body2.type !== "block") continue;
9061
- const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, false);
9143
+ const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
9062
9144
  allBlocks.push(...blocks);
9063
9145
  allEdges.push(...edges);
9064
9146
  blockIdCounter = nextId;
@@ -12258,7 +12340,11 @@ var DEFAULT_SINKS = [
12258
12340
  // File: covers both File(String pathname) and File(parent, child). The 2-arg
12259
12341
  // overload's child argument carries CVE-2018-8041 (Camel mail Content-Disposition
12260
12342
  // filename written to disk).
12261
- { method: "File", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
12343
+ // Java `new File(path)` idiom. Excluded for C#: there is no `new File()` in
12344
+ // .NET (it's `System.IO.File` statics + `FileStream`, covered separately), and
12345
+ // this over-matched ASP.NET `ControllerBase.File(stream|bytes, contentType)` —
12346
+ // a result helper that returns a file, not a path sink (cognium-ai#326 B).
12347
+ { method: "File", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], exclude_languages: ["csharp"] },
12262
12348
  { method: "FileInputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
12263
12349
  { method: "FileOutputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
12264
12350
  { method: "FileReader", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
@@ -13785,6 +13871,11 @@ var DEFAULT_SINKS = [
13785
13871
  // ADO.NET `cmd.CommandText = "…" + x` — emitted as a synthetic call by
13786
13872
  // extractCSharpCalls (property-assignment sink; the ctor-arg sink misses it).
13787
13873
  { method: "CommandText", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13874
+ // `DirectorySearcher.Filter = "(uid=" + x + ")"` — LDAP injection (cognium-ai#272).
13875
+ // Emitted as a synthetic call by extractCSharpCalls ONLY when the receiver
13876
+ // resolves to a DirectorySearcher, so this classless entry never over-matches
13877
+ // other `.Filter =` assignments (DataView/BindingSource/collection filters).
13878
+ { method: "Filter", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
13788
13879
  // ADO.NET `cmd.Execute*()` — object-carried sink (cognium-dev#271). The taint
13789
13880
  // rides the SqlCommand receiver (CommandText set from tainted data); the
13790
13881
  // receiver is surfaced as arg[0] by extractCSharpCalls, and the command object
@@ -13801,6 +13892,9 @@ var DEFAULT_SINKS = [
13801
13892
  // injectable — the second is the argv path where taint rides arg[1] (#276).
13802
13893
  { method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
13803
13894
  { method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
13895
+ // P/Invoke of libc `system(cmd)` — lowercase `system` in C# is virtually
13896
+ // always the imported shell entry point (cognium-ai#275 interop/IlPinvoke).
13897
+ { method: "system", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13804
13898
  // C# path traversal — System.IO file APIs (CWE-22). Distinctive method names.
13805
13899
  { method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13806
13900
  { method: "ReadAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13813,6 +13907,25 @@ var DEFAULT_SINKS = [
13813
13907
  { method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13814
13908
  { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13815
13909
  { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13910
+ // Remaining System.IO.File path APIs — class-scoped so they don't collide
13911
+ // with unrelated methods. Copy/Move take a source AND destination path (both
13912
+ // attacker-controllable → read-anywhere / write-anywhere), hence [0, 1].
13913
+ { method: "Copy", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
13914
+ { method: "Move", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
13915
+ { method: "Delete", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13916
+ { method: "Open", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13917
+ { method: "OpenText", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13918
+ { method: "Create", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13919
+ { method: "WriteAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13920
+ { method: "AppendAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13921
+ // System.IO.Directory path APIs.
13922
+ { method: "CreateDirectory", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13923
+ { method: "Delete", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13924
+ { method: "GetFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13925
+ { method: "EnumerateFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13926
+ // ASP.NET `ControllerBase.PhysicalFile(path, contentType)` serves a file from
13927
+ // an absolute disk path — attacker-controllable path is CWE-22 (cognium-ai#326/#275).
13928
+ { method: "PhysicalFile", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13816
13929
  // C# SSRF — HttpClient / WebClient / WebRequest (CWE-918).
13817
13930
  { method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
13818
13931
  { method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13829,13 +13942,36 @@ var DEFAULT_SINKS = [
13829
13942
  // C# code injection — dynamic script/assembly loading (CWE-94).
13830
13943
  { method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13831
13944
  { method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13832
- // C# insecure deserialization BinaryFormatter et al. (CWE-502).
13945
+ // Loading an assembly from an attacker-controlled name/path is arbitrary code
13946
+ // execution. Class-scoped to `Assembly` (low FP — a tainted arg here is rare
13947
+ // in benign code). `Type.GetType`/`Activator.CreateInstance` are intentionally
13948
+ // NOT added — reflection with config-driven type names is common in DI, an FP
13949
+ // trap without the reflection-suppress machinery.
13950
+ { method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13951
+ { method: "LoadFrom", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13952
+ { method: "LoadFile", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13953
+ // C# insecure deserialization — the polymorphic BCL formatters that can
13954
+ // instantiate arbitrary types named in the payload (CWE-502, cognium-ai#318).
13955
+ // Each of these classes exists only to deserialize and is unsafe on untrusted
13956
+ // input; class-scoped, so no collision with safe JSON APIs (System.Text.Json /
13957
+ // Newtonsoft-default, which are intentionally NOT sinks).
13833
13958
  { method: "Deserialize", class: "BinaryFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13834
13959
  { method: "Deserialize", class: "NetDataContractSerializer", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13960
+ { method: "Deserialize", class: "SoapFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13961
+ { method: "Deserialize", class: "LosFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13962
+ { method: "Deserialize", class: "ObjectStateFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13835
13963
  // C# XSS — raw HTML output (CWE-79).
13836
13964
  { method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
13837
13965
  { method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
13838
13966
  { method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
13967
+ // Blazor `new MarkupString(x)` renders its argument as raw HTML (the framework's
13968
+ // documented "trusted markup" escape hatch) — attacker-controlled input is XSS. (ca#275)
13969
+ { method: "MarkupString", class: "constructor", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
13970
+ // `String.Format(fmt, …)` with an attacker-controlled FORMAT string — CWE-134.
13971
+ // Taint-gated on arg 0 (the format), so ordinary `String.Format("...", user)` where
13972
+ // only an argument is tainted never fires. .NET composite formatting can't corrupt
13973
+ // memory, so medium (FormatException DoS / unintended arg access), unlike C printf.
13974
+ { method: "Format", class: "String", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["csharp"] },
13839
13975
  // C# LDAP injection — System.DirectoryServices (CWE-90). The user-built
13840
13976
  // filter is the constructor argument.
13841
13977
  { method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13856,14 +13992,37 @@ var DEFAULT_SINKS = [
13856
13992
  // cognium-dev#273/#275). `BsonDocument.Parse(userJson)` deserializes an
13857
13993
  // attacker-controlled query document. Class-scoped (static receiver resolves).
13858
13994
  { method: "Parse", class: "BsonDocument", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["csharp"] },
13995
+ // C# log injection — Microsoft.Extensions.Logging `ILogger` (CWE-117,
13996
+ // cognium-ai#318). Only the message TEMPLATE (arg[0]) is the injectable
13997
+ // position; the structured form `LogInformation("… {Id}", id)` keeps taint in
13998
+ // later args (the logger renders them as data), so arg_positions:[0] does not
13999
+ // flag it. Mirrors the Java `Logger` sinks; severity low.
14000
+ { method: "LogInformation", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
14001
+ { method: "LogWarning", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
14002
+ { method: "LogError", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
14003
+ { method: "LogDebug", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
14004
+ { method: "LogCritical", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
14005
+ { method: "LogTrace", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
14006
+ // Generic `ILogger.Log(logLevel, message, …)` — class-scoped (`Log` alone is
14007
+ // too generic); the message is arg[1].
14008
+ { method: "Log", class: "ILogger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1], languages: ["csharp"] },
13859
14009
  // C# XPath injection — System.Xml.XPath (CWE-643). Distinctive selector methods.
13860
14010
  { method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
13861
14011
  { method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
13862
14012
  { method: "Compile", class: "XPathExpression", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
14013
+ // XPathNavigator.Select/Evaluate — class-scoped (both names collide with LINQ
14014
+ // `.Select`/`.Evaluate`, so they must resolve to an XPathNavigator receiver).
14015
+ { method: "Select", class: "XPathNavigator", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
14016
+ { method: "Evaluate", class: "XPathNavigator", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
13863
14017
  // C# XXE — untrusted XML into a parser without DTD hardening (CWE-611).
13864
14018
  { method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13865
14019
  { method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13866
14020
  { method: "Load", class: "XDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
14021
+ // `new XmlTextReader(input)` — its DtdProcessing defaults to Parse (resolves
14022
+ // external entities), unlike `XmlReader.Create` (Prohibit). CWE-611,
14023
+ // cognium-ai#318. The 4.7.0 XmlResolver=null / DtdProcessing=Prohibit
14024
+ // hardening still suppresses the safe shape.
14025
+ { method: "XmlTextReader", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13867
14026
  // Python SQLi — asyncpg Connection.*
13868
14027
  { method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
13869
14028
  { method: "fetch", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
@@ -15276,6 +15435,57 @@ function isSafeJSChildProcessCall(call, pattern, language) {
15276
15435
  if (SHELL_PROGRAMS.has(program)) return false;
15277
15436
  return true;
15278
15437
  }
15438
+ var CSHARP_SHELL_PROGRAMS = /* @__PURE__ */ new Set([
15439
+ "sh",
15440
+ "bash",
15441
+ "zsh",
15442
+ "dash",
15443
+ "ash",
15444
+ "ksh",
15445
+ "cmd",
15446
+ "powershell",
15447
+ "pwsh"
15448
+ ]);
15449
+ function isConstNonShellExe(raw) {
15450
+ if (!raw) return false;
15451
+ const t = raw.trim();
15452
+ if (!/^@?"[^"]*"$/.test(t)) return false;
15453
+ const program = (t.replace(/^@?"|"$/g, "").split(/[\\/]/).pop() ?? "").toLowerCase().replace(/\.exe$/, "");
15454
+ return !CSHARP_SHELL_PROGRAMS.has(program);
15455
+ }
15456
+ var PSI_CTOR_EXE_RE = /\bnew\s+ProcessStartInfo\s*(?:<[^>]*>)?\s*\(\s*(@?"[^"]*")/;
15457
+ function processStartInfoExe(expr, sourceLines) {
15458
+ const inline = PSI_CTOR_EXE_RE.exec(expr);
15459
+ if (inline) return inline[1];
15460
+ if (sourceLines && /^[A-Za-z_]\w*$/.test(expr.trim())) {
15461
+ const varName = expr.trim();
15462
+ const assignRe = new RegExp(
15463
+ `\\b${varName}\\s*=\\s*new\\s+ProcessStartInfo\\s*(?:<[^>]*>)?\\s*\\(\\s*(@?"[^"]*")`
15464
+ );
15465
+ for (const line of sourceLines) {
15466
+ const m = assignRe.exec(line);
15467
+ if (m) return m[1];
15468
+ }
15469
+ }
15470
+ return null;
15471
+ }
15472
+ function isSafeCSharpProcessStartCall(call, pattern, language, sourceLines) {
15473
+ if (language !== "csharp") return false;
15474
+ if (pattern.type !== "command_injection") return false;
15475
+ const method = call.method_name;
15476
+ if (method !== "Start" && method !== "ProcessStartInfo") return false;
15477
+ if (call.arguments.length >= 2) {
15478
+ const fileArg = call.arguments.find((a) => a.position === 0);
15479
+ const raw = fileArg?.literal != null ? String(fileArg.literal) : fileArg?.expression;
15480
+ return isConstNonShellExe(raw);
15481
+ }
15482
+ if (method === "Start" && call.arguments.length === 1) {
15483
+ const arg0 = call.arguments.find((a) => a.position === 0);
15484
+ const expr = (arg0?.expression ?? "").trim();
15485
+ return isConstNonShellExe(processStartInfoExe(expr, sourceLines));
15486
+ }
15487
+ return false;
15488
+ }
15279
15489
  function isSafeRustCommandCall(call, pattern, language) {
15280
15490
  if (language !== "rust") return false;
15281
15491
  if (pattern.type !== "command_injection") return false;
@@ -15531,6 +15741,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
15531
15741
  if (isSafeJSChildProcessCall(call, pattern, language)) {
15532
15742
  continue;
15533
15743
  }
15744
+ if (isSafeCSharpProcessStartCall(call, pattern, language, sourceLines)) {
15745
+ continue;
15746
+ }
15534
15747
  if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at, types)) {
15535
15748
  continue;
15536
15749
  }
@@ -16761,8 +16974,8 @@ function canSourceReachSink(sourceType, sinkType) {
16761
16974
  network_input: ["sql_injection", "command_injection", "xss", "ssrf", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
16762
16975
  config_param: ["sql_injection", "command_injection", "path_traversal", "xss", "ssrf", "log_injection", "format_string"],
16763
16976
  // Servlet init params
16764
- interprocedural_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
16765
- // Cross-method taint; Sprint 82 (#189) — open_redirect added; Sprint 91 (#117) — trust_boundary added; cognium-ai#129 — log_injection/format_string/nosql_injection added; cognium-ai#281 — prompt_injection added
16977
+ interprocedural_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection", "xxe", "deserialization"],
16978
+ // Cross-method taint; Sprint 82 (#189) — open_redirect added; Sprint 91 (#117) — trust_boundary added; cognium-ai#129 — log_injection/format_string/nosql_injection added; cognium-ai#281 — prompt_injection added; cognium-ai#317 — xxe/deserialization added (C# xxe/deser sinks are reached via param-seeded sources and were silently dropped, 0% finding conversion)
16766
16979
  plugin_param: ["sql_injection", "command_injection", "path_traversal", "xss", "code_injection", "log_injection", "format_string"]
16767
16980
  // Plugin/config parameters
16768
16981
  };
@@ -27764,8 +27977,9 @@ function buildJavaTaintedVars(sourceCode, seedVars) {
27764
27977
  if (JAVA_KEYWORDS.has(lhs)) continue;
27765
27978
  if (knownTainted.has(lhs)) continue;
27766
27979
  const escaped = (v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27980
+ const rhsCode = rhs.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ").replace(/'(?:[^'\\]|\\.)'/g, " ");
27767
27981
  const ref = [...knownTainted].some(
27768
- (v) => new RegExp(`(?<![\\p{L}\\p{N}_])${escaped(v)}(?![\\p{L}\\p{N}_])`, "u").test(rhs)
27982
+ (v) => new RegExp(`(?<![\\p{L}\\p{N}_])${escaped(v)}(?![\\p{L}\\p{N}_])`, "u").test(rhsCode)
27769
27983
  );
27770
27984
  if (ref) {
27771
27985
  derived.set(lhs, i2 + 1);
@@ -40790,6 +41004,8 @@ function isLikelyCredentialAssignment(line) {
40790
41004
  if (CRED_DYNAMIC_VALUE_RE.test(value)) return null;
40791
41005
  if (value.length < 3) return null;
40792
41006
  if (isAllSameChar(value)) return null;
41007
+ if (value.toLowerCase() === name2.toLowerCase()) return null;
41008
+ if (/^(?:https?:\/\/|urn:|xmlns)/i.test(value)) return null;
40793
41009
  if (value.length < 12) return null;
40794
41010
  if (shannonEntropy(value) < 3.5) return null;
40795
41011
  if (charClassDiversity(value) < 2) return null;
@@ -43346,8 +43562,12 @@ var InsecureDeserializationConfigPass = class {
43346
43562
  name = "insecure-deserialization-config";
43347
43563
  category = "security";
43348
43564
  run(ctx) {
43565
+ if (ctx.language === "java") return this.runJava(ctx);
43566
+ if (ctx.language === "csharp") return this.runCSharp(ctx);
43567
+ return { findings: [] };
43568
+ }
43569
+ runJava(ctx) {
43349
43570
  const { graph, language } = ctx;
43350
- if (language !== "java") return { findings: [] };
43351
43571
  const file = graph.ir.meta.file;
43352
43572
  const findings = [];
43353
43573
  for (const call of graph.ir.calls) {
@@ -43372,12 +43592,46 @@ var InsecureDeserializationConfigPass = class {
43372
43592
  }
43373
43593
  return { findings };
43374
43594
  }
43595
+ // C# — Json.NET `TypeNameHandling` set to a value other than `None` (ca#318).
43596
+ // `TypeNameHandling.All/Auto/Objects/Arrays` makes Json.NET honour a `$type`
43597
+ // field in the payload and instantiate the named .NET type, so
43598
+ // `JsonConvert.DeserializeObject(untrusted, settings)` becomes a gadget-chain
43599
+ // RCE (the .NET analogue of XStream's grant-all). The vulnerability is the
43600
+ // constant setting, independent of flow — the secure value `None` never matches.
43601
+ runCSharp(ctx) {
43602
+ const file = ctx.graph.ir.meta.file;
43603
+ const findings = [];
43604
+ const lines = ctx.code.split("\n");
43605
+ for (let i2 = 0; i2 < lines.length; i2++) {
43606
+ const m = INSECURE_TYPE_NAME_HANDLING_RE.exec(lines[i2]);
43607
+ if (!m) continue;
43608
+ const line = i2 + 1;
43609
+ const api = `TypeNameHandling = TypeNameHandling.${m[1]}`;
43610
+ findings.push({ line, api });
43611
+ ctx.addFinding({
43612
+ id: `${this.name}-${file}-${line}`,
43613
+ pass: this.name,
43614
+ category: this.category,
43615
+ rule_id: this.name,
43616
+ cwe: "CWE-502",
43617
+ severity: "high",
43618
+ level: "error",
43619
+ message: `Json.NET configured with TypeNameHandling.${m[1]}: a $type field in untrusted JSON can instantiate arbitrary .NET types (deserialization RCE)`,
43620
+ file,
43621
+ line,
43622
+ fix: "Use TypeNameHandling.None (the default), or bind a SerializationBinder that allow-lists the exact types you deserialize.",
43623
+ evidence: { api, language: "csharp" }
43624
+ });
43625
+ }
43626
+ return { findings };
43627
+ }
43375
43628
  isPermissiveXStreamConfig(call) {
43376
43629
  if (call.method_name !== "addPermission") return false;
43377
43630
  const arg0 = call.arguments[0]?.expression;
43378
43631
  return typeof arg0 === "string" && ANY_TYPE_PERMISSION_RE.test(arg0);
43379
43632
  }
43380
43633
  };
43634
+ var INSECURE_TYPE_NAME_HANDLING_RE = /\bTypeNameHandling\s*=\s*(?:Newtonsoft\.Json\.)?TypeNameHandling\.(All|Auto|Objects|Arrays)\b/;
43381
43635
 
43382
43636
  // src/analysis/passes/plaintext-password-storage-pass.ts
43383
43637
  function isWriteStorageCall(call, language) {
@@ -45541,6 +45795,25 @@ function getNodeTypesForLanguage(language) {
45541
45795
  "selector_expression",
45542
45796
  "identifier"
45543
45797
  ]);
45798
+ case "csharp":
45799
+ return /* @__PURE__ */ new Set([
45800
+ "method_invocation",
45801
+ "object_creation_expression",
45802
+ "class_declaration",
45803
+ "method_declaration",
45804
+ "constructor_declaration",
45805
+ "field_declaration",
45806
+ "import_declaration",
45807
+ "interface_declaration",
45808
+ "enum_declaration",
45809
+ "package_declaration",
45810
+ "local_variable_declaration",
45811
+ // buildCSharpCFG method-like containers
45812
+ "destructor_declaration",
45813
+ "operator_declaration",
45814
+ "local_function_statement",
45815
+ "accessor_declaration"
45816
+ ]);
45544
45817
  default:
45545
45818
  return /* @__PURE__ */ new Set([
45546
45819
  "method_invocation",