cognium-dev 3.55.0 → 3.57.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 +189 -4
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -13622,6 +13622,27 @@ class ConstantPropagator {
13622
13622
  if (nameNode) {
13623
13623
  const varName = getNodeText2(nameNode, this.source);
13624
13624
  loopVarNames.add(varName);
13625
+ const collectionNode = node.childForFieldName("value");
13626
+ if (collectionNode) {
13627
+ const collectionName = getNodeText2(collectionNode, this.source);
13628
+ const scopedCollection = this.currentMethod ? `${this.currentMethod}:${collectionName}` : collectionName;
13629
+ let elementIsTainted = this.tainted.has(collectionName) || this.tainted.has(scopedCollection);
13630
+ if (!elementIsTainted) {
13631
+ const taintedIndices = this.taintedArrayElements.get(collectionName);
13632
+ if (taintedIndices && taintedIndices.size > 0)
13633
+ elementIsTainted = true;
13634
+ }
13635
+ if (!elementIsTainted) {
13636
+ const taintedKeys = this.taintedCollections.get(collectionName);
13637
+ if (taintedKeys && taintedKeys.size > 0)
13638
+ elementIsTainted = true;
13639
+ }
13640
+ if (elementIsTainted) {
13641
+ const scopedVar = this.currentMethod ? `${this.currentMethod}:${varName}` : varName;
13642
+ this.tainted.add(varName);
13643
+ this.tainted.add(scopedVar);
13644
+ }
13645
+ }
13625
13646
  }
13626
13647
  }
13627
13648
  for (const varName of loopVarNames) {
@@ -14514,9 +14535,21 @@ class ConstantPropagator {
14514
14535
  this.taintedCollections.set(collectionName, new Set);
14515
14536
  }
14516
14537
  this.taintedCollections.get(collectionName).add(keyStr);
14538
+ const scopedCollection = this.currentMethod ? `${this.currentMethod}:${collectionName}` : collectionName;
14539
+ this.tainted.add(scopedCollection);
14540
+ this.tainted.add(collectionName);
14517
14541
  }
14518
14542
  }
14519
14543
  }
14544
+ if (methodName === "append" || methodName === "insert") {
14545
+ const args2 = argsNode.children.filter((c) => c.type !== "(" && c.type !== ")" && c.type !== ",");
14546
+ const valueArg = methodName === "insert" && args2.length >= 2 ? args2[1] : args2[0];
14547
+ if (valueArg && this.isTaintedExpression(valueArg)) {
14548
+ const scopedCollection = this.currentMethod ? `${this.currentMethod}:${collectionName}` : collectionName;
14549
+ this.tainted.add(scopedCollection);
14550
+ this.tainted.add(collectionName);
14551
+ }
14552
+ }
14520
14553
  if (methodName === "add" || methodName === "addLast") {
14521
14554
  const args2 = argsNode.children.filter((c) => c.type !== "(" && c.type !== ")" && c.type !== ",");
14522
14555
  if (args2.length >= 1) {
@@ -27514,6 +27547,113 @@ function detectHardcodedKeyJava(call) {
27514
27547
  return `literal string`;
27515
27548
  return null;
27516
27549
  }
27550
+ function detectHardcodedKeyPython(call, constProp, literalBindings) {
27551
+ const arg = call.arguments.find((a) => a.position === 0);
27552
+ if (!arg)
27553
+ return null;
27554
+ const expr = (arg.expression ?? arg.literal ?? "").trim();
27555
+ if (!expr)
27556
+ return null;
27557
+ if (/^[bB][rR]?["'][^"']*["']$/.test(expr) || /^[rR][bB]["'][^"']*["']$/.test(expr)) {
27558
+ return `literal bytes ${expr.slice(0, 24)}${expr.length > 24 ? "…" : ""}`;
27559
+ }
27560
+ if (/^["'][^"']*["']$/.test(expr)) {
27561
+ return `literal string ${expr.slice(0, 24)}${expr.length > 24 ? "…" : ""}`;
27562
+ }
27563
+ if (arg.variable && constProp) {
27564
+ const sym = constProp.symbols.get(arg.variable);
27565
+ if (sym && sym.type === "string" && typeof sym.value === "string") {
27566
+ return `constant-propagated bytes from \`${arg.variable}\``;
27567
+ }
27568
+ }
27569
+ if (arg.variable) {
27570
+ const lit = literalBindings.get(arg.variable);
27571
+ if (lit) {
27572
+ return `literal-bound ${arg.variable} = ${lit.slice(0, 24)}${lit.length > 24 ? "…" : ""}`;
27573
+ }
27574
+ }
27575
+ return null;
27576
+ }
27577
+ function detectHardcodedKeyGo(call, constProp, literalBindings) {
27578
+ const arg = call.arguments.find((a) => a.position === 0);
27579
+ if (!arg)
27580
+ return null;
27581
+ const expr = (arg.literal ?? arg.expression ?? "").trim();
27582
+ if (!expr)
27583
+ return null;
27584
+ if (/^\[\s*\]\s*byte\s*\(\s*["'`][^"'`]*["'`]\s*\)$/.test(expr)) {
27585
+ return `literal []byte("…")`;
27586
+ }
27587
+ if (/^\[\s*\]\s*byte\s*\{[^}]*\}$/.test(expr)) {
27588
+ return `literal []byte{…} composite`;
27589
+ }
27590
+ if (arg.variable && constProp) {
27591
+ const sym = constProp.symbols.get(arg.variable);
27592
+ if (sym && sym.type === "string" && typeof sym.value === "string") {
27593
+ return `constant-propagated key from \`${arg.variable}\``;
27594
+ }
27595
+ }
27596
+ if (arg.variable) {
27597
+ const lit = literalBindings.get(arg.variable);
27598
+ if (lit) {
27599
+ return `literal-bound ${arg.variable} = ${lit.slice(0, 24)}${lit.length > 24 ? "…" : ""}`;
27600
+ }
27601
+ }
27602
+ return null;
27603
+ }
27604
+ function parseWeakRsaKeySizePython(call) {
27605
+ for (const arg of call.arguments) {
27606
+ const expr = (arg.expression ?? "").trim();
27607
+ const lit = (arg.literal ?? "").trim();
27608
+ const m = expr.match(/^key_size\s*=\s*(-?\d+)\s*$/);
27609
+ if (m && m[1]) {
27610
+ const n = parseInt(m[1], 10);
27611
+ if (Number.isFinite(n) && n > 0 && n < 2048)
27612
+ return n;
27613
+ return null;
27614
+ }
27615
+ if (/^key_size\s*=/.test(expr) && lit) {
27616
+ const n = parseInt(lit, 10);
27617
+ if (Number.isFinite(n) && n > 0 && n < 2048)
27618
+ return n;
27619
+ }
27620
+ }
27621
+ return null;
27622
+ }
27623
+ function scanLiteralBindings(code, language) {
27624
+ const out2 = new Map;
27625
+ if (!code)
27626
+ return out2;
27627
+ if (language === "python") {
27628
+ const re = /^[ \t]*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(b[rR]?["'][^"']*["']|[rR]?b["'][^"']*["']|["'][^"']*["'])\s*(?:$|#)/gm;
27629
+ let m;
27630
+ while ((m = re.exec(code)) !== null) {
27631
+ if (m[1] && m[2])
27632
+ out2.set(m[1], m[2]);
27633
+ }
27634
+ return out2;
27635
+ }
27636
+ if (language === "go") {
27637
+ const reByte = /^[ \t]*(?:var\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*(?::=|=)\s*(\[\s*\]\s*byte\s*\(\s*["'`][^"'`]*["'`]\s*\))/gm;
27638
+ let m;
27639
+ while ((m = reByte.exec(code)) !== null) {
27640
+ if (m[1] && m[2])
27641
+ out2.set(m[1], m[2]);
27642
+ }
27643
+ const reStr = /^[ \t]*(?:var|const)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(["'`][^"'`]*["'`])/gm;
27644
+ while ((m = reStr.exec(code)) !== null) {
27645
+ if (m[1] && m[2])
27646
+ out2.set(m[1], m[2]);
27647
+ }
27648
+ const reShort = /^[ \t]*([A-Za-z_][A-Za-z0-9_]*)\s*:=\s*(["'`][^"'`]*["'`])/gm;
27649
+ while ((m = reShort.exec(code)) !== null) {
27650
+ if (m[1] && m[2])
27651
+ out2.set(m[1], m[2]);
27652
+ }
27653
+ return out2;
27654
+ }
27655
+ return out2;
27656
+ }
27517
27657
  var ISSUE_CWE = {
27518
27658
  "weak-cipher": "CWE-327",
27519
27659
  "ecb-mode": "CWE-327",
@@ -27527,11 +27667,13 @@ class WeakCryptoPass {
27527
27667
  name = "weak-crypto";
27528
27668
  category = "security";
27529
27669
  run(ctx) {
27530
- const { graph, language } = ctx;
27670
+ const { graph, language, code } = ctx;
27531
27671
  const file = graph.ir.meta.file;
27532
27672
  const findings = [];
27673
+ const constProp = ctx.hasResult("constant-propagation") ? ctx.getResult("constant-propagation") : null;
27674
+ const literalBindings = scanLiteralBindings(code, language);
27533
27675
  for (const call of graph.ir.calls) {
27534
- const detections = this.detect(call, language);
27676
+ const detections = this.detect(call, language, constProp, literalBindings);
27535
27677
  for (const det of detections) {
27536
27678
  const line = call.location.line;
27537
27679
  findings.push({ line, language, ...det });
@@ -27584,7 +27726,7 @@ class WeakCryptoPass {
27584
27726
  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
27727
  }
27586
27728
  }
27587
- detect(call, language) {
27729
+ detect(call, language, constProp, literalBindings) {
27588
27730
  const method = call.method_name;
27589
27731
  const receiver = call.receiver ?? "";
27590
27732
  const out2 = [];
@@ -27644,6 +27786,12 @@ class WeakCryptoPass {
27644
27786
  out2.push({ issue: "ecb-mode", detail: "AES.MODE_ECB", api: `${receiver}.new` });
27645
27787
  }
27646
27788
  }
27789
+ if (lastSeg === "aes" || lastSeg.endsWith(".aes") || WEAK_CIPHER_BASES.has(lastSeg)) {
27790
+ const keyDetail = detectHardcodedKeyPython(call, constProp, literalBindings);
27791
+ if (keyDetail) {
27792
+ out2.push({ issue: "hardcoded-key", detail: keyDetail, api: `${receiver}.new` });
27793
+ }
27794
+ }
27647
27795
  }
27648
27796
  const isHazmatAlgos = receiver === "algorithms" || receiver.endsWith(".algorithms");
27649
27797
  if (isHazmatAlgos) {
@@ -27652,6 +27800,25 @@ class WeakCryptoPass {
27652
27800
  if (WEAK_CIPHER_BASES.has(normalized)) {
27653
27801
  out2.push({ issue: "weak-cipher", detail: normalized, api: `algorithms.${method}` });
27654
27802
  }
27803
+ if (m === "aes") {
27804
+ const keyDetail = detectHardcodedKeyPython(call, constProp, literalBindings);
27805
+ if (keyDetail) {
27806
+ out2.push({ issue: "hardcoded-key", detail: keyDetail, api: `algorithms.${method}` });
27807
+ }
27808
+ }
27809
+ }
27810
+ if (method === "ECB" && (receiver === "modes" || receiver.endsWith(".modes"))) {
27811
+ out2.push({ issue: "ecb-mode", detail: "modes.ECB()", api: `${receiver}.ECB` });
27812
+ }
27813
+ if (method === "generate_private_key" && (receiver === "rsa" || receiver === "dsa" || receiver.endsWith(".rsa") || receiver.endsWith(".dsa"))) {
27814
+ const n = parseWeakRsaKeySizePython(call);
27815
+ if (n !== null) {
27816
+ out2.push({
27817
+ issue: "weak-rsa-key",
27818
+ detail: String(n),
27819
+ api: `${receiver}.generate_private_key`
27820
+ });
27821
+ }
27655
27822
  }
27656
27823
  return out2;
27657
27824
  }
@@ -27693,6 +27860,24 @@ class WeakCryptoPass {
27693
27860
  if ((method === "NewECBEncrypter" || method === "NewECBDecrypter") && receiver === "cipher") {
27694
27861
  out2.push({ issue: "ecb-mode", detail: method, api: `cipher.${method}` });
27695
27862
  }
27863
+ if (receiver === "aes" && method === "NewCipher" || receiver === "des" && (method === "NewCipher" || method === "NewTripleDESCipher") || receiver === "rc4" && method === "NewCipher") {
27864
+ const keyDetail = detectHardcodedKeyGo(call, constProp, literalBindings);
27865
+ if (keyDetail) {
27866
+ out2.push({ issue: "hardcoded-key", detail: keyDetail, api: `${receiver}.${method}` });
27867
+ }
27868
+ }
27869
+ if (receiver === "rsa" && method === "GenerateKey") {
27870
+ const bitsArg = call.arguments.find((a) => a.position === 1);
27871
+ const expr = (bitsArg?.literal ?? bitsArg?.expression ?? "").trim();
27872
+ const n = parseInt(expr, 10);
27873
+ if (Number.isFinite(n) && n > 0 && n < 2048) {
27874
+ out2.push({
27875
+ issue: "weak-rsa-key",
27876
+ detail: String(n),
27877
+ api: "rsa.GenerateKey"
27878
+ });
27879
+ }
27880
+ }
27696
27881
  return out2;
27697
27882
  }
27698
27883
  return out2;
@@ -29838,7 +30023,7 @@ var colors = {
29838
30023
  };
29839
30024
 
29840
30025
  // src/version.ts
29841
- var version = "3.55.0";
30026
+ var version = "3.57.0";
29842
30027
 
29843
30028
  // src/formatters.ts
29844
30029
  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.57.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.57.0"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^25.5.0",