cognium-dev 3.97.0 → 3.99.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 +70 -10
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -12245,6 +12245,27 @@ function isSafeGoJsonUnmarshalCall(call, pattern, language, sourceLines) {
|
|
|
12245
12245
|
}
|
|
12246
12246
|
return false;
|
|
12247
12247
|
}
|
|
12248
|
+
var TEMPLATE_LITERAL_RECEIVER_RE = /^Template\(\s*(?:"[^"\\]*"|'[^'\\]*')\s*\)$/;
|
|
12249
|
+
function isSafeJinjaRenderCall(call, pattern, language) {
|
|
12250
|
+
if (language !== "python")
|
|
12251
|
+
return false;
|
|
12252
|
+
if (pattern.type !== "xss" && pattern.type !== "code_injection")
|
|
12253
|
+
return false;
|
|
12254
|
+
const method = call.method_name;
|
|
12255
|
+
if (method === "render_template_string") {
|
|
12256
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
12257
|
+
return typeof arg0?.literal === "string";
|
|
12258
|
+
}
|
|
12259
|
+
if (method === "Template" || method === "from_string") {
|
|
12260
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
12261
|
+
return typeof arg0?.literal === "string";
|
|
12262
|
+
}
|
|
12263
|
+
if (method === "render") {
|
|
12264
|
+
const receiver = (call.receiver ?? "").trim();
|
|
12265
|
+
return TEMPLATE_LITERAL_RECEIVER_RE.test(receiver);
|
|
12266
|
+
}
|
|
12267
|
+
return false;
|
|
12268
|
+
}
|
|
12248
12269
|
function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
|
|
12249
12270
|
const sinkMap = new Map;
|
|
12250
12271
|
for (const call of calls) {
|
|
@@ -12286,6 +12307,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
|
|
|
12286
12307
|
if (isSafeGoJsonUnmarshalCall(call, pattern, language, sourceLines)) {
|
|
12287
12308
|
continue;
|
|
12288
12309
|
}
|
|
12310
|
+
if (isSafeJinjaRenderCall(call, pattern, language)) {
|
|
12311
|
+
continue;
|
|
12312
|
+
}
|
|
12289
12313
|
const location = formatCallLocation(call);
|
|
12290
12314
|
const key = `${location}:${call.location.line}:${pattern.cwe}`;
|
|
12291
12315
|
const confidence = calculateSinkConfidence(call, pattern);
|
|
@@ -22711,6 +22735,13 @@ function findPythonTrustBoundaryViolations(sourceCode, taintedVars) {
|
|
|
22711
22735
|
}
|
|
22712
22736
|
return violations;
|
|
22713
22737
|
}
|
|
22738
|
+
var JINJA_LITERAL_ARG_RE_FRAG = `(?:"[^"\\\\]*"|'[^'\\\\]*')`;
|
|
22739
|
+
var JINJA_SAFE_RTS_RE = new RegExp(`^(?:flask\\.|jinja2\\.)?render_template_string\\s*\\(\\s*${JINJA_LITERAL_ARG_RE_FRAG}\\s*[,)]`);
|
|
22740
|
+
var JINJA_SAFE_TEMPLATE_RE = new RegExp(`^(?:jinja2\\.)?Template\\s*\\(\\s*${JINJA_LITERAL_ARG_RE_FRAG}\\s*\\)(?:\\s*\\.render\\s*\\(|\\s*$)`);
|
|
22741
|
+
function isSafeJinjaReturnExpr(expr) {
|
|
22742
|
+
const trimmed = expr.trim();
|
|
22743
|
+
return JINJA_SAFE_RTS_RE.test(trimmed) || JINJA_SAFE_TEMPLATE_RE.test(trimmed);
|
|
22744
|
+
}
|
|
22714
22745
|
function findPythonReturnXSSSinks(sourceCode, taintedVars) {
|
|
22715
22746
|
if (taintedVars.size === 0)
|
|
22716
22747
|
return [];
|
|
@@ -22732,6 +22763,8 @@ function findPythonReturnXSSSinks(sourceCode, taintedVars) {
|
|
|
22732
22763
|
const looksLikeHTML = expr.includes("<") || /['"]\s*\+/.test(expr) || /\+\s*['"]/.test(expr) || /f['"][^'"]*\{/.test(expr);
|
|
22733
22764
|
if (!looksLikeHTML)
|
|
22734
22765
|
continue;
|
|
22766
|
+
if (isSafeJinjaReturnExpr(expr))
|
|
22767
|
+
continue;
|
|
22735
22768
|
sinks.push({ sinkLine: i2 + 1 });
|
|
22736
22769
|
}
|
|
22737
22770
|
return sinks;
|
|
@@ -23594,7 +23627,7 @@ class SinkFilterPass {
|
|
|
23594
23627
|
if (["javascript", "typescript"].includes(language)) {
|
|
23595
23628
|
const sourceLines = ctx.code.split(`
|
|
23596
23629
|
`);
|
|
23597
|
-
const guardPatterns = /\b(?:includes|startsWith|endsWith|indexOf|test|match)\s*\(/;
|
|
23630
|
+
const guardPatterns = /\b(?:includes|startsWith|endsWith|indexOf|test|match|has)\s*\(/;
|
|
23598
23631
|
filtered = filtered.filter((sink) => {
|
|
23599
23632
|
if (sink.type !== "open_redirect" && sink.type !== "crlf") {
|
|
23600
23633
|
return true;
|
|
@@ -23614,6 +23647,9 @@ class SinkFilterPass {
|
|
|
23614
23647
|
if (setHeaderMatch) {
|
|
23615
23648
|
return false;
|
|
23616
23649
|
}
|
|
23650
|
+
if (sink.method === "cookie" && sink.type === "crlf") {
|
|
23651
|
+
return false;
|
|
23652
|
+
}
|
|
23617
23653
|
return true;
|
|
23618
23654
|
});
|
|
23619
23655
|
}
|
|
@@ -31655,15 +31691,34 @@ function isExceptionExpression(expr) {
|
|
|
31655
31691
|
if (!expr)
|
|
31656
31692
|
return false;
|
|
31657
31693
|
const e = expr.trim();
|
|
31658
|
-
return /\b(err|error|exc|exception|e|t|throwable)\.(stack|
|
|
31694
|
+
return /\b(err|error|exc|exception|e|t|throwable)\.(stack|toString\(|getStackTrace\(|getLocalizedMessage\(|getCause\()/i.test(e) || /\btraceback\.(format_exc|format_exception|print_exc)\b/i.test(e) || /\bdebug\.Stack\(\)/.test(e) || /\bstr\(\s*(err|error|exc|exception|e)\s*\)/i.test(e) || /\bString\(\s*(err|error|exc|exception|e)\s*\)/i.test(e);
|
|
31695
|
+
}
|
|
31696
|
+
var PY_FILE_HANDLE_OPEN_RE = /^\s*(?:with\s+open\s*\([^)]*\)\s+as\s+(\w+)\s*:|(\w+)\s*=\s*open\s*\()/;
|
|
31697
|
+
function isPythonFileHandle(receiver, callLine, sourceLines) {
|
|
31698
|
+
if (!receiver || !/^[A-Za-z_]\w*$/.test(receiver))
|
|
31699
|
+
return false;
|
|
31700
|
+
const start2 = Math.max(0, callLine - 11);
|
|
31701
|
+
for (let i2 = callLine - 2;i2 >= start2; i2--) {
|
|
31702
|
+
const ln = sourceLines[i2] ?? "";
|
|
31703
|
+
const m = ln.match(PY_FILE_HANDLE_OPEN_RE);
|
|
31704
|
+
if (!m)
|
|
31705
|
+
continue;
|
|
31706
|
+
const name2 = m[1] ?? m[2];
|
|
31707
|
+
if (name2 === receiver)
|
|
31708
|
+
return true;
|
|
31709
|
+
}
|
|
31710
|
+
return false;
|
|
31659
31711
|
}
|
|
31660
31712
|
function argIsException(arg) {
|
|
31661
31713
|
if (!arg)
|
|
31662
31714
|
return false;
|
|
31715
|
+
const expr = (arg.expression ?? "").trim();
|
|
31663
31716
|
if (arg.variable && /^(err|error|exc|exception|e|t|throwable)$/i.test(arg.variable)) {
|
|
31664
|
-
|
|
31717
|
+
if (!expr || expr === arg.variable)
|
|
31718
|
+
return true;
|
|
31719
|
+
return isExceptionExpression(expr);
|
|
31665
31720
|
}
|
|
31666
|
-
return isExceptionExpression(
|
|
31721
|
+
return isExceptionExpression(expr);
|
|
31667
31722
|
}
|
|
31668
31723
|
function detectJavaPrintStackTrace(call) {
|
|
31669
31724
|
if (call.method_name !== "printStackTrace")
|
|
@@ -31680,13 +31735,16 @@ function detectJavaPrintStackTrace(call) {
|
|
|
31680
31735
|
}
|
|
31681
31736
|
return null;
|
|
31682
31737
|
}
|
|
31683
|
-
function detectResponseLeakCall(call) {
|
|
31738
|
+
function detectResponseLeakCall(call, language, sourceLines) {
|
|
31684
31739
|
const method = call.method_name ?? "";
|
|
31685
31740
|
const receiver = call.receiver ?? "";
|
|
31686
31741
|
if (!RESPONSE_SEND_METHODS.has(method))
|
|
31687
31742
|
return null;
|
|
31688
31743
|
if (LOGGER_RECEIVER_RE.test(receiver))
|
|
31689
31744
|
return null;
|
|
31745
|
+
if (language === "python" && (method === "write" || method === "writelines") && sourceLines && isPythonFileHandle(receiver, call.location.line, sourceLines)) {
|
|
31746
|
+
return null;
|
|
31747
|
+
}
|
|
31690
31748
|
const recTail = receiver.split(".").pop() ?? receiver;
|
|
31691
31749
|
const recHead = receiver.split(".")[0] ?? receiver;
|
|
31692
31750
|
if (!RESPONSE_RECEIVER_RE.test(recTail) && !RESPONSE_RECEIVER_RE.test(recHead)) {
|
|
@@ -31731,6 +31789,8 @@ class InfoDisclosureStacktracePass {
|
|
|
31731
31789
|
const { graph, language } = ctx;
|
|
31732
31790
|
const file = graph.ir.meta.file;
|
|
31733
31791
|
const findings = [];
|
|
31792
|
+
const sourceLines = ctx.code.split(`
|
|
31793
|
+
`);
|
|
31734
31794
|
if (language === "python") {
|
|
31735
31795
|
for (const f of detectPythonTracebackReturn(ctx)) {
|
|
31736
31796
|
findings.push({ line: f.line, api: f.api, language });
|
|
@@ -31742,9 +31802,9 @@ class InfoDisclosureStacktracePass {
|
|
|
31742
31802
|
if (language === "java") {
|
|
31743
31803
|
api = detectJavaPrintStackTrace(call);
|
|
31744
31804
|
if (!api)
|
|
31745
|
-
api = detectResponseLeakCall(call);
|
|
31805
|
+
api = detectResponseLeakCall(call, language, sourceLines);
|
|
31746
31806
|
} else if (language === "javascript" || language === "typescript") {
|
|
31747
|
-
api = detectResponseLeakCall(call);
|
|
31807
|
+
api = detectResponseLeakCall(call, language, sourceLines);
|
|
31748
31808
|
} else if (language === "go") {
|
|
31749
31809
|
const method = call.method_name ?? "";
|
|
31750
31810
|
const rec = call.receiver ?? "";
|
|
@@ -31765,10 +31825,10 @@ class InfoDisclosureStacktracePass {
|
|
|
31765
31825
|
}
|
|
31766
31826
|
}
|
|
31767
31827
|
} else {
|
|
31768
|
-
api = detectResponseLeakCall(call);
|
|
31828
|
+
api = detectResponseLeakCall(call, language, sourceLines);
|
|
31769
31829
|
}
|
|
31770
31830
|
} else if (language === "python") {
|
|
31771
|
-
api = detectResponseLeakCall(call);
|
|
31831
|
+
api = detectResponseLeakCall(call, language, sourceLines);
|
|
31772
31832
|
}
|
|
31773
31833
|
if (!api)
|
|
31774
31834
|
continue;
|
|
@@ -34708,7 +34768,7 @@ var colors = {
|
|
|
34708
34768
|
};
|
|
34709
34769
|
|
|
34710
34770
|
// src/version.ts
|
|
34711
|
-
var version = "3.
|
|
34771
|
+
var version = "3.99.0";
|
|
34712
34772
|
|
|
34713
34773
|
// src/formatters.ts
|
|
34714
34774
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.99.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.99.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|