cognium-dev 4.9.28 → 4.10.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 +238 -33
  2. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -3667,7 +3667,7 @@ function detectLanguage(tree) {
3667
3667
  return "python";
3668
3668
  return jsScore > javaScore ? "javascript" : "java";
3669
3669
  }
3670
- function extractTypes(tree, cache, language) {
3670
+ function extractTypes(tree, cache, language, options) {
3671
3671
  const effectiveLanguage = language ?? detectLanguage(tree);
3672
3672
  const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
3673
3673
  const isPython = effectiveLanguage === "python";
@@ -3687,7 +3687,27 @@ function extractTypes(tree, cache, language) {
3687
3687
  if (effectiveLanguage === "csharp") {
3688
3688
  return extractCSharpTypes(tree, cache);
3689
3689
  }
3690
- return extractJavaTypes(tree, cache);
3690
+ return extractJavaTypes(tree, cache, options);
3691
+ }
3692
+ function csharpMemberNodes(body2) {
3693
+ if (!body2)
3694
+ return [];
3695
+ const out2 = [];
3696
+ const visit = (parent, depth) => {
3697
+ if (depth > 32)
3698
+ return;
3699
+ for (let i2 = 0;i2 < parent.childCount; i2++) {
3700
+ const c = parent.child(i2);
3701
+ if (!c)
3702
+ continue;
3703
+ if (c.type.startsWith("preproc_"))
3704
+ visit(c, depth + 1);
3705
+ else
3706
+ out2.push(c);
3707
+ }
3708
+ };
3709
+ visit(body2, 0);
3710
+ return out2;
3691
3711
  }
3692
3712
  function extractCSharpTypes(tree, cache) {
3693
3713
  const types = [];
@@ -3705,9 +3725,8 @@ function extractCSharpTypes(tree, cache) {
3705
3725
  const methods = [];
3706
3726
  const fields = extractCSharpFields(body2);
3707
3727
  if (body2) {
3708
- for (let i2 = 0;i2 < body2.childCount; i2++) {
3709
- const m = body2.child(i2);
3710
- if (!m || m.type !== "method_declaration")
3728
+ for (const m of csharpMemberNodes(body2)) {
3729
+ if (m.type !== "method_declaration")
3711
3730
  continue;
3712
3731
  const mName = m.childForFieldName("name");
3713
3732
  const paramList = m.childForFieldName("parameters");
@@ -3761,10 +3780,7 @@ function extractCSharpFields(body2) {
3761
3780
  const fields = [];
3762
3781
  if (!body2)
3763
3782
  return fields;
3764
- for (let i2 = 0;i2 < body2.childCount; i2++) {
3765
- const c = body2.child(i2);
3766
- if (!c)
3767
- continue;
3783
+ for (const c of csharpMemberNodes(body2)) {
3768
3784
  if (c.type === "field_declaration") {
3769
3785
  let varDecl = null;
3770
3786
  for (let k = 0;k < c.childCount; k++) {
@@ -3827,7 +3843,7 @@ function extractCSharpModifiers(node) {
3827
3843
  }
3828
3844
  return mods;
3829
3845
  }
3830
- function extractJavaTypes(tree, cache) {
3846
+ function extractJavaTypes(tree, cache, options) {
3831
3847
  const types = [];
3832
3848
  const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
3833
3849
  for (const cls of classes) {
@@ -3841,8 +3857,81 @@ function extractJavaTypes(tree, cache) {
3841
3857
  for (const enumDecl of enums) {
3842
3858
  types.push(extractEnumInfo(enumDecl));
3843
3859
  }
3860
+ if (!options?.navigationTypes)
3861
+ return types;
3862
+ for (const rec of getNodesFromCache(tree.rootNode, "record_declaration", cache)) {
3863
+ const info2 = extractClassInfo(rec);
3864
+ info2.is_record = true;
3865
+ for (const comp of recordComponents(rec)) {
3866
+ if (!info2.fields.some((f) => f.name === comp.name)) {
3867
+ info2.fields.push({
3868
+ name: comp.name,
3869
+ type: comp.type,
3870
+ modifiers: ["private", "final"],
3871
+ annotations: []
3872
+ });
3873
+ }
3874
+ if (!info2.methods.some((m) => m.name === comp.name)) {
3875
+ info2.methods.push({
3876
+ name: comp.name,
3877
+ return_type: comp.type,
3878
+ parameters: [],
3879
+ annotations: [],
3880
+ modifiers: ["public"],
3881
+ start_line: rec.startPosition.row + 1,
3882
+ end_line: rec.startPosition.row + 1
3883
+ });
3884
+ }
3885
+ }
3886
+ types.push(info2);
3887
+ }
3888
+ const declNodes = [
3889
+ ...classes,
3890
+ ...interfaces,
3891
+ ...enums,
3892
+ ...getNodesFromCache(tree.rootNode, "record_declaration", cache)
3893
+ ];
3894
+ for (const t of types) {
3895
+ const node = declNodes.find((n) => n.startPosition.row + 1 === t.start_line && getIdentifier(n, "name") === t.name);
3896
+ const path = node ? enclosingTypePath(node) : [];
3897
+ if (path.length > 0)
3898
+ t.enclosing_type = path.join(".");
3899
+ }
3844
3900
  return types;
3845
3901
  }
3902
+ function recordComponents(node) {
3903
+ const out2 = [];
3904
+ const params = node.childForFieldName("parameters");
3905
+ if (!params)
3906
+ return out2;
3907
+ for (let i2 = 0;i2 < params.childCount; i2++) {
3908
+ const c = params.child(i2);
3909
+ if (!c || c.type !== "formal_parameter")
3910
+ continue;
3911
+ const name2 = c.childForFieldName("name");
3912
+ const type = c.childForFieldName("type");
3913
+ if (name2)
3914
+ out2.push({ name: getNodeText(name2), type: type ? getNodeText(type) : null });
3915
+ }
3916
+ return out2;
3917
+ }
3918
+ function enclosingTypePath(node) {
3919
+ const DECLS = new Set([
3920
+ "class_declaration",
3921
+ "interface_declaration",
3922
+ "enum_declaration",
3923
+ "record_declaration"
3924
+ ]);
3925
+ const path = [];
3926
+ for (let p = node.parent;p; p = p.parent) {
3927
+ if (!DECLS.has(p.type))
3928
+ continue;
3929
+ const n = getIdentifier(p, "name");
3930
+ if (n)
3931
+ path.unshift(n);
3932
+ }
3933
+ return path;
3934
+ }
3846
3935
  function extractJavaScriptTypes(tree, cache) {
3847
3936
  const types = [];
3848
3937
  const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
@@ -5351,6 +5440,15 @@ function extractCalls(tree, cache, language) {
5351
5440
  function buildCSharpReceiverTypeMap(tree, cache) {
5352
5441
  const map = new Map;
5353
5442
  const simple = (t) => t.replace(/<[^>]*>/g, "").split(".").pop()?.trim() ?? t;
5443
+ for (const prm of getNodesFromCache(tree.rootNode, "parameter", cache)) {
5444
+ const nameNode = prm.childForFieldName("name");
5445
+ const typeNode = prm.childForFieldName("type");
5446
+ if (nameNode?.type !== "identifier" || !typeNode)
5447
+ continue;
5448
+ const t = getNodeText(typeNode);
5449
+ if (t && t !== "var")
5450
+ map.set(getNodeText(nameNode), simple(t));
5451
+ }
5354
5452
  for (const vd of getNodesFromCache(tree.rootNode, "variable_declaration", cache)) {
5355
5453
  const typeNode = vd.childForFieldName("type");
5356
5454
  const declaredType = typeNode ? getNodeText(typeNode) : null;
@@ -5387,6 +5485,7 @@ function findFirstDescendant(node, type) {
5387
5485
  }
5388
5486
  return null;
5389
5487
  }
5488
+ var CSHARP_RESPONSE_TYPES = new Set(["HttpResponse", "HttpResponseBase"]);
5390
5489
  var CSHARP_COMMAND_EXECUTE_METHODS = new Set([
5391
5490
  "ExecuteScalar",
5392
5491
  "ExecuteReader",
@@ -5547,7 +5646,8 @@ function extractCSharpCalls(tree, cache) {
5547
5646
  continue;
5548
5647
  } else if (propName === "StatusDescription") {
5549
5648
  const recv = exprNode ? getNodeText(exprNode) : "";
5550
- if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv))
5649
+ const recvType = recv ? typeMap.get(recv) : undefined;
5650
+ if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv) && !(recvType && CSHARP_RESPONSE_TYPES.has(recvType)))
5551
5651
  continue;
5552
5652
  } else if (propName === "BaseAddress") {
5553
5653
  const recv = exprNode ? getNodeText(exprNode) : null;
@@ -11207,6 +11307,14 @@ var DEFAULT_SOURCES = [
11207
11307
  { method: "nextLine", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
11208
11308
  { method: "next", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
11209
11309
  { method: "nextInt", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
11310
+ { method: "ReadLine", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11311
+ { method: "ReadLineAsync", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11312
+ { method: "ReadToEnd", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11313
+ { method: "ReadToEndAsync", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11314
+ { method: "GetStream", class: "TcpClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11315
+ { method: "AcceptTcpClient", class: "TcpListener", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11316
+ { method: "OpenRead", class: "WebClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11317
+ { method: "DownloadString", class: "WebClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11210
11318
  { method: "getString", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
11211
11319
  { method: "getObject", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
11212
11320
  { method: "getInt", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
@@ -12556,6 +12664,22 @@ var DEFAULT_SINKS = [
12556
12664
  { method: "deserialize", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
12557
12665
  { method: "from_str", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12558
12666
  { method: "from_str", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12667
+ { method: "from_slice", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12668
+ { method: "from_read", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12669
+ { method: "from_read_ref", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12670
+ { method: "from_slice", class: "serde_yaml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12671
+ { method: "from_slice", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12672
+ { method: "from_reader", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12673
+ { method: "from_bytes", class: "postcard", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12674
+ { method: "from_slice", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12675
+ { method: "from_reader", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12676
+ { method: "from_reader", class: "ciborium", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12677
+ { method: "from_str", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12678
+ { method: "from_reader", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12679
+ { method: "from_str", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12680
+ { method: "from_reader", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12681
+ { method: "from_slice", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
12682
+ { method: "deserialize_from", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
12559
12683
  { method: "body", class: "HttpResponseBuilder", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
12560
12684
  { method: "body", class: "HttpResponse", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
12561
12685
  { method: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
@@ -12730,8 +12854,8 @@ var DEFAULT_SINKS = [
12730
12854
  { method: "OpenRead", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12731
12855
  { method: "OpenWrite", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12732
12856
  { method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12733
- { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12734
- { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12857
+ { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], safe_if_stream_arg_at: 0, languages: ["csharp"] },
12858
+ { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], safe_if_stream_arg_at: 0, languages: ["csharp"] },
12735
12859
  { method: "Copy", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
12736
12860
  { method: "Move", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
12737
12861
  { method: "Delete", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -12761,6 +12885,10 @@ var DEFAULT_SINKS = [
12761
12885
  { method: "DownloadFile", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [1], languages: ["csharp"] },
12762
12886
  { method: "Create", class: "WebRequest", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12763
12887
  { method: "RestClient", class: "constructor", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12888
+ { method: "CompileAssemblyFromSource", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [1, 2], languages: ["csharp"] },
12889
+ { method: "CompileAssemblyFromSourceBatch", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [1, 2], languages: ["csharp"] },
12890
+ { method: "ParseText", class: "CSharpSyntaxTree", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12891
+ { method: "ParseText", class: "SyntaxFactory", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12764
12892
  { method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12765
12893
  { method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12766
12894
  { method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
@@ -14348,6 +14476,15 @@ function escapeRe(s) {
14348
14476
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14349
14477
  }
14350
14478
  var TYPE_TOKEN_RE = /^new\s+(?:TypeReference|TypeToken)\s*<[\s\S]*>\s*\(\s*\)\s*\{\s*\}$/;
14479
+ function argIsStreamExpression(call, position) {
14480
+ const arg = call.arguments.find((a) => a.position === position);
14481
+ if (!arg)
14482
+ return false;
14483
+ const expr = (arg.expression ?? "").trim();
14484
+ if (!expr)
14485
+ return false;
14486
+ return /^new\s+[A-Za-z_][\w.]*Stream\s*\(/.test(expr) || /\.\s*Get(?:Response)?Stream\s*\(/.test(expr) || /\.\s*OpenReadStream\s*\(/.test(expr) || /\.\s*OpenRead\s*\(/.test(expr) || /\.\s*(?:Body|BaseStream)\s*$/.test(expr) || /^(?:[A-Za-z_]\w*\.)*[A-Za-z_]*[sS]tream\d*$/.test(expr);
14487
+ }
14351
14488
  function argIsStringLiteral(call, position) {
14352
14489
  const arg = call.arguments.find((a) => a.position === position);
14353
14490
  if (!arg)
@@ -14618,6 +14755,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
14618
14755
  if (pattern.safe_if_string_literal_at !== undefined && argIsStringLiteral(call, pattern.safe_if_string_literal_at)) {
14619
14756
  continue;
14620
14757
  }
14758
+ if (pattern.safe_if_stream_arg_at !== undefined && argIsStreamExpression(call, pattern.safe_if_stream_arg_at)) {
14759
+ continue;
14760
+ }
14621
14761
  if (pattern.type === "command_injection") {
14622
14762
  if (call.is_constructor) {
14623
14763
  if (!CWE_78_RECEIVER_ALLOWLIST.has(call.method_name)) {
@@ -26866,6 +27006,10 @@ function findCSharpRequestSources(sourceCode, language) {
26866
27006
  const formFileDeclRe = /\bIFormFile\s+([A-Za-z_]\w*)/g;
26867
27007
  const httpRequestReceivers = new Set(["Request"]);
26868
27008
  const httpRequestDeclRe = /\b(?:HttpRequest|HttpRequestBase)\??\s+([A-Za-z_]\w*)/g;
27009
+ const streamReaderVars = new Set;
27010
+ const streamReaderDeclRe = /\b(?:StreamReader|TextReader|BinaryReader)\??\s+([A-Za-z_]\w*)/g;
27011
+ const dbReaderVars = new Set;
27012
+ const dbReaderDeclRe = /\b(?:SqlDataReader|DbDataReader|IDataReader|OdbcDataReader|OleDbDataReader)\??\s+([A-Za-z_]\w*)/g;
26869
27013
  for (const line of lines) {
26870
27014
  const formRe = new RegExp(formFileDeclRe.source, "g");
26871
27015
  let d;
@@ -26874,15 +27018,25 @@ function findCSharpRequestSources(sourceCode, language) {
26874
27018
  const reqRe = new RegExp(httpRequestDeclRe.source, "g");
26875
27019
  while ((d = reqRe.exec(line)) !== null)
26876
27020
  httpRequestReceivers.add(d[1]);
26877
- }
27021
+ const srRe = new RegExp(streamReaderDeclRe.source, "g");
27022
+ while ((d = srRe.exec(line)) !== null)
27023
+ streamReaderVars.add(d[1]);
27024
+ const dbRe = new RegExp(dbReaderDeclRe.source, "g");
27025
+ while ((d = dbRe.exec(line)) !== null)
27026
+ dbReaderVars.add(d[1]);
27027
+ }
27028
+ const streamReadRe = streamReaderVars.size > 0 ? new RegExp(`\\b(?:${[...streamReaderVars].join("|")})\\s*\\.\\s*(?:ReadLine|ReadToEnd|ReadLineAsync|ReadToEndAsync|ReadBlock|Read)\\s*\\(`) : null;
27029
+ const dbReadRe = dbReaderVars.size > 0 ? new RegExp(`\\b(?:${[...dbReaderVars].join("|")})\\s*\\.\\s*(?:GetString|GetValue|GetInt32|GetInt64|GetDecimal|GetDateTime|GetFieldValue|\\[)`) : null;
26878
27030
  const formFileReadRe = formFileParams.size > 0 ? new RegExp(`\\b(?:${[...formFileParams].join("|")})\\s*\\.\\s*(?:FileName|ContentType)\\b`) : null;
26879
27031
  const requestReadRe = new RegExp(`\\b(?:${[...httpRequestReceivers].join("|")})\\s*\\.\\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\\b`);
26880
- const classify = (text) => requestReadRe.test(text) ? "http_param" : formFileReadRe?.test(text) ? "http_param" : consoleReadRe.test(text) ? "io_input" : envReadRe.test(text) ? "env_input" : null;
27032
+ const classify = (text) => requestReadRe.test(text) ? "http_param" : formFileReadRe?.test(text) ? "http_param" : consoleReadRe.test(text) ? "io_input" : envReadRe.test(text) ? "env_input" : streamReadRe?.test(text) ? "io_input" : dbReadRe?.test(text) ? "db_input" : null;
26881
27033
  const inlineReads = [
26882
27034
  [requestReadRe, "http_param"],
26883
27035
  [formFileReadRe, "http_param"],
26884
27036
  [consoleReadRe, "io_input"],
26885
- [envReadRe, "env_input"]
27037
+ [envReadRe, "env_input"],
27038
+ [streamReadRe, "io_input"],
27039
+ [dbReadRe, "db_input"]
26886
27040
  ];
26887
27041
  const inlineRead = (text) => {
26888
27042
  const argsStart = text.indexOf("(");
@@ -26904,7 +27058,8 @@ function findCSharpRequestSources(sourceCode, language) {
26904
27058
  const embeddedAssignRe = /(?:^|[{;])\s*(?:await\s+)?(?:using\s*\(?\s*)?(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=(?!=)\s*([^;{}]+)/;
26905
27059
  for (let i2 = 0;i2 < lines.length; i2++) {
26906
27060
  const m = assignRe.exec(lines[i2]);
26907
- if (!m) {
27061
+ const rhsCrossesBlock = m !== null && m[2].includes("{");
27062
+ if (!m || rhsCrossesBlock) {
26908
27063
  const stmt = lines[i2];
26909
27064
  if (stmt.includes("{") && !/^\s*(?:\/\/|\/\*|\*)/.test(stmt)) {
26910
27065
  const emb = new RegExp(embeddedAssignRe.source, "g");
@@ -26922,7 +27077,7 @@ function findCSharpRequestSources(sourceCode, language) {
26922
27077
  sources.push({
26923
27078
  type: type3,
26924
27079
  location: `${varName2} = ${rhs2.trim().substring(0, 50)}${rhs2.length > 50 ? "..." : ""}`,
26925
- severity: "high",
27080
+ severity: type3 === "db_input" ? "medium" : "high",
26926
27081
  line: lineNumber3,
26927
27082
  confidence: 1,
26928
27083
  variable: varName2
@@ -26945,7 +27100,7 @@ function findCSharpRequestSources(sourceCode, language) {
26945
27100
  sources.push({
26946
27101
  type: type2,
26947
27102
  location: `${read} in ${code.substring(0, 50)}${code.length > 50 ? "..." : ""}`,
26948
- severity: "high",
27103
+ severity: type2 === "db_input" ? "medium" : "high",
26949
27104
  line: lineNumber2,
26950
27105
  confidence: 1,
26951
27106
  variable: "<inline>",
@@ -26963,7 +27118,7 @@ function findCSharpRequestSources(sourceCode, language) {
26963
27118
  sources.push({
26964
27119
  type,
26965
27120
  location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
26966
- severity: "high",
27121
+ severity: type === "db_input" ? "medium" : "high",
26967
27122
  line: lineNumber,
26968
27123
  confidence: 1,
26969
27124
  variable: varName
@@ -27157,7 +27312,7 @@ function findPythonAssignmentSources(sourceCode, language) {
27157
27312
  sources.push({
27158
27313
  type,
27159
27314
  location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
27160
- severity: "high",
27315
+ severity: type === "db_input" ? "medium" : "high",
27161
27316
  line: lineNumber,
27162
27317
  confidence: 0.95,
27163
27318
  variable: varName
@@ -37753,13 +37908,17 @@ function maskStringLiteralsForTokenScan(expr) {
37753
37908
  }
37754
37909
  return out2;
37755
37910
  }
37911
+ function argsReferenceTaintedVar(expr, sources) {
37912
+ const codePart = expr.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ").replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " ");
37913
+ return sources.some((s) => new RegExp(`(?<![\\p{L}\\p{N}_])${s.variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\p{L}\\p{N}_])`, "u").test(codePart));
37914
+ }
37756
37915
  function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachableLines, tainted, code, language, types) {
37757
37916
  const flows = [];
37758
- const goFnRanges = language === "go" && types ? types.flatMap((t) => t.methods.map((m) => [m.start_line, m.end_line])) : [];
37759
- const goFnStart = (line) => {
37917
+ const methodRanges = (langs) => language && langs.includes(language) && types ? types.flatMap((t) => t.methods.map((m) => [m.start_line, m.end_line])) : [];
37918
+ const enclosingStart = (ranges, line) => {
37760
37919
  let best = -1;
37761
37920
  let bestSpan = -1;
37762
- for (const [start2, end] of goFnRanges) {
37921
+ for (const [start2, end] of ranges) {
37763
37922
  if (line >= start2 && line <= end && end - start2 > bestSpan) {
37764
37923
  best = start2;
37765
37924
  bestSpan = end - start2;
@@ -37767,6 +37926,10 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
37767
37926
  }
37768
37927
  return best;
37769
37928
  };
37929
+ const goFnRanges = methodRanges(["go"]);
37930
+ const goFnStart = (line) => enclosingStart(goFnRanges, line);
37931
+ const csFnRanges = methodRanges(["csharp"]);
37932
+ const csFnStart = (line) => enclosingStart(csFnRanges, line);
37770
37933
  const sourcesWithVar = sources.filter((s) => typeof s.variable === "string" && s.variable.length > 0);
37771
37934
  const aliasSanitizedFor = new Map;
37772
37935
  if (language === "python" && typeof code === "string") {
@@ -38102,15 +38265,51 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
38102
38265
  const [, obj, rhs] = m;
38103
38266
  if (known.has(obj))
38104
38267
  continue;
38105
- const codePart = rhs.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ");
38106
- const rhsTainted = sourcesWithVar.some((s) => new RegExp(`(?<![\\p{L}\\p{N}_])${s.variable.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}(?![\\p{L}\\p{N}_])`, "u").test(codePart));
38107
- if (!rhsTainted)
38268
+ if (!argsReferenceTaintedVar(rhs, sourcesWithVar))
38108
38269
  continue;
38109
38270
  sourcesWithVar.push({ ...anchor, variable: obj, line: i2 + 1 });
38110
38271
  added = true;
38111
38272
  }
38112
38273
  }
38113
38274
  }
38275
+ const builderSeeds = new Set;
38276
+ if (language === "csharp" && typeof code === "string" && sourcesWithVar.length > 0) {
38277
+ const lines = code.split(`
38278
+ `);
38279
+ const builders = new Set;
38280
+ for (const line of lines) {
38281
+ for (const m of line.matchAll(/\bStringBuilder\s+([\p{L}_][\p{L}\p{N}_]*)/gu))
38282
+ builders.add(m[1]);
38283
+ for (const m of line.matchAll(/\b([\p{L}_][\p{L}\p{N}_]*)\s*=\s*new\s+StringBuilder\b/gu))
38284
+ builders.add(m[1]);
38285
+ }
38286
+ if (builders.size > 0) {
38287
+ const appendRe = /([\p{L}_][\p{L}\p{N}_]*)\s*\.\s*(?:Append|AppendLine|AppendFormat|Insert)\s*\(([\s\S]*)/u;
38288
+ const anchor = sourcesWithVar.reduce((a, s) => s.line < a.line ? s : a, sourcesWithVar[0]);
38289
+ let added = true;
38290
+ let guard = 0;
38291
+ while (added && guard < lines.length + 2) {
38292
+ added = false;
38293
+ guard++;
38294
+ const known = new Set(sourcesWithVar.map((s) => s.variable));
38295
+ for (let i2 = 0;i2 < lines.length; i2++) {
38296
+ const m = appendRe.exec(lines[i2]);
38297
+ if (!m)
38298
+ continue;
38299
+ const [, obj, args2] = m;
38300
+ if (!builders.has(obj) || known.has(obj))
38301
+ continue;
38302
+ const appendFn = csFnStart(i2 + 1);
38303
+ const reachable = sourcesWithVar.filter((sv) => appendFn === -1 || csFnStart(sv.line) === -1 || csFnStart(sv.line) === appendFn);
38304
+ if (reachable.length > 0 && argsReferenceTaintedVar(args2, reachable)) {
38305
+ sourcesWithVar.push({ ...anchor, variable: obj, line: i2 + 1 });
38306
+ builderSeeds.add(`${obj}@${i2 + 1}`);
38307
+ added = true;
38308
+ }
38309
+ }
38310
+ }
38311
+ }
38312
+ }
38114
38313
  const SIMPLE_IDENT = /^[\p{L}\p{N}_]+$/u;
38115
38314
  const reCache = new Map;
38116
38315
  for (const s of sourcesWithVar) {
@@ -38181,6 +38380,12 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
38181
38380
  if (srcFn !== -1 && sinkFn !== -1 && srcFn !== sinkFn)
38182
38381
  continue;
38183
38382
  }
38383
+ if (csFnRanges.length > 0 && builderSeeds.has(`${source.variable}@${source.line}`)) {
38384
+ const srcFn = csFnStart(source.line);
38385
+ const sinkFn = csFnStart(sink.line);
38386
+ if (srcFn !== sinkFn)
38387
+ continue;
38388
+ }
38184
38389
  if (complex) {
38185
38390
  const re = reCache.get(source.variable);
38186
38391
  if (!re || !re.test(expr))
@@ -49154,7 +49359,7 @@ async function analyze(code, filePath, language, options = {}) {
49154
49359
  };
49155
49360
  }
49156
49361
  const tTypes = phaseStart();
49157
- const types = extractTypes(tree, nodeCache, language);
49362
+ const types = extractTypes(tree, nodeCache, language, { navigationTypes: options.navigationTypes });
49158
49363
  phaseEnd("extractTypes", tTypes);
49159
49364
  const tCalls = phaseStart();
49160
49365
  const calls = extractCalls(tree, nodeCache, language);
@@ -50669,7 +50874,7 @@ var colors = {
50669
50874
  };
50670
50875
 
50671
50876
  // src/version.ts
50672
- var version = "4.9.28";
50877
+ var version = "4.10.0";
50673
50878
 
50674
50879
  // src/formatters.ts
50675
50880
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -51221,8 +51426,8 @@ function generateSarifResults(results, crossFileData) {
51221
51426
 
51222
51427
  // src/build-info.ts
51223
51428
  var buildInfo = {
51224
- gitSha: "7bfe5de",
51225
- builtAt: "2026-10-01T00:24:15.337Z"
51429
+ gitSha: "598f8fc",
51430
+ builtAt: "2026-10-02T23:23:28.653Z"
51226
51431
  };
51227
51432
 
51228
51433
  // src/utils/args.ts
@@ -51850,7 +52055,7 @@ async function initWasm(spin) {
51850
52055
  go: join3(wasmDir, "tree-sitter-go.wasm"),
51851
52056
  java: join3(wasmDir, "tree-sitter-java.wasm"),
51852
52057
  javascript: join3(wasmDir, "tree-sitter-javascript.wasm"),
51853
- typescript: join3(wasmDir, "tree-sitter-javascript.wasm"),
52058
+ typescript: join3(wasmDir, "tree-sitter-typescript.wasm"),
51854
52059
  python: join3(wasmDir, "tree-sitter-python.wasm"),
51855
52060
  rust: join3(wasmDir, "tree-sitter-rust.wasm"),
51856
52061
  html: join3(wasmDir, "tree-sitter-html.wasm"),
@@ -51885,7 +52090,7 @@ Please ensure the wasm/ directory is located next to the binary or in your curre
51885
52090
  go: wasmBasePath + "tree-sitter-go.wasm",
51886
52091
  java: wasmBasePath + "tree-sitter-java.wasm",
51887
52092
  javascript: wasmBasePath + "tree-sitter-javascript.wasm",
51888
- typescript: wasmBasePath + "tree-sitter-javascript.wasm",
52093
+ typescript: wasmBasePath + "tree-sitter-typescript.wasm",
51889
52094
  python: wasmBasePath + "tree-sitter-python.wasm",
51890
52095
  rust: wasmBasePath + "tree-sitter-rust.wasm",
51891
52096
  html: wasmBasePath + "tree-sitter-html.wasm",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.9.28",
3
+ "version": "4.10.0",
4
4
  "description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
5
5
  "bin": {
6
6
  "cognium-dev": "dist/cli.js"
@@ -12,7 +12,7 @@
12
12
  "test": "bun test",
13
13
  "clean": "rm -rf dist cognium-dev cognium-dev-* *.bun-build wasm",
14
14
  "typecheck": "tsc --noEmit",
15
- "version": "node -e \"const v = require('./package.json').version; require('fs').writeFileSync('src/version.ts', '/**\\n * Version information\\n *\\n * Kept in sync with package.json via the \\`version\\` npm lifecycle script.\\n * Do not edit manually \u2014 use \\`npm version patch|minor|major\\` instead.\\n */\\nexport const version = \\x27' + v + '\\x27;\\n')\" && git add src/version.ts",
15
+ "version": "node -e \"const v = require('./package.json').version; require('fs').writeFileSync('src/version.ts', '/**\\n * Version information\\n *\\n * Kept in sync with package.json via the \\`version\\` npm lifecycle script.\\n * Do not edit manually — use \\`npm version patch|minor|major\\` instead.\\n */\\nexport const version = \\x27' + v + '\\x27;\\n')\" && git add src/version.ts",
16
16
  "dogfood": "bun run src/cli.ts scan src/ -q",
17
17
  "prepublishOnly": "bun run build",
18
18
  "test:coverage": "bun test --coverage"
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "dependencies": {
67
67
  "@cognium/project-profile-detect": "1.1.2",
68
- "circle-ir": "4.9.28"
68
+ "circle-ir": "4.10.0"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^26.6.2",