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.
- package/dist/cli.js +189 -4
- 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.
|
|
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.
|
|
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.
|
|
68
|
+
"circle-ir": "^3.57.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|