cognium-dev 3.215.0 → 4.0.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 (2) hide show
  1. package/dist/cli.js +612 -7
  2. package/package.json +2 -2
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") {
@@ -23377,6 +23705,65 @@ class GoPlugin extends BaseLanguagePlugin {
23377
23705
  }
23378
23706
  }
23379
23707
 
23708
+ // ../circle-ir/dist/languages/plugins/csharp.js
23709
+ class CSharpPlugin extends BaseLanguagePlugin {
23710
+ id = "csharp";
23711
+ name = "C#";
23712
+ extensions = [".cs"];
23713
+ wasmPath = "tree-sitter-csharp.wasm";
23714
+ nodeTypes = {
23715
+ classDeclaration: ["class_declaration", "record_declaration", "struct_declaration"],
23716
+ interfaceDeclaration: ["interface_declaration"],
23717
+ enumDeclaration: ["enum_declaration"],
23718
+ functionDeclaration: [],
23719
+ methodDeclaration: ["method_declaration", "constructor_declaration", "local_function_statement"],
23720
+ methodCall: ["invocation_expression"],
23721
+ functionCall: [],
23722
+ assignment: ["assignment_expression"],
23723
+ variableDeclaration: ["local_declaration_statement", "field_declaration", "variable_declaration"],
23724
+ parameter: ["parameter"],
23725
+ argument: ["argument_list"],
23726
+ annotation: ["attribute", "attribute_list"],
23727
+ decorator: [],
23728
+ importStatement: ["using_directive"],
23729
+ ifStatement: ["if_statement"],
23730
+ forStatement: ["for_statement", "for_each_statement"],
23731
+ whileStatement: ["while_statement"],
23732
+ tryStatement: ["try_statement"],
23733
+ returnStatement: ["return_statement"]
23734
+ };
23735
+ detectFramework(context) {
23736
+ for (const imp of context.imports) {
23737
+ const path = imp.from_package || imp.imported_name;
23738
+ if (path.startsWith("Microsoft.AspNetCore") || path.startsWith("Microsoft.Extensions")) {
23739
+ return { name: "aspnetcore", confidence: 0.9, indicators: [`using: ${path}`] };
23740
+ }
23741
+ if (path.startsWith("Microsoft.EntityFrameworkCore")) {
23742
+ return { name: "efcore", confidence: 0.9, indicators: [`using: ${path}`] };
23743
+ }
23744
+ }
23745
+ return;
23746
+ }
23747
+ extractTypes(_context) {
23748
+ return [];
23749
+ }
23750
+ extractCalls(_context) {
23751
+ return [];
23752
+ }
23753
+ extractImports(_context) {
23754
+ return [];
23755
+ }
23756
+ extractPackage(_context) {
23757
+ return;
23758
+ }
23759
+ getBuiltinSources() {
23760
+ return [];
23761
+ }
23762
+ getBuiltinSinks() {
23763
+ return [];
23764
+ }
23765
+ }
23766
+
23380
23767
  // ../circle-ir/dist/languages/plugins/index.js
23381
23768
  function registerBuiltinPlugins() {
23382
23769
  registerLanguage(new JavaPlugin);
@@ -23387,6 +23774,7 @@ function registerBuiltinPlugins() {
23387
23774
  registerLanguage(new HtmlPlugin);
23388
23775
  registerLanguage(new VuePlugin);
23389
23776
  registerLanguage(new GoPlugin);
23777
+ registerLanguage(new CSharpPlugin);
23390
23778
  }
23391
23779
  // ../circle-ir/dist/analysis/passes/cross-file-pass.js
23392
23780
  class CrossFilePass {
@@ -24570,6 +24958,7 @@ class LanguageSourcesPass {
24570
24958
  additionalSources.push(...findStaticFieldSources(types, code, language));
24571
24959
  additionalSources.push(...findSetterChainSources(types, code, language));
24572
24960
  additionalSources.push(...findJavaScriptAssignmentSources(code, language));
24961
+ additionalSources.push(...findCSharpRequestSources(code, language));
24573
24962
  const jsDOMSinks = findJavaScriptDOMSinks(code, language);
24574
24963
  for (const s of jsDOMSinks) {
24575
24964
  const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
@@ -25172,6 +25561,35 @@ function findSetterChainSources(types, sourceCode, language) {
25172
25561
  }
25173
25562
  return sources;
25174
25563
  }
25564
+ function findCSharpRequestSources(sourceCode, language) {
25565
+ if (language !== "csharp")
25566
+ return [];
25567
+ const sources = [];
25568
+ const lines = sourceCode.split(`
25569
+ `);
25570
+ const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
25571
+ const requestReadRe = /\bRequest\s*\.\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\b/;
25572
+ for (let i2 = 0;i2 < lines.length; i2++) {
25573
+ const m = assignRe.exec(lines[i2]);
25574
+ if (!m)
25575
+ continue;
25576
+ const [, varName, rhs] = m;
25577
+ if (!requestReadRe.test(rhs))
25578
+ continue;
25579
+ const lineNumber = i2 + 1;
25580
+ if (sources.some((s) => s.line === lineNumber && s.variable === varName))
25581
+ continue;
25582
+ sources.push({
25583
+ type: "http_param",
25584
+ location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
25585
+ severity: "high",
25586
+ line: lineNumber,
25587
+ confidence: 1,
25588
+ variable: varName
25589
+ });
25590
+ }
25591
+ return sources;
25592
+ }
25175
25593
  function findJavaScriptAssignmentSources(sourceCode, language) {
25176
25594
  if (!["javascript", "typescript"].includes(language))
25177
25595
  return [];
@@ -31857,6 +32275,13 @@ var REPLACE_ALL_TO_PLACEHOLDER_RE = /\.replaceAll\s*\([\s\S]*?,\s*"[\s,?]*\?[\s,
31857
32275
  var HTML_CONTENT_TYPE_RE = /text\/html|TEXT_HTML/;
31858
32276
  var FETCH_GLOBAL_RECEIVER_RE = /(?:^|[^.\w])(?:window|globalThis|self|global)\s*\.\s*fetch\s*\(/;
31859
32277
  var FETCH_MEMBER_RECEIVER_RE = /[\w$)\]]\s*\.\s*fetch\s*\(/;
32278
+ var NOSQL_ARRAY_METHODS = new Set(["find", "filter", "some", "every", "findIndex", "findLast"]);
32279
+ var FUNCTION_FIRST_ARG_RE = /\.\s*(?:find|filter|some|every|findIndex|findLast)\s*\(\s*(?:async\s+)?(?:function\b|(?:\([^()]*\)|[A-Za-z_$][\w$]*)\s*=>)/;
32280
+ var ORM_BUILDER_KEY_RE = /[{,]\s*(?:where|select|populate|include|orderBy|relations|attributes)\s*:/;
32281
+ var BROWSER_COMPONENT_EXT_RE = /\.(?:jsx|tsx|vue|svelte)$/;
32282
+ 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*=/;
32283
+ 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*\(/;
32284
+ var FIXED_HOST_TEMPLATE_RE = /`https?:\/\/[^/${}\s`]+[/`]/;
31860
32285
  function javaTraversalRejectGuardedLines(code) {
31861
32286
  const covered = new Set;
31862
32287
  const lines = code.split(`
@@ -31903,6 +32328,121 @@ function javaTraversalRejectGuardedLines(code) {
31903
32328
  }
31904
32329
  return covered;
31905
32330
  }
32331
+ function isAnchoredHostAllowlistRegex(src) {
32332
+ if (!src.startsWith("^"))
32333
+ return false;
32334
+ if (!/https?/i.test(src))
32335
+ return false;
32336
+ return /[A-Za-z][A-Za-z0-9-]+\\?\./.test(src);
32337
+ }
32338
+ function isSchemeHostPrefix(prefix) {
32339
+ if (!/^https?:\/\//i.test(prefix))
32340
+ return false;
32341
+ const afterScheme = prefix.replace(/^https?:\/\//i, "");
32342
+ return /[A-Za-z][A-Za-z0-9-]*\./.test(afterScheme);
32343
+ }
32344
+ function jsSsrfHostGuardedLines(code) {
32345
+ const covered = new Set;
32346
+ const lines = code.split(`
32347
+ `);
32348
+ const rejectRegexTest = /\bif\s*\(\s*!\s*\/((?:[^/\\]|\\.)*)\/[a-z]*\s*\.\s*test\s*\(\s*([A-Za-z_]\w*)\s*\)/;
32349
+ const rejectStartsWith = /\bif\s*\(\s*!\s*([A-Za-z_]\w*)\s*\.\s*startsWith\s*\(\s*["'`]([^"'`]+)["'`]/;
32350
+ const terminatorRe = /\b(throw|return)\b/;
32351
+ const assignRe = /^\s*(?:const|let|var\s+)?\s*([A-Za-z_]\w*)\s*=(?!=)\s*(.*)$/;
32352
+ for (let i2 = 0;i2 < lines.length; i2++) {
32353
+ let guardedVar = null;
32354
+ const rm = rejectRegexTest.exec(lines[i2]);
32355
+ if (rm && isAnchoredHostAllowlistRegex(rm[1]))
32356
+ guardedVar = rm[2];
32357
+ if (!guardedVar) {
32358
+ const sm = rejectStartsWith.exec(lines[i2]);
32359
+ if (sm && isSchemeHostPrefix(sm[2]))
32360
+ guardedVar = sm[1];
32361
+ }
32362
+ if (!guardedVar)
32363
+ continue;
32364
+ let hasTerminator = terminatorRe.test(lines[i2]);
32365
+ if (!hasTerminator) {
32366
+ for (let j = i2 + 1;j < Math.min(lines.length, i2 + 6); j++) {
32367
+ if (terminatorRe.test(lines[j])) {
32368
+ hasTerminator = true;
32369
+ break;
32370
+ }
32371
+ if (lines[j].includes("}"))
32372
+ break;
32373
+ }
32374
+ }
32375
+ if (!hasTerminator)
32376
+ continue;
32377
+ const guarded = new Set([guardedVar]);
32378
+ const mentionsGuarded = (text) => {
32379
+ for (const name2 of guarded)
32380
+ if (new RegExp(`\\b${name2}\\b`).test(text))
32381
+ return true;
32382
+ return false;
32383
+ };
32384
+ for (let l = i2 + 1;l < lines.length; l++) {
32385
+ const lineText = lines[l];
32386
+ const assign = assignRe.exec(lineText);
32387
+ if (assign) {
32388
+ if (mentionsGuarded(assign[2]))
32389
+ guarded.add(assign[1]);
32390
+ else if (assign[1] !== guardedVar)
32391
+ guarded.delete(assign[1]);
32392
+ }
32393
+ if (mentionsGuarded(lineText))
32394
+ covered.add(l + 1);
32395
+ }
32396
+ }
32397
+ return covered;
32398
+ }
32399
+ var CSHARP_SANITIZER_RES = [
32400
+ { re: /\b(?:HtmlEncode|JavaScriptStringEncode)\s*\(/, type: "xss" },
32401
+ { re: /\bHtmlEncoder\s*\.\s*Encode\s*\(/, type: "xss" },
32402
+ { re: /\bPath\s*\.\s*GetFileName\s*\(/, type: "path_traversal" }
32403
+ ];
32404
+ function csharpSanitizedVarsByType(code) {
32405
+ const lines = code.split(`
32406
+ `);
32407
+ const byType = new Map;
32408
+ const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
32409
+ for (const { type } of CSHARP_SANITIZER_RES) {
32410
+ if (!byType.has(type))
32411
+ byType.set(type, new Set);
32412
+ }
32413
+ for (const line of lines) {
32414
+ const m = assignRe.exec(line);
32415
+ if (!m)
32416
+ continue;
32417
+ const [, lhs, rhs] = m;
32418
+ for (const { re, type } of CSHARP_SANITIZER_RES) {
32419
+ if (re.test(rhs))
32420
+ byType.get(type).add(lhs);
32421
+ }
32422
+ }
32423
+ let changed = true;
32424
+ let guard = 0;
32425
+ while (changed && guard < lines.length + 2) {
32426
+ changed = false;
32427
+ guard++;
32428
+ for (const line of lines) {
32429
+ const m = assignRe.exec(line);
32430
+ if (!m)
32431
+ continue;
32432
+ const [, lhs, rhs] = m;
32433
+ for (const [type, set] of byType) {
32434
+ if (set.has(lhs))
32435
+ continue;
32436
+ const refsSanitized = [...set].some((v) => new RegExp(`(?<![\\w])${escapeRegex(v)}(?![\\w])`).test(rhs));
32437
+ if (refsSanitized) {
32438
+ set.add(lhs);
32439
+ changed = true;
32440
+ }
32441
+ }
32442
+ }
32443
+ }
32444
+ return byType;
32445
+ }
31906
32446
  var JAVA_INLINE_MATCHES_RE = /\.\s*matches\s*\(\s*"((?:[^"\\]|\\.)*)"\s*\)/g;
31907
32447
  function resolveJavaReceiverType(receiver, sinkLine, sourceLines) {
31908
32448
  if (!receiver || !/^[A-Za-z_]\w*$/.test(receiver))
@@ -32702,6 +33242,70 @@ class SinkFilterPass {
32702
33242
  }
32703
33243
  if (["javascript", "typescript"].includes(language)) {
32704
33244
  const sourceLines = ctx.code.split(`
33245
+ `);
33246
+ filtered = filtered.filter((sink) => {
33247
+ if (sink.type !== "nosql_injection")
33248
+ return true;
33249
+ if (!NOSQL_ARRAY_METHODS.has(sink.method ?? ""))
33250
+ return true;
33251
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
33252
+ return !FUNCTION_FIRST_ARG_RE.test(sinkLineText);
33253
+ });
33254
+ }
33255
+ if (["javascript", "typescript"].includes(language)) {
33256
+ const sourceLines = ctx.code.split(`
33257
+ `);
33258
+ filtered = filtered.filter((sink) => {
33259
+ if (sink.type !== "sql_injection" && sink.type !== "nosql_injection")
33260
+ return true;
33261
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
33262
+ return !ORM_BUILDER_KEY_RE.test(sinkLineText);
33263
+ });
33264
+ }
33265
+ if (["javascript", "typescript", "tsx"].includes(language)) {
33266
+ const file = ctx.graph?.ir?.meta?.file ?? "";
33267
+ if (BROWSER_COMPONENT_EXT_RE.test(file) && CLIENT_SIGNAL_RE.test(ctx.code) && !SERVER_SIGNAL_RE.test(ctx.code)) {
33268
+ filtered = filtered.filter((sink) => sink.type !== "ssrf");
33269
+ }
33270
+ }
33271
+ if (["javascript", "typescript"].includes(language)) {
33272
+ const sourceLines = ctx.code.split(`
33273
+ `);
33274
+ filtered = filtered.filter((sink) => {
33275
+ if (sink.type !== "ssrf")
33276
+ return true;
33277
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
33278
+ return !FIXED_HOST_TEMPLATE_RE.test(sinkLineText);
33279
+ });
33280
+ }
33281
+ if (["javascript", "typescript"].includes(language)) {
33282
+ const guardedLines = jsSsrfHostGuardedLines(ctx.code);
33283
+ if (guardedLines.size > 0) {
33284
+ filtered = filtered.filter((sink) => !(sink.type === "ssrf" && guardedLines.has(sink.line)));
33285
+ }
33286
+ }
33287
+ if (language === "csharp") {
33288
+ const sourceLines = ctx.code.split(`
33289
+ `);
33290
+ const sanitizedByType = csharpSanitizedVarsByType(ctx.code);
33291
+ filtered = filtered.filter((sink) => {
33292
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
33293
+ for (const { re, type } of CSHARP_SANITIZER_RES) {
33294
+ if (type === sink.type && re.test(sinkLineText))
33295
+ return false;
33296
+ }
33297
+ const sanitizedVars = sanitizedByType.get(sink.type);
33298
+ if (sanitizedVars && sanitizedVars.size > 0) {
33299
+ for (const v of sanitizedVars) {
33300
+ if (new RegExp(`(?<![\\w])${escapeRegex(v)}(?![\\w])`).test(sinkLineText))
33301
+ return false;
33302
+ }
33303
+ }
33304
+ return true;
33305
+ });
33306
+ }
33307
+ if (["javascript", "typescript"].includes(language)) {
33308
+ const sourceLines = ctx.code.split(`
32705
33309
  `);
32706
33310
  filtered = filtered.filter((sink) => {
32707
33311
  if (sink.type !== "log_injection")
@@ -35343,7 +35947,7 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
35343
35947
  }
35344
35948
  }
35345
35949
  }
35346
- if (language === "java" && typeof code === "string" && sourcesWithVar.length > 0) {
35950
+ if ((language === "java" || language === "csharp") && typeof code === "string" && sourcesWithVar.length > 0) {
35347
35951
  const seedVars = new Set(sourcesWithVar.map((s) => s.variable));
35348
35952
  const derived = buildJavaTaintedVars(code, seedVars);
35349
35953
  if (derived.size > 0) {
@@ -47152,7 +47756,7 @@ var colors = {
47152
47756
  };
47153
47757
 
47154
47758
  // src/version.ts
47155
- var version = "3.215.0";
47759
+ var version = "4.0.0";
47156
47760
 
47157
47761
  // src/formatters.ts
47158
47762
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -48034,6 +48638,7 @@ var LANG_MAP = {
48034
48638
  ".tsx": "typescript",
48035
48639
  ".py": "python",
48036
48640
  ".go": "go",
48641
+ ".cs": "csharp",
48037
48642
  ".rs": "rust",
48038
48643
  ".sh": "bash",
48039
48644
  ".bash": "bash",
@@ -48792,7 +49397,7 @@ async function handleInit() {
48792
49397
  }
48793
49398
  const config = {
48794
49399
  version: "1.0",
48795
- include: ["src/**/*.java", "src/**/*.ts", "src/**/*.js", "src/**/*.py", "src/**/*.go"],
49400
+ include: ["src/**/*.java", "src/**/*.ts", "src/**/*.js", "src/**/*.py", "src/**/*.go", "src/**/*.cs"],
48796
49401
  exclude: ["**/test/**", "**/tests/**", "**/node_modules/**", "**/dist/**"],
48797
49402
  passes: {
48798
49403
  "naming-convention": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "3.215.0",
3
+ "version": "4.0.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.215.0"
69
+ "circle-ir": "^4.0.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",