cognium-dev 3.216.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +7 -1
  2. package/dist/cli.js +571 -18
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -65,7 +65,7 @@ Scan files or directories for security vulnerabilities.
65
65
  cognium-dev scan <path> [options]
66
66
 
67
67
  Options:
68
- -l, --language <lang> Force language (java|javascript|typescript|python|go|rust|bash|html)
68
+ -l, --language <lang> Force language (java|javascript|typescript|python|go|rust|bash|html|csharp)
69
69
  -f, --format <format> Output format (text|json|sarif) [default: text]
70
70
  --threads <n> Parallel analysis threads [default: 4]
71
71
  --severity <level> Filter by severity:
@@ -307,6 +307,12 @@ Filter to security findings only: `cognium-dev scan ./src --category security`
307
307
  | Rust | `.rs` | Actix-web, Rocket, Axum |
308
308
  | Bash | `.sh`, `.bash` | Shell scripts |
309
309
  | HTML | `.html`, `.htm` | Web extraction preprocessor |
310
+ | C#/.NET _(experimental)_ | `.cs` | ASP.NET Core, ADO.NET, EF Core |
311
+
312
+ > **C#/.NET is experimental / preview** (since 4.0.0). Straight-line taint
313
+ > analysis across 10 CWE families (SQLi, command injection, path traversal,
314
+ > SSRF, code injection, XSS, deserialization, LDAP, XPath, XXE). Not yet
315
+ > benchmark-verified — expect gaps in branch/alias precision and detector breadth.
310
316
 
311
317
  ## Configuration
312
318
 
package/dist/cli.js CHANGED
@@ -3627,8 +3627,87 @@ function extractTypes(tree, cache, language) {
3627
3627
  if (isJavaScript) {
3628
3628
  return extractJavaScriptTypes(tree, cache);
3629
3629
  }
3630
+ if (effectiveLanguage === "csharp") {
3631
+ return extractCSharpTypes(tree, cache);
3632
+ }
3630
3633
  return extractJavaTypes(tree, cache);
3631
3634
  }
3635
+ function extractCSharpTypes(tree, cache) {
3636
+ const types = [];
3637
+ const KINDS = {
3638
+ class_declaration: "class",
3639
+ record_declaration: "class",
3640
+ struct_declaration: "class",
3641
+ interface_declaration: "interface",
3642
+ enum_declaration: "enum"
3643
+ };
3644
+ for (const kindNode of Object.keys(KINDS)) {
3645
+ for (const node of getNodesFromCache(tree.rootNode, kindNode, cache)) {
3646
+ const nameNode = node.childForFieldName("name");
3647
+ const body2 = node.childForFieldName("body");
3648
+ const methods = [];
3649
+ if (body2) {
3650
+ for (let i2 = 0;i2 < body2.childCount; i2++) {
3651
+ const m = body2.child(i2);
3652
+ if (!m || m.type !== "method_declaration")
3653
+ continue;
3654
+ const mName = m.childForFieldName("name");
3655
+ const paramList = m.childForFieldName("parameters");
3656
+ const parameters = [];
3657
+ if (paramList) {
3658
+ for (let j = 0;j < paramList.childCount; j++) {
3659
+ const pnode = paramList.child(j);
3660
+ if (!pnode || pnode.type !== "parameter")
3661
+ continue;
3662
+ const pName = pnode.childForFieldName("name");
3663
+ const pType = pnode.childForFieldName("type");
3664
+ if (pName) {
3665
+ parameters.push({
3666
+ name: getNodeText(pName),
3667
+ type: pType ? getNodeText(pType) : null,
3668
+ annotations: [],
3669
+ line: pnode.startPosition.row + 1
3670
+ });
3671
+ }
3672
+ }
3673
+ }
3674
+ const returns = m.childForFieldName("returns");
3675
+ methods.push({
3676
+ name: mName ? getNodeText(mName) : "unknown",
3677
+ return_type: returns ? getNodeText(returns) : null,
3678
+ parameters,
3679
+ annotations: [],
3680
+ modifiers: extractCSharpModifiers(m),
3681
+ start_line: m.startPosition.row + 1,
3682
+ end_line: m.endPosition.row + 1
3683
+ });
3684
+ }
3685
+ }
3686
+ types.push({
3687
+ name: nameNode ? getNodeText(nameNode) : "anonymous",
3688
+ kind: KINDS[kindNode],
3689
+ package: null,
3690
+ extends: null,
3691
+ implements: [],
3692
+ annotations: [],
3693
+ methods,
3694
+ fields: [],
3695
+ start_line: node.startPosition.row + 1,
3696
+ end_line: node.endPosition.row + 1
3697
+ });
3698
+ }
3699
+ }
3700
+ return types;
3701
+ }
3702
+ function extractCSharpModifiers(node) {
3703
+ const mods = [];
3704
+ for (let i2 = 0;i2 < node.childCount; i2++) {
3705
+ const c = node.child(i2);
3706
+ if (c && c.type === "modifier")
3707
+ mods.push(getNodeText(c));
3708
+ }
3709
+ return mods;
3710
+ }
3632
3711
  function extractJavaTypes(tree, cache) {
3633
3712
  const types = [];
3634
3713
  const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
@@ -5133,6 +5212,9 @@ function extractCalls(tree, cache, language) {
5133
5212
  if (isJavaScript) {
5134
5213
  return extractJavaScriptCalls(tree, cache);
5135
5214
  }
5215
+ if (detectedLanguage === "csharp") {
5216
+ return extractCSharpCalls(tree, cache);
5217
+ }
5136
5218
  const context = buildResolutionContext(tree, cache);
5137
5219
  const invocations = getNodesFromCache(tree.rootNode, "method_invocation", cache);
5138
5220
  for (const inv of invocations) {
@@ -5147,6 +5229,115 @@ function extractCalls(tree, cache, language) {
5147
5229
  }
5148
5230
  return calls;
5149
5231
  }
5232
+ function buildCSharpReceiverTypeMap(tree, cache) {
5233
+ const map = new Map;
5234
+ const simple = (t) => t.replace(/<[^>]*>/g, "").split(".").pop()?.trim() ?? t;
5235
+ for (const vd of getNodesFromCache(tree.rootNode, "variable_declaration", cache)) {
5236
+ const typeNode = vd.childForFieldName("type");
5237
+ const declaredType = typeNode ? getNodeText(typeNode) : null;
5238
+ for (let i2 = 0;i2 < vd.childCount; i2++) {
5239
+ const decl = vd.child(i2);
5240
+ if (!decl || decl.type !== "variable_declarator")
5241
+ continue;
5242
+ const nameNode = decl.childForFieldName("name");
5243
+ if (nameNode?.type !== "identifier")
5244
+ continue;
5245
+ let t = declaredType && declaredType !== "var" ? declaredType : null;
5246
+ if (!t) {
5247
+ const oce = findFirstDescendant(decl, "object_creation_expression");
5248
+ const oceType = oce?.childForFieldName("type");
5249
+ if (oceType)
5250
+ t = getNodeText(oceType);
5251
+ }
5252
+ if (t && t !== "var")
5253
+ map.set(getNodeText(nameNode), simple(t));
5254
+ }
5255
+ }
5256
+ return map;
5257
+ }
5258
+ function findFirstDescendant(node, type) {
5259
+ for (let i2 = 0;i2 < node.childCount; i2++) {
5260
+ const c = node.child(i2);
5261
+ if (!c)
5262
+ continue;
5263
+ if (c.type === type)
5264
+ return c;
5265
+ const found = findFirstDescendant(c, type);
5266
+ if (found)
5267
+ return found;
5268
+ }
5269
+ return null;
5270
+ }
5271
+ function extractCSharpCalls(tree, cache) {
5272
+ const calls = [];
5273
+ const typeMap = buildCSharpReceiverTypeMap(tree, cache);
5274
+ const invocations = getNodesFromCache(tree.rootNode, "invocation_expression", cache);
5275
+ for (const inv of invocations) {
5276
+ const fn = inv.childForFieldName("function");
5277
+ let methodName = "unknown";
5278
+ let receiver = null;
5279
+ if (fn?.type === "member_access_expression") {
5280
+ const nameNode = fn.childForFieldName("name");
5281
+ const exprNode = fn.childForFieldName("expression");
5282
+ methodName = nameNode ? getNodeText(nameNode) : "unknown";
5283
+ receiver = exprNode ? getNodeText(exprNode) : null;
5284
+ } else if (fn) {
5285
+ methodName = getNodeText(fn);
5286
+ }
5287
+ const argsNode = inv.childForFieldName("arguments");
5288
+ calls.push({
5289
+ method_name: methodName,
5290
+ receiver,
5291
+ receiver_type: receiver ? typeMap.get(receiver) ?? null : null,
5292
+ receiver_type_fqn: null,
5293
+ arguments: argsNode ? extractCSharpArguments(argsNode) : [],
5294
+ location: { line: inv.startPosition.row + 1, column: inv.startPosition.column },
5295
+ in_method: findEnclosingMethod(inv)
5296
+ });
5297
+ }
5298
+ const objectCreations = getNodesFromCache(tree.rootNode, "object_creation_expression", cache);
5299
+ for (const creation of objectCreations) {
5300
+ const typeNode = creation.childForFieldName("type");
5301
+ const argsNode = creation.childForFieldName("arguments");
5302
+ calls.push({
5303
+ method_name: typeNode ? getNodeText(typeNode) : "unknown",
5304
+ receiver: null,
5305
+ receiver_type: null,
5306
+ receiver_type_fqn: null,
5307
+ arguments: argsNode ? extractCSharpArguments(argsNode) : [],
5308
+ location: { line: creation.startPosition.row + 1, column: creation.startPosition.column },
5309
+ in_method: findEnclosingMethod(creation),
5310
+ is_constructor: true
5311
+ });
5312
+ }
5313
+ return calls;
5314
+ }
5315
+ function extractCSharpArguments(argsNode) {
5316
+ const args2 = [];
5317
+ let position = 0;
5318
+ for (let i2 = 0;i2 < argsNode.childCount; i2++) {
5319
+ const child = argsNode.child(i2);
5320
+ if (!child || child.type !== "argument")
5321
+ continue;
5322
+ let expr = null;
5323
+ for (let j = child.childCount - 1;j >= 0; j--) {
5324
+ const c = child.child(j);
5325
+ if (c && c.isNamed) {
5326
+ expr = c;
5327
+ break;
5328
+ }
5329
+ }
5330
+ const text = expr ? getNodeText(expr) : getNodeText(child);
5331
+ args2.push({
5332
+ position: position++,
5333
+ expression: text,
5334
+ variable: expr?.type === "identifier" ? text : null,
5335
+ literal: expr?.type === "string_literal" ? text : null,
5336
+ value: null
5337
+ });
5338
+ }
5339
+ return args2;
5340
+ }
5150
5341
  function extractJavaScriptCalls(tree, cache) {
5151
5342
  const calls = [];
5152
5343
  const context = buildJSResolutionContext(tree, cache);
@@ -8337,8 +8528,72 @@ function buildDFG(tree, cache, language) {
8337
8528
  if (effectiveLanguage === "go") {
8338
8529
  return buildGoDFG(tree);
8339
8530
  }
8531
+ if (effectiveLanguage === "csharp") {
8532
+ return buildCSharpDFG(tree, cache);
8533
+ }
8340
8534
  return buildJavaDFG(tree, cache);
8341
8535
  }
8536
+ function buildCSharpDFG(tree, cache) {
8537
+ const defs = [];
8538
+ const uses = [];
8539
+ let defIdCounter = 1;
8540
+ let useIdCounter = 1;
8541
+ const scopeStack = [new Map];
8542
+ const methods = [
8543
+ ...getNodesFromCache(tree.rootNode, "method_declaration", cache),
8544
+ ...getNodesFromCache(tree.rootNode, "constructor_declaration", cache),
8545
+ ...getNodesFromCache(tree.rootNode, "local_function_statement", cache)
8546
+ ];
8547
+ for (const method of methods) {
8548
+ scopeStack.push(new Map);
8549
+ const params = method.childForFieldName("parameters");
8550
+ if (params) {
8551
+ for (const def of extractParameterDefs(params, defIdCounter)) {
8552
+ defs.push(def);
8553
+ currentScope(scopeStack).set(def.variable, def.id);
8554
+ defIdCounter++;
8555
+ }
8556
+ }
8557
+ const body2 = method.childForFieldName("body");
8558
+ if (body2) {
8559
+ for (const decl of findNodes(body2, "variable_declarator")) {
8560
+ const nameNode = decl.childForFieldName("name");
8561
+ if (nameNode?.type === "identifier") {
8562
+ const name2 = getNodeText(nameNode);
8563
+ const def = { id: defIdCounter++, variable: name2, line: decl.startPosition.row + 1, kind: "local" };
8564
+ defs.push(def);
8565
+ currentScope(scopeStack).set(name2, def.id);
8566
+ }
8567
+ }
8568
+ for (const asn of findNodes(body2, "assignment_expression")) {
8569
+ const left = asn.childForFieldName("left");
8570
+ if (left?.type === "identifier") {
8571
+ const name2 = getNodeText(left);
8572
+ const def = { id: defIdCounter++, variable: name2, line: asn.startPosition.row + 1, kind: "local" };
8573
+ defs.push(def);
8574
+ currentScope(scopeStack).set(name2, def.id);
8575
+ }
8576
+ }
8577
+ const bodyUses = extractUses(body2, useIdCounter, scopeStack, false);
8578
+ uses.push(...bodyUses.uses);
8579
+ useIdCounter = bodyUses.nextId;
8580
+ }
8581
+ scopeStack.pop();
8582
+ }
8583
+ const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
8584
+ for (const cls of classes) {
8585
+ const body2 = cls.childForFieldName("body");
8586
+ if (body2) {
8587
+ for (const def of extractFieldDefs(body2, defIdCounter)) {
8588
+ defs.push(def);
8589
+ currentScope(scopeStack).set(def.variable, def.id);
8590
+ defIdCounter++;
8591
+ }
8592
+ }
8593
+ }
8594
+ const chains = computeChains(defs, uses);
8595
+ return { defs, uses, chains };
8596
+ }
8342
8597
  function buildJavaDFG(tree, cache) {
8343
8598
  const defs = [];
8344
8599
  const uses = [];
@@ -8808,7 +9063,7 @@ function extractParameterDefs(params, startId) {
8808
9063
  const param = params.child(i2);
8809
9064
  if (!param)
8810
9065
  continue;
8811
- if (param.type === "formal_parameter" || param.type === "spread_parameter") {
9066
+ if (param.type === "formal_parameter" || param.type === "spread_parameter" || param.type === "parameter") {
8812
9067
  const nameNode = param.childForFieldName("name");
8813
9068
  if (nameNode) {
8814
9069
  defs.push({
@@ -12020,6 +12275,55 @@ var DEFAULT_SINKS = [
12020
12275
  { method: "put", class: "httpx", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
12021
12276
  { method: "patch", class: "httpx", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
12022
12277
  { method: "head", class: "httpx", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
12278
+ { method: "SqlCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12279
+ { method: "NpgsqlCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12280
+ { method: "MySqlCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12281
+ { method: "SqliteCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12282
+ { method: "OracleCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12283
+ { method: "ExecuteSqlRaw", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12284
+ { method: "ExecuteSqlRawAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12285
+ { method: "FromSqlRaw", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12286
+ { method: "FromSqlRawAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12287
+ { method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12288
+ { method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
12289
+ { method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12290
+ { method: "ReadAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12291
+ { method: "ReadAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12292
+ { method: "WriteAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12293
+ { method: "WriteAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12294
+ { method: "AppendAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12295
+ { method: "OpenRead", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12296
+ { method: "OpenWrite", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12297
+ { method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12298
+ { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12299
+ { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12300
+ { method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12301
+ { method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12302
+ { method: "PutAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12303
+ { method: "DeleteAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12304
+ { method: "GetStringAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12305
+ { method: "GetByteArrayAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12306
+ { method: "GetStreamAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12307
+ { method: "SendAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12308
+ { method: "DownloadString", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12309
+ { method: "DownloadData", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12310
+ { method: "DownloadFile", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [1], languages: ["csharp"] },
12311
+ { method: "Create", class: "WebRequest", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12312
+ { method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12313
+ { method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12314
+ { method: "Deserialize", class: "BinaryFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12315
+ { method: "Deserialize", class: "NetDataContractSerializer", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12316
+ { method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
12317
+ { method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
12318
+ { method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
12319
+ { method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
12320
+ { method: "DirectoryEntry", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
12321
+ { method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
12322
+ { method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
12323
+ { method: "Compile", class: "XPathExpression", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
12324
+ { method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
12325
+ { method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
12326
+ { method: "Load", class: "XDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
12023
12327
  { method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
12024
12328
  { method: "fetch", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
12025
12329
  { method: "fetchrow", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
@@ -12138,6 +12442,10 @@ var DEFAULT_SANITIZERS = [
12138
12442
  { method: "escapeEcmaScript", removes: ["xss"] },
12139
12443
  { method: "escapeCsv", removes: ["xss"] },
12140
12444
  { method: "sanitize", class: "PolicyFactory", removes: ["xss"] },
12445
+ { method: "HtmlEncode", removes: ["xss"] },
12446
+ { method: "JavaScriptStringEncode", removes: ["xss"] },
12447
+ { method: "Encode", class: "HtmlEncoder", removes: ["xss"] },
12448
+ { method: "GetFileName", class: "Path", removes: ["path_traversal"] },
12141
12449
  { method: "encodeToString", class: "Encoder", removes: ["sql_injection", "command_injection", "xss", "path_traversal", "code_injection"] },
12142
12450
  { method: "encode", class: "Encoder", removes: ["sql_injection", "command_injection", "xss", "path_traversal", "code_injection"] },
12143
12451
  { method: "encodeHexString", class: "Hex", removes: ["sql_injection", "command_injection", "xss", "path_traversal", "code_injection"] },
@@ -12790,7 +13098,7 @@ function findSources(calls, types, patterns, sourceLines, language) {
12790
13098
  if (skipMethods.includes(method.name))
12791
13099
  continue;
12792
13100
  for (const param of method.parameters) {
12793
- const isTaintable = param.type ? isInterproceduralTaintableType(param.type) : true;
13101
+ const isTaintable = param.type ? isInterproceduralTaintableType(param.type, language) : true;
12794
13102
  if (isTaintable) {
12795
13103
  const paramLine = param.line ?? method.start_line;
12796
13104
  sources.push({
@@ -12800,7 +13108,7 @@ function findSources(calls, types, patterns, sourceLines, language) {
12800
13108
  line: paramLine,
12801
13109
  confidence: param.type ? 0.7 : 0.5,
12802
13110
  in_method: method.name,
12803
- ...language === "java" ? { variable: param.name } : {}
13111
+ ...language === "java" || language === "csharp" ? { variable: param.name } : {}
12804
13112
  });
12805
13113
  }
12806
13114
  }
@@ -12903,7 +13211,7 @@ function findSources(calls, types, patterns, sourceLines, language) {
12903
13211
  }
12904
13212
  return result;
12905
13213
  }
12906
- function isInterproceduralTaintableType(typeName) {
13214
+ function isInterproceduralTaintableType(typeName, language) {
12907
13215
  const baseType = typeName.split("<")[0].trim();
12908
13216
  const excludedTypes = [
12909
13217
  "HttpServletRequest",
@@ -12966,6 +13274,26 @@ function isInterproceduralTaintableType(typeName) {
12966
13274
  if (taintableTypes.includes(baseType)) {
12967
13275
  return true;
12968
13276
  }
13277
+ if (language === "csharp") {
13278
+ const csharpTaintable = [
13279
+ "string",
13280
+ "object",
13281
+ "IEnumerable",
13282
+ "ICollection",
13283
+ "IList",
13284
+ "Dictionary",
13285
+ "IDictionary",
13286
+ "IReadOnlyList",
13287
+ "IReadOnlyCollection",
13288
+ "Stream",
13289
+ "byte[]",
13290
+ "Byte[]",
13291
+ "MemoryStream"
13292
+ ];
13293
+ const leaf = baseType.split(".").pop() ?? baseType;
13294
+ if (csharpTaintable.includes(baseType) || csharpTaintable.includes(leaf))
13295
+ return true;
13296
+ }
12969
13297
  if (typeName.endsWith("[]")) {
12970
13298
  const elementType = typeName.slice(0, -2);
12971
13299
  if (elementType === "String" || elementType === "Object" || elementType === "byte") {
@@ -14549,19 +14877,19 @@ function walkBackwardDefs(startDefId, chainsByToDef, defById, options = {}) {
14549
14877
  // ../circle-ir/dist/analysis/findings.js
14550
14878
  function canSourceReachSink(sourceType, sinkType) {
14551
14879
  const sourceToSinkMapping = {
14552
- http_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "deserialization", "log_injection", "format_string", "nosql_injection"],
14553
- http_body: ["sql_injection", "command_injection", "deserialization", "xxe", "xss", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection"],
14554
- http_header: ["sql_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection"],
14555
- http_cookie: ["sql_injection", "xss", "mybatis_mapper_call", "code_injection", "crlf", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection"],
14556
- http_path: ["path_traversal", "sql_injection", "ssrf", "mybatis_mapper_call", "open_redirect", "trust_boundary", "xss", "log_injection", "format_string"],
14557
- http_query: ["sql_injection", "command_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "deserialization", "log_injection", "format_string", "nosql_injection"],
14558
- io_input: ["command_injection", "path_traversal", "deserialization", "xxe", "code_injection", "xss", "ssrf", "log_injection", "format_string"],
14880
+ http_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "deserialization", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
14881
+ http_body: ["sql_injection", "command_injection", "deserialization", "xxe", "xss", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
14882
+ http_header: ["sql_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
14883
+ http_cookie: ["sql_injection", "xss", "mybatis_mapper_call", "code_injection", "crlf", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
14884
+ http_path: ["path_traversal", "sql_injection", "ssrf", "mybatis_mapper_call", "open_redirect", "trust_boundary", "xss", "log_injection", "format_string", "prompt_injection"],
14885
+ http_query: ["sql_injection", "command_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "deserialization", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
14886
+ io_input: ["command_injection", "path_traversal", "deserialization", "xxe", "code_injection", "xss", "ssrf", "log_injection", "format_string", "prompt_injection"],
14559
14887
  env_input: ["command_injection", "path_traversal"],
14560
14888
  db_input: ["xss", "sql_injection", "log_injection"],
14561
14889
  file_input: ["deserialization", "xxe", "path_traversal", "command_injection", "code_injection"],
14562
- network_input: ["sql_injection", "command_injection", "xss", "ssrf", "log_injection", "format_string", "nosql_injection"],
14890
+ network_input: ["sql_injection", "command_injection", "xss", "ssrf", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
14563
14891
  config_param: ["sql_injection", "command_injection", "path_traversal", "xss", "ssrf", "log_injection", "format_string"],
14564
- 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"],
14892
+ 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"],
14565
14893
  plugin_param: ["sql_injection", "command_injection", "path_traversal", "xss", "code_injection", "log_injection", "format_string"]
14566
14894
  };
14567
14895
  const validSinks = sourceToSinkMapping[sourceType];
@@ -19694,6 +20022,10 @@ function applyLibraryApiSurfaceDowngrade(findings) {
19694
20022
  }
19695
20023
 
19696
20024
  // ../circle-ir/dist/analysis/note-coalescer.js
20025
+ var CLICKJACKING_PAIR = new Set(["missing-x-frame-options", "missing-csp-frame-ancestors"]);
20026
+ function levelRank(level) {
20027
+ return level === "error" ? 0 : level === "warning" ? 1 : 2;
20028
+ }
19697
20029
  function coalesceNoteLevelFindings(findings) {
19698
20030
  if (findings.length < 2)
19699
20031
  return [...findings];
@@ -19711,11 +20043,27 @@ function coalesceNoteLevelFindings(findings) {
19711
20043
  }
19712
20044
  const out2 = [];
19713
20045
  for (const key of order) {
19714
- const bucket = groups.get(key);
20046
+ let bucket = groups.get(key);
19715
20047
  if (bucket.length === 1) {
19716
20048
  out2.push(bucket[0]);
19717
20049
  continue;
19718
20050
  }
20051
+ const cj = bucket.filter((f) => CLICKJACKING_PAIR.has(f.rule_id));
20052
+ if (new Set(cj.map((f) => f.rule_id)).size === CLICKJACKING_PAIR.size) {
20053
+ const primary2 = [...cj].sort((a, b) => levelRank(a.level) - levelRank(b.level) || a.rule_id.localeCompare(b.rule_id))[0];
20054
+ const labels = Array.from(new Set([
20055
+ ...primary2.labels ?? [],
20056
+ ...cj.flatMap((f) => [f.rule_id, ...f.labels ?? []])
20057
+ ])).filter((l) => l !== primary2.rule_id);
20058
+ out2.push({ ...primary2, labels });
20059
+ bucket = bucket.filter((f) => !CLICKJACKING_PAIR.has(f.rule_id));
20060
+ if (bucket.length === 0)
20061
+ continue;
20062
+ if (bucket.length === 1) {
20063
+ out2.push(bucket[0]);
20064
+ continue;
20065
+ }
20066
+ }
19719
20067
  const allNote = bucket.every((f) => f.level === "note");
19720
20068
  if (!allNote) {
19721
20069
  for (const f of bucket)
@@ -23377,6 +23725,65 @@ class GoPlugin extends BaseLanguagePlugin {
23377
23725
  }
23378
23726
  }
23379
23727
 
23728
+ // ../circle-ir/dist/languages/plugins/csharp.js
23729
+ class CSharpPlugin extends BaseLanguagePlugin {
23730
+ id = "csharp";
23731
+ name = "C#";
23732
+ extensions = [".cs"];
23733
+ wasmPath = "tree-sitter-csharp.wasm";
23734
+ nodeTypes = {
23735
+ classDeclaration: ["class_declaration", "record_declaration", "struct_declaration"],
23736
+ interfaceDeclaration: ["interface_declaration"],
23737
+ enumDeclaration: ["enum_declaration"],
23738
+ functionDeclaration: [],
23739
+ methodDeclaration: ["method_declaration", "constructor_declaration", "local_function_statement"],
23740
+ methodCall: ["invocation_expression"],
23741
+ functionCall: [],
23742
+ assignment: ["assignment_expression"],
23743
+ variableDeclaration: ["local_declaration_statement", "field_declaration", "variable_declaration"],
23744
+ parameter: ["parameter"],
23745
+ argument: ["argument_list"],
23746
+ annotation: ["attribute", "attribute_list"],
23747
+ decorator: [],
23748
+ importStatement: ["using_directive"],
23749
+ ifStatement: ["if_statement"],
23750
+ forStatement: ["for_statement", "for_each_statement"],
23751
+ whileStatement: ["while_statement"],
23752
+ tryStatement: ["try_statement"],
23753
+ returnStatement: ["return_statement"]
23754
+ };
23755
+ detectFramework(context) {
23756
+ for (const imp of context.imports) {
23757
+ const path = imp.from_package || imp.imported_name;
23758
+ if (path.startsWith("Microsoft.AspNetCore") || path.startsWith("Microsoft.Extensions")) {
23759
+ return { name: "aspnetcore", confidence: 0.9, indicators: [`using: ${path}`] };
23760
+ }
23761
+ if (path.startsWith("Microsoft.EntityFrameworkCore")) {
23762
+ return { name: "efcore", confidence: 0.9, indicators: [`using: ${path}`] };
23763
+ }
23764
+ }
23765
+ return;
23766
+ }
23767
+ extractTypes(_context) {
23768
+ return [];
23769
+ }
23770
+ extractCalls(_context) {
23771
+ return [];
23772
+ }
23773
+ extractImports(_context) {
23774
+ return [];
23775
+ }
23776
+ extractPackage(_context) {
23777
+ return;
23778
+ }
23779
+ getBuiltinSources() {
23780
+ return [];
23781
+ }
23782
+ getBuiltinSinks() {
23783
+ return [];
23784
+ }
23785
+ }
23786
+
23380
23787
  // ../circle-ir/dist/languages/plugins/index.js
23381
23788
  function registerBuiltinPlugins() {
23382
23789
  registerLanguage(new JavaPlugin);
@@ -23387,6 +23794,7 @@ function registerBuiltinPlugins() {
23387
23794
  registerLanguage(new HtmlPlugin);
23388
23795
  registerLanguage(new VuePlugin);
23389
23796
  registerLanguage(new GoPlugin);
23797
+ registerLanguage(new CSharpPlugin);
23390
23798
  }
23391
23799
  // ../circle-ir/dist/analysis/passes/cross-file-pass.js
23392
23800
  class CrossFilePass {
@@ -24570,6 +24978,7 @@ class LanguageSourcesPass {
24570
24978
  additionalSources.push(...findStaticFieldSources(types, code, language));
24571
24979
  additionalSources.push(...findSetterChainSources(types, code, language));
24572
24980
  additionalSources.push(...findJavaScriptAssignmentSources(code, language));
24981
+ additionalSources.push(...findCSharpRequestSources(code, language));
24573
24982
  const jsDOMSinks = findJavaScriptDOMSinks(code, language);
24574
24983
  for (const s of jsDOMSinks) {
24575
24984
  const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
@@ -25172,12 +25581,43 @@ function findSetterChainSources(types, sourceCode, language) {
25172
25581
  }
25173
25582
  return sources;
25174
25583
  }
25584
+ function findCSharpRequestSources(sourceCode, language) {
25585
+ if (language !== "csharp")
25586
+ return [];
25587
+ const sources = [];
25588
+ const lines = sourceCode.split(`
25589
+ `);
25590
+ const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
25591
+ const requestReadRe = /\bRequest\s*\.\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\b/;
25592
+ for (let i2 = 0;i2 < lines.length; i2++) {
25593
+ const m = assignRe.exec(lines[i2]);
25594
+ if (!m)
25595
+ continue;
25596
+ const [, varName, rhs] = m;
25597
+ if (!requestReadRe.test(rhs))
25598
+ continue;
25599
+ const lineNumber = i2 + 1;
25600
+ if (sources.some((s) => s.line === lineNumber && s.variable === varName))
25601
+ continue;
25602
+ sources.push({
25603
+ type: "http_param",
25604
+ location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
25605
+ severity: "high",
25606
+ line: lineNumber,
25607
+ confidence: 1,
25608
+ variable: varName
25609
+ });
25610
+ }
25611
+ return sources;
25612
+ }
25175
25613
  function findJavaScriptAssignmentSources(sourceCode, language) {
25176
25614
  if (!["javascript", "typescript"].includes(language))
25177
25615
  return [];
25178
25616
  const sources = [];
25179
25617
  const lines = sourceCode.split(`
25180
25618
  `);
25619
+ const isRouteHandler = /export\s+(?:async\s+)?(?:function\s+(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|loader|action)\b|const\s+(?:GET|POST|PUT|PATCH|DELETE|loader|action)\b)/.test(sourceCode) && /\{[^}]*\bparams\b[^}]*\}/.test(sourceCode);
25620
+ const patterns = isRouteHandler ? [...JS_TAINTED_PATTERNS, { pattern: /\bparams\s*[.[]/, type: "http_path" }] : JS_TAINTED_PATTERNS;
25181
25621
  for (let lineNum = 0;lineNum < lines.length; lineNum++) {
25182
25622
  const line = lines[lineNum];
25183
25623
  const lineNumber = lineNum + 1;
@@ -25185,7 +25625,7 @@ function findJavaScriptAssignmentSources(sourceCode, language) {
25185
25625
  if (!assignmentMatch)
25186
25626
  continue;
25187
25627
  const [, varName, rhs] = assignmentMatch;
25188
- for (const { pattern, type } of JS_TAINTED_PATTERNS) {
25628
+ for (const { pattern, type } of patterns) {
25189
25629
  if (pattern.test(rhs)) {
25190
25630
  const alreadyExists = sources.some((s) => s.line === lineNumber && s.type === type);
25191
25631
  if (!alreadyExists) {
@@ -31857,6 +32297,13 @@ var REPLACE_ALL_TO_PLACEHOLDER_RE = /\.replaceAll\s*\([\s\S]*?,\s*"[\s,?]*\?[\s,
31857
32297
  var HTML_CONTENT_TYPE_RE = /text\/html|TEXT_HTML/;
31858
32298
  var FETCH_GLOBAL_RECEIVER_RE = /(?:^|[^.\w])(?:window|globalThis|self|global)\s*\.\s*fetch\s*\(/;
31859
32299
  var FETCH_MEMBER_RECEIVER_RE = /[\w$)\]]\s*\.\s*fetch\s*\(/;
32300
+ var NOSQL_ARRAY_METHODS = new Set(["find", "filter", "some", "every", "findIndex", "findLast"]);
32301
+ var FUNCTION_FIRST_ARG_RE = /\.\s*(?:find|filter|some|every|findIndex|findLast)\s*\(\s*(?:async\s+)?(?:function\b|(?:\([^()]*\)|[A-Za-z_$][\w$]*)\s*=>)/;
32302
+ var ORM_BUILDER_KEY_RE = /[{,]\s*(?:where|select|populate|include|orderBy|relations|attributes)\s*:/;
32303
+ var BROWSER_COMPONENT_EXT_RE = /\.(?:jsx|tsx|vue|svelte)$/;
32304
+ var CLIENT_SIGNAL_RE = /["']use client["']|\buse(?:State|Effect|Ref|Callback|Memo|Context|Reducer|LayoutEffect)\s*\(|\b(?:window|document|localStorage|sessionStorage|navigator)\s*\.|\son(?:Click|Change|Submit|Input|KeyDown|KeyUp|MouseOver|Focus|Blur)\s*=/;
32305
+ var SERVER_SIGNAL_RE = /["']use server["']|\b(?:getServerSideProps|getStaticProps|getStaticPaths)\b|from\s+["']next\/server["']|\bNext(?:Api)?(?:Request|Response)\b|from\s+["'](?:node:)?(?:fs|child_process|http|https|net|dns|dgram)["']|\bexport\s+(?:default\s+)?(?:async\s+)?function\s+handler\s*\(/;
32306
+ var FIXED_HOST_TEMPLATE_RE = /`https?:\/\/[^/${}\s`]+[/`]/;
31860
32307
  function javaTraversalRejectGuardedLines(code) {
31861
32308
  const covered = new Set;
31862
32309
  const lines = code.split(`
@@ -31971,6 +32418,53 @@ function jsSsrfHostGuardedLines(code) {
31971
32418
  }
31972
32419
  return covered;
31973
32420
  }
32421
+ var CSHARP_SANITIZER_RES = [
32422
+ { re: /\b(?:HtmlEncode|JavaScriptStringEncode)\s*\(/, type: "xss" },
32423
+ { re: /\bHtmlEncoder\s*\.\s*Encode\s*\(/, type: "xss" },
32424
+ { re: /\bPath\s*\.\s*GetFileName\s*\(/, type: "path_traversal" }
32425
+ ];
32426
+ function csharpSanitizedVarsByType(code) {
32427
+ const lines = code.split(`
32428
+ `);
32429
+ const byType = new Map;
32430
+ const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
32431
+ for (const { type } of CSHARP_SANITIZER_RES) {
32432
+ if (!byType.has(type))
32433
+ byType.set(type, new Set);
32434
+ }
32435
+ for (const line of lines) {
32436
+ const m = assignRe.exec(line);
32437
+ if (!m)
32438
+ continue;
32439
+ const [, lhs, rhs] = m;
32440
+ for (const { re, type } of CSHARP_SANITIZER_RES) {
32441
+ if (re.test(rhs))
32442
+ byType.get(type).add(lhs);
32443
+ }
32444
+ }
32445
+ let changed = true;
32446
+ let guard = 0;
32447
+ while (changed && guard < lines.length + 2) {
32448
+ changed = false;
32449
+ guard++;
32450
+ for (const line of lines) {
32451
+ const m = assignRe.exec(line);
32452
+ if (!m)
32453
+ continue;
32454
+ const [, lhs, rhs] = m;
32455
+ for (const [type, set] of byType) {
32456
+ if (set.has(lhs))
32457
+ continue;
32458
+ const refsSanitized = [...set].some((v) => new RegExp(`(?<![\\w])${escapeRegex(v)}(?![\\w])`).test(rhs));
32459
+ if (refsSanitized) {
32460
+ set.add(lhs);
32461
+ changed = true;
32462
+ }
32463
+ }
32464
+ }
32465
+ }
32466
+ return byType;
32467
+ }
31974
32468
  var JAVA_INLINE_MATCHES_RE = /\.\s*matches\s*\(\s*"((?:[^"\\]|\\.)*)"\s*\)/g;
31975
32469
  function resolveJavaReceiverType(receiver, sinkLine, sourceLines) {
31976
32470
  if (!receiver || !/^[A-Za-z_]\w*$/.test(receiver))
@@ -32768,12 +33262,70 @@ class SinkFilterPass {
32768
33262
  return !FETCH_MEMBER_RECEIVER_RE.test(sinkLineText);
32769
33263
  });
32770
33264
  }
33265
+ if (["javascript", "typescript"].includes(language)) {
33266
+ const sourceLines = ctx.code.split(`
33267
+ `);
33268
+ filtered = filtered.filter((sink) => {
33269
+ if (sink.type !== "nosql_injection")
33270
+ return true;
33271
+ if (!NOSQL_ARRAY_METHODS.has(sink.method ?? ""))
33272
+ return true;
33273
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
33274
+ return !FUNCTION_FIRST_ARG_RE.test(sinkLineText);
33275
+ });
33276
+ }
33277
+ if (["javascript", "typescript"].includes(language)) {
33278
+ const sourceLines = ctx.code.split(`
33279
+ `);
33280
+ filtered = filtered.filter((sink) => {
33281
+ if (sink.type !== "sql_injection" && sink.type !== "nosql_injection")
33282
+ return true;
33283
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
33284
+ return !ORM_BUILDER_KEY_RE.test(sinkLineText);
33285
+ });
33286
+ }
33287
+ if (["javascript", "typescript", "tsx"].includes(language)) {
33288
+ const file = ctx.graph?.ir?.meta?.file ?? "";
33289
+ if (BROWSER_COMPONENT_EXT_RE.test(file) && CLIENT_SIGNAL_RE.test(ctx.code) && !SERVER_SIGNAL_RE.test(ctx.code)) {
33290
+ filtered = filtered.filter((sink) => sink.type !== "ssrf");
33291
+ }
33292
+ }
33293
+ if (["javascript", "typescript"].includes(language)) {
33294
+ const sourceLines = ctx.code.split(`
33295
+ `);
33296
+ filtered = filtered.filter((sink) => {
33297
+ if (sink.type !== "ssrf")
33298
+ return true;
33299
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
33300
+ return !FIXED_HOST_TEMPLATE_RE.test(sinkLineText);
33301
+ });
33302
+ }
32771
33303
  if (["javascript", "typescript"].includes(language)) {
32772
33304
  const guardedLines = jsSsrfHostGuardedLines(ctx.code);
32773
33305
  if (guardedLines.size > 0) {
32774
33306
  filtered = filtered.filter((sink) => !(sink.type === "ssrf" && guardedLines.has(sink.line)));
32775
33307
  }
32776
33308
  }
33309
+ if (language === "csharp") {
33310
+ const sourceLines = ctx.code.split(`
33311
+ `);
33312
+ const sanitizedByType = csharpSanitizedVarsByType(ctx.code);
33313
+ filtered = filtered.filter((sink) => {
33314
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
33315
+ for (const { re, type } of CSHARP_SANITIZER_RES) {
33316
+ if (type === sink.type && re.test(sinkLineText))
33317
+ return false;
33318
+ }
33319
+ const sanitizedVars = sanitizedByType.get(sink.type);
33320
+ if (sanitizedVars && sanitizedVars.size > 0) {
33321
+ for (const v of sanitizedVars) {
33322
+ if (new RegExp(`(?<![\\w])${escapeRegex(v)}(?![\\w])`).test(sinkLineText))
33323
+ return false;
33324
+ }
33325
+ }
33326
+ return true;
33327
+ });
33328
+ }
32777
33329
  if (["javascript", "typescript"].includes(language)) {
32778
33330
  const sourceLines = ctx.code.split(`
32779
33331
  `);
@@ -35417,7 +35969,7 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
35417
35969
  }
35418
35970
  }
35419
35971
  }
35420
- if (language === "java" && typeof code === "string" && sourcesWithVar.length > 0) {
35972
+ if ((language === "java" || language === "csharp") && typeof code === "string" && sourcesWithVar.length > 0) {
35421
35973
  const seedVars = new Set(sourcesWithVar.map((s) => s.variable));
35422
35974
  const derived = buildJavaTaintedVars(code, seedVars);
35423
35975
  if (derived.size > 0) {
@@ -47226,7 +47778,7 @@ var colors = {
47226
47778
  };
47227
47779
 
47228
47780
  // src/version.ts
47229
- var version = "3.216.0";
47781
+ var version = "4.2.0";
47230
47782
 
47231
47783
  // src/formatters.ts
47232
47784
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -48108,6 +48660,7 @@ var LANG_MAP = {
48108
48660
  ".tsx": "typescript",
48109
48661
  ".py": "python",
48110
48662
  ".go": "go",
48663
+ ".cs": "csharp",
48111
48664
  ".rs": "rust",
48112
48665
  ".sh": "bash",
48113
48666
  ".bash": "bash",
@@ -48866,7 +49419,7 @@ async function handleInit() {
48866
49419
  }
48867
49420
  const config = {
48868
49421
  version: "1.0",
48869
- include: ["src/**/*.java", "src/**/*.ts", "src/**/*.js", "src/**/*.py", "src/**/*.go"],
49422
+ include: ["src/**/*.java", "src/**/*.ts", "src/**/*.js", "src/**/*.py", "src/**/*.go", "src/**/*.cs"],
48870
49423
  exclude: ["**/test/**", "**/tests/**", "**/node_modules/**", "**/dist/**"],
48871
49424
  passes: {
48872
49425
  "naming-convention": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "3.216.0",
3
+ "version": "4.2.0",
4
4
  "description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -66,7 +66,7 @@
66
66
  },
67
67
  "dependencies": {
68
68
  "@cognium/project-profile-detect": "^1.1.0",
69
- "circle-ir": "^3.216.0"
69
+ "circle-ir": "^4.2.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",