cognium-dev 4.4.0 → 4.6.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 +96 -4
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -5268,6 +5268,14 @@ function findFirstDescendant(node, type) {
5268
5268
  }
5269
5269
  return null;
5270
5270
  }
5271
+ var CSHARP_COMMAND_EXECUTE_METHODS = new Set([
5272
+ "ExecuteScalar",
5273
+ "ExecuteReader",
5274
+ "ExecuteNonQuery",
5275
+ "ExecuteScalarAsync",
5276
+ "ExecuteReaderAsync",
5277
+ "ExecuteNonQueryAsync"
5278
+ ]);
5271
5279
  function extractCSharpCalls(tree, cache) {
5272
5280
  const calls = [];
5273
5281
  const typeMap = buildCSharpReceiverTypeMap(tree, cache);
@@ -5285,12 +5293,16 @@ function extractCSharpCalls(tree, cache) {
5285
5293
  methodName = getNodeText(fn);
5286
5294
  }
5287
5295
  const argsNode = inv.childForFieldName("arguments");
5296
+ let args2 = argsNode ? extractCSharpArguments(argsNode) : [];
5297
+ if (receiver && CSHARP_COMMAND_EXECUTE_METHODS.has(methodName)) {
5298
+ args2 = [{ position: 0, expression: receiver, variable: receiver, literal: null, value: null }, ...args2];
5299
+ }
5288
5300
  calls.push({
5289
5301
  method_name: methodName,
5290
5302
  receiver,
5291
5303
  receiver_type: receiver ? typeMap.get(receiver) ?? null : null,
5292
5304
  receiver_type_fqn: null,
5293
- arguments: argsNode ? extractCSharpArguments(argsNode) : [],
5305
+ arguments: args2,
5294
5306
  location: { line: inv.startPosition.row + 1, column: inv.startPosition.column },
5295
5307
  in_method: findEnclosingMethod(inv)
5296
5308
  });
@@ -12314,6 +12326,12 @@ var DEFAULT_SINKS = [
12314
12326
  { method: "FromSqlRaw", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12315
12327
  { method: "FromSqlRawAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12316
12328
  { method: "CommandText", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12329
+ { method: "ExecuteScalar", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12330
+ { method: "ExecuteReader", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12331
+ { method: "ExecuteNonQuery", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12332
+ { method: "ExecuteScalarAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12333
+ { method: "ExecuteReaderAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12334
+ { method: "ExecuteNonQueryAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12317
12335
  { method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12318
12336
  { method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
12319
12337
  { method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -25619,18 +25637,20 @@ function findCSharpRequestSources(sourceCode, language) {
25619
25637
  `);
25620
25638
  const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
25621
25639
  const requestReadRe = /\bRequest\s*\.\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\b/;
25640
+ const consoleReadRe = /\bConsole\s*\.\s*ReadLine\s*\(/;
25622
25641
  for (let i2 = 0;i2 < lines.length; i2++) {
25623
25642
  const m = assignRe.exec(lines[i2]);
25624
25643
  if (!m)
25625
25644
  continue;
25626
25645
  const [, varName, rhs] = m;
25627
- if (!requestReadRe.test(rhs))
25646
+ const type = requestReadRe.test(rhs) ? "http_param" : consoleReadRe.test(rhs) ? "io_input" : null;
25647
+ if (!type)
25628
25648
  continue;
25629
25649
  const lineNumber = i2 + 1;
25630
25650
  if (sources.some((s) => s.line === lineNumber && s.variable === varName))
25631
25651
  continue;
25632
25652
  sources.push({
25633
- type: "http_param",
25653
+ type,
25634
25654
  location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
25635
25655
  severity: "high",
25636
25656
  line: lineNumber,
@@ -36020,6 +36040,33 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
36020
36040
  }
36021
36041
  }
36022
36042
  }
36043
+ if (language === "csharp" && typeof code === "string" && sourcesWithVar.length > 0) {
36044
+ const lines = code.split(`
36045
+ `);
36046
+ const ctRe = /(\w+)\s*\.\s*CommandText\s*\+?=\s*(.+)/;
36047
+ const anchor = sourcesWithVar.reduce((a, s) => s.line < a.line ? s : a, sourcesWithVar[0]);
36048
+ let added = true;
36049
+ let guard = 0;
36050
+ while (added && guard < lines.length + 2) {
36051
+ added = false;
36052
+ guard++;
36053
+ const known = new Set(sourcesWithVar.map((s) => s.variable));
36054
+ for (let i2 = 0;i2 < lines.length; i2++) {
36055
+ const m = ctRe.exec(lines[i2]);
36056
+ if (!m)
36057
+ continue;
36058
+ const [, obj, rhs] = m;
36059
+ if (known.has(obj))
36060
+ continue;
36061
+ const codePart = rhs.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ");
36062
+ const rhsTainted = sourcesWithVar.some((s) => new RegExp(`(?<![\\p{L}\\p{N}_])${s.variable.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}(?![\\p{L}\\p{N}_])`, "u").test(codePart));
36063
+ if (!rhsTainted)
36064
+ continue;
36065
+ sourcesWithVar.push({ ...anchor, variable: obj, line: i2 + 1 });
36066
+ added = true;
36067
+ }
36068
+ }
36069
+ }
36023
36070
  const reCache = new Map;
36024
36071
  for (const s of sourcesWithVar) {
36025
36072
  if (reCache.has(s.variable))
@@ -43859,6 +43906,49 @@ function methodAcceptsHtmlShapedParam(method) {
43859
43906
  return false;
43860
43907
  }
43861
43908
 
43909
+ // ../circle-ir/dist/analysis/passes/insecure-deserialization-config-pass.js
43910
+ var ANY_TYPE_PERMISSION_RE = /\bAnyTypePermission\b/;
43911
+
43912
+ class InsecureDeserializationConfigPass {
43913
+ name = "insecure-deserialization-config";
43914
+ category = "security";
43915
+ run(ctx) {
43916
+ const { graph, language } = ctx;
43917
+ if (language !== "java")
43918
+ return { findings: [] };
43919
+ const file = graph.ir.meta.file;
43920
+ const findings = [];
43921
+ for (const call of graph.ir.calls) {
43922
+ if (!this.isPermissiveXStreamConfig(call))
43923
+ continue;
43924
+ const line = call.location.line;
43925
+ const api = `addPermission(${call.arguments[0]?.expression ?? "AnyTypePermission"})`;
43926
+ findings.push({ line, api });
43927
+ ctx.addFinding({
43928
+ id: `${this.name}-${file}-${line}`,
43929
+ pass: this.name,
43930
+ category: this.category,
43931
+ rule_id: this.name,
43932
+ cwe: "CWE-502",
43933
+ severity: "high",
43934
+ level: "error",
43935
+ message: "XStream configured with AnyTypePermission (grant-all): untrusted XML " + "passed to fromXML/unmarshal can instantiate arbitrary types (deserialization RCE)",
43936
+ file,
43937
+ line,
43938
+ fix: "Replace AnyTypePermission with an explicit type allow-list: xstream.addPermission(NoTypePermission.NONE) then allowTypes/allowTypesByWildcard for the classes you deserialize.",
43939
+ evidence: { api, language }
43940
+ });
43941
+ }
43942
+ return { findings };
43943
+ }
43944
+ isPermissiveXStreamConfig(call) {
43945
+ if (call.method_name !== "addPermission")
43946
+ return false;
43947
+ const arg0 = call.arguments[0]?.expression;
43948
+ return typeof arg0 === "string" && ANY_TYPE_PERMISSION_RE.test(arg0);
43949
+ }
43950
+ }
43951
+
43862
43952
  // ../circle-ir/dist/analysis/passes/plaintext-password-storage-pass.js
43863
43953
  function isWriteStorageCall(call, language) {
43864
43954
  const method = call.method_name ?? "";
@@ -46544,6 +46634,8 @@ async function analyze(code, filePath, language, options = {}) {
46544
46634
  pipeline.add(new UnrestrictedFileUploadPass);
46545
46635
  if (!disabledPasses.has("missing-sanitizer-gate"))
46546
46636
  pipeline.add(new MissingSanitizerGatePass);
46637
+ if (!disabledPasses.has("insecure-deserialization-config"))
46638
+ pipeline.add(new InsecureDeserializationConfigPass);
46547
46639
  const { results, findings } = pipeline.run(graph, code, language, config);
46548
46640
  const sinkFilter = results.get("sink-filter");
46549
46641
  const interProc = results.get("interprocedural");
@@ -47808,7 +47900,7 @@ var colors = {
47808
47900
  };
47809
47901
 
47810
47902
  // src/version.ts
47811
- var version = "4.4.0";
47903
+ var version = "4.6.0";
47812
47904
 
47813
47905
  // src/formatters.ts
47814
47906
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.4.0",
3
+ "version": "4.6.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": "^4.4.0"
69
+ "circle-ir": "^4.6.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",