cognium-dev 3.55.0 → 3.56.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 +156 -4
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -27514,6 +27514,113 @@ function detectHardcodedKeyJava(call) {
27514
27514
  return `literal string`;
27515
27515
  return null;
27516
27516
  }
27517
+ function detectHardcodedKeyPython(call, constProp, literalBindings) {
27518
+ const arg = call.arguments.find((a) => a.position === 0);
27519
+ if (!arg)
27520
+ return null;
27521
+ const expr = (arg.expression ?? arg.literal ?? "").trim();
27522
+ if (!expr)
27523
+ return null;
27524
+ if (/^[bB][rR]?["'][^"']*["']$/.test(expr) || /^[rR][bB]["'][^"']*["']$/.test(expr)) {
27525
+ return `literal bytes ${expr.slice(0, 24)}${expr.length > 24 ? "…" : ""}`;
27526
+ }
27527
+ if (/^["'][^"']*["']$/.test(expr)) {
27528
+ return `literal string ${expr.slice(0, 24)}${expr.length > 24 ? "…" : ""}`;
27529
+ }
27530
+ if (arg.variable && constProp) {
27531
+ const sym = constProp.symbols.get(arg.variable);
27532
+ if (sym && sym.type === "string" && typeof sym.value === "string") {
27533
+ return `constant-propagated bytes from \`${arg.variable}\``;
27534
+ }
27535
+ }
27536
+ if (arg.variable) {
27537
+ const lit = literalBindings.get(arg.variable);
27538
+ if (lit) {
27539
+ return `literal-bound ${arg.variable} = ${lit.slice(0, 24)}${lit.length > 24 ? "…" : ""}`;
27540
+ }
27541
+ }
27542
+ return null;
27543
+ }
27544
+ function detectHardcodedKeyGo(call, constProp, literalBindings) {
27545
+ const arg = call.arguments.find((a) => a.position === 0);
27546
+ if (!arg)
27547
+ return null;
27548
+ const expr = (arg.literal ?? arg.expression ?? "").trim();
27549
+ if (!expr)
27550
+ return null;
27551
+ if (/^\[\s*\]\s*byte\s*\(\s*["'`][^"'`]*["'`]\s*\)$/.test(expr)) {
27552
+ return `literal []byte("…")`;
27553
+ }
27554
+ if (/^\[\s*\]\s*byte\s*\{[^}]*\}$/.test(expr)) {
27555
+ return `literal []byte{…} composite`;
27556
+ }
27557
+ if (arg.variable && constProp) {
27558
+ const sym = constProp.symbols.get(arg.variable);
27559
+ if (sym && sym.type === "string" && typeof sym.value === "string") {
27560
+ return `constant-propagated key from \`${arg.variable}\``;
27561
+ }
27562
+ }
27563
+ if (arg.variable) {
27564
+ const lit = literalBindings.get(arg.variable);
27565
+ if (lit) {
27566
+ return `literal-bound ${arg.variable} = ${lit.slice(0, 24)}${lit.length > 24 ? "…" : ""}`;
27567
+ }
27568
+ }
27569
+ return null;
27570
+ }
27571
+ function parseWeakRsaKeySizePython(call) {
27572
+ for (const arg of call.arguments) {
27573
+ const expr = (arg.expression ?? "").trim();
27574
+ const lit = (arg.literal ?? "").trim();
27575
+ const m = expr.match(/^key_size\s*=\s*(-?\d+)\s*$/);
27576
+ if (m && m[1]) {
27577
+ const n = parseInt(m[1], 10);
27578
+ if (Number.isFinite(n) && n > 0 && n < 2048)
27579
+ return n;
27580
+ return null;
27581
+ }
27582
+ if (/^key_size\s*=/.test(expr) && lit) {
27583
+ const n = parseInt(lit, 10);
27584
+ if (Number.isFinite(n) && n > 0 && n < 2048)
27585
+ return n;
27586
+ }
27587
+ }
27588
+ return null;
27589
+ }
27590
+ function scanLiteralBindings(code, language) {
27591
+ const out2 = new Map;
27592
+ if (!code)
27593
+ return out2;
27594
+ if (language === "python") {
27595
+ const re = /^[ \t]*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(b[rR]?["'][^"']*["']|[rR]?b["'][^"']*["']|["'][^"']*["'])\s*(?:$|#)/gm;
27596
+ let m;
27597
+ while ((m = re.exec(code)) !== null) {
27598
+ if (m[1] && m[2])
27599
+ out2.set(m[1], m[2]);
27600
+ }
27601
+ return out2;
27602
+ }
27603
+ if (language === "go") {
27604
+ const reByte = /^[ \t]*(?:var\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*(?::=|=)\s*(\[\s*\]\s*byte\s*\(\s*["'`][^"'`]*["'`]\s*\))/gm;
27605
+ let m;
27606
+ while ((m = reByte.exec(code)) !== null) {
27607
+ if (m[1] && m[2])
27608
+ out2.set(m[1], m[2]);
27609
+ }
27610
+ const reStr = /^[ \t]*(?:var|const)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(["'`][^"'`]*["'`])/gm;
27611
+ while ((m = reStr.exec(code)) !== null) {
27612
+ if (m[1] && m[2])
27613
+ out2.set(m[1], m[2]);
27614
+ }
27615
+ const reShort = /^[ \t]*([A-Za-z_][A-Za-z0-9_]*)\s*:=\s*(["'`][^"'`]*["'`])/gm;
27616
+ while ((m = reShort.exec(code)) !== null) {
27617
+ if (m[1] && m[2])
27618
+ out2.set(m[1], m[2]);
27619
+ }
27620
+ return out2;
27621
+ }
27622
+ return out2;
27623
+ }
27517
27624
  var ISSUE_CWE = {
27518
27625
  "weak-cipher": "CWE-327",
27519
27626
  "ecb-mode": "CWE-327",
@@ -27527,11 +27634,13 @@ class WeakCryptoPass {
27527
27634
  name = "weak-crypto";
27528
27635
  category = "security";
27529
27636
  run(ctx) {
27530
- const { graph, language } = ctx;
27637
+ const { graph, language, code } = ctx;
27531
27638
  const file = graph.ir.meta.file;
27532
27639
  const findings = [];
27640
+ const constProp = ctx.hasResult("constant-propagation") ? ctx.getResult("constant-propagation") : null;
27641
+ const literalBindings = scanLiteralBindings(code, language);
27533
27642
  for (const call of graph.ir.calls) {
27534
- const detections = this.detect(call, language);
27643
+ const detections = this.detect(call, language, constProp, literalBindings);
27535
27644
  for (const det of detections) {
27536
27645
  const line = call.location.line;
27537
27646
  findings.push({ line, language, ...det });
@@ -27584,7 +27693,7 @@ class WeakCryptoPass {
27584
27693
  return "Use AES-GCM (authenticated) or ChaCha20-Poly1305. Avoid DES, " + "3DES, RC2, RC4, Blowfish, and ECB mode. For asymmetric encryption " + "use RSA-OAEP with ≥2048-bit keys or modern curve-based schemes.";
27585
27694
  }
27586
27695
  }
27587
- detect(call, language) {
27696
+ detect(call, language, constProp, literalBindings) {
27588
27697
  const method = call.method_name;
27589
27698
  const receiver = call.receiver ?? "";
27590
27699
  const out2 = [];
@@ -27644,6 +27753,12 @@ class WeakCryptoPass {
27644
27753
  out2.push({ issue: "ecb-mode", detail: "AES.MODE_ECB", api: `${receiver}.new` });
27645
27754
  }
27646
27755
  }
27756
+ if (lastSeg === "aes" || lastSeg.endsWith(".aes") || WEAK_CIPHER_BASES.has(lastSeg)) {
27757
+ const keyDetail = detectHardcodedKeyPython(call, constProp, literalBindings);
27758
+ if (keyDetail) {
27759
+ out2.push({ issue: "hardcoded-key", detail: keyDetail, api: `${receiver}.new` });
27760
+ }
27761
+ }
27647
27762
  }
27648
27763
  const isHazmatAlgos = receiver === "algorithms" || receiver.endsWith(".algorithms");
27649
27764
  if (isHazmatAlgos) {
@@ -27652,6 +27767,25 @@ class WeakCryptoPass {
27652
27767
  if (WEAK_CIPHER_BASES.has(normalized)) {
27653
27768
  out2.push({ issue: "weak-cipher", detail: normalized, api: `algorithms.${method}` });
27654
27769
  }
27770
+ if (m === "aes") {
27771
+ const keyDetail = detectHardcodedKeyPython(call, constProp, literalBindings);
27772
+ if (keyDetail) {
27773
+ out2.push({ issue: "hardcoded-key", detail: keyDetail, api: `algorithms.${method}` });
27774
+ }
27775
+ }
27776
+ }
27777
+ if (method === "ECB" && (receiver === "modes" || receiver.endsWith(".modes"))) {
27778
+ out2.push({ issue: "ecb-mode", detail: "modes.ECB()", api: `${receiver}.ECB` });
27779
+ }
27780
+ if (method === "generate_private_key" && (receiver === "rsa" || receiver === "dsa" || receiver.endsWith(".rsa") || receiver.endsWith(".dsa"))) {
27781
+ const n = parseWeakRsaKeySizePython(call);
27782
+ if (n !== null) {
27783
+ out2.push({
27784
+ issue: "weak-rsa-key",
27785
+ detail: String(n),
27786
+ api: `${receiver}.generate_private_key`
27787
+ });
27788
+ }
27655
27789
  }
27656
27790
  return out2;
27657
27791
  }
@@ -27693,6 +27827,24 @@ class WeakCryptoPass {
27693
27827
  if ((method === "NewECBEncrypter" || method === "NewECBDecrypter") && receiver === "cipher") {
27694
27828
  out2.push({ issue: "ecb-mode", detail: method, api: `cipher.${method}` });
27695
27829
  }
27830
+ if (receiver === "aes" && method === "NewCipher" || receiver === "des" && (method === "NewCipher" || method === "NewTripleDESCipher") || receiver === "rc4" && method === "NewCipher") {
27831
+ const keyDetail = detectHardcodedKeyGo(call, constProp, literalBindings);
27832
+ if (keyDetail) {
27833
+ out2.push({ issue: "hardcoded-key", detail: keyDetail, api: `${receiver}.${method}` });
27834
+ }
27835
+ }
27836
+ if (receiver === "rsa" && method === "GenerateKey") {
27837
+ const bitsArg = call.arguments.find((a) => a.position === 1);
27838
+ const expr = (bitsArg?.literal ?? bitsArg?.expression ?? "").trim();
27839
+ const n = parseInt(expr, 10);
27840
+ if (Number.isFinite(n) && n > 0 && n < 2048) {
27841
+ out2.push({
27842
+ issue: "weak-rsa-key",
27843
+ detail: String(n),
27844
+ api: "rsa.GenerateKey"
27845
+ });
27846
+ }
27847
+ }
27696
27848
  return out2;
27697
27849
  }
27698
27850
  return out2;
@@ -29838,7 +29990,7 @@ var colors = {
29838
29990
  };
29839
29991
 
29840
29992
  // src/version.ts
29841
- var version = "3.55.0";
29993
+ var version = "3.56.0";
29842
29994
 
29843
29995
  // src/formatters.ts
29844
29996
  var SINK_SEVERITY = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "3.55.0",
3
+ "version": "3.56.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",
@@ -65,7 +65,7 @@
65
65
  "registry": "https://registry.npmjs.org/"
66
66
  },
67
67
  "dependencies": {
68
- "circle-ir": "^3.55.0"
68
+ "circle-ir": "^3.56.0"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^25.5.0",