cognium-dev 3.98.0 → 3.100.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 +145 -10
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -23490,6 +23490,75 @@ var JS_XSS_SANITIZERS = [
|
|
|
23490
23490
|
/\btrustAsHtml\s*\(/,
|
|
23491
23491
|
/\bbypassSecurityTrust/
|
|
23492
23492
|
];
|
|
23493
|
+
var DATA_PARSER_TYPES = new Set([
|
|
23494
|
+
"Parser",
|
|
23495
|
+
"CommandLine",
|
|
23496
|
+
"JCommander",
|
|
23497
|
+
"DateParser",
|
|
23498
|
+
"FastDateFormat",
|
|
23499
|
+
"FastDatePrinter",
|
|
23500
|
+
"ResultParser",
|
|
23501
|
+
"DateFormat",
|
|
23502
|
+
"SimpleDateFormat",
|
|
23503
|
+
"NumberFormat",
|
|
23504
|
+
"DecimalFormat",
|
|
23505
|
+
"OptionParser",
|
|
23506
|
+
"CmdLineParser"
|
|
23507
|
+
]);
|
|
23508
|
+
var COMPILED_TEMPLATE_TYPES = new Set([
|
|
23509
|
+
"Template",
|
|
23510
|
+
"JetTemplate",
|
|
23511
|
+
"ITemplate",
|
|
23512
|
+
"VelocityTemplate",
|
|
23513
|
+
"BeetlTemplate"
|
|
23514
|
+
]);
|
|
23515
|
+
var REFLECTION_LITERAL_METHODS = new Set([
|
|
23516
|
+
"forName",
|
|
23517
|
+
"loadClass",
|
|
23518
|
+
"getMethod",
|
|
23519
|
+
"getDeclaredMethod",
|
|
23520
|
+
"getConstructor",
|
|
23521
|
+
"getDeclaredConstructor",
|
|
23522
|
+
"parseExpression",
|
|
23523
|
+
"invoke"
|
|
23524
|
+
]);
|
|
23525
|
+
var JAVA_DECL_RE_TEMPLATE = "(?:\\b(?:final|public|private|protected|static)\\s+)*" + "([A-Z]\\w*(?:\\.[A-Z]\\w*)?(?:<[^>]*>)?)\\s+RECV\\b\\s*[=;,)]";
|
|
23526
|
+
function resolveJavaReceiverType(receiver, sinkLine, sourceLines) {
|
|
23527
|
+
if (!receiver || !/^[A-Za-z_]\w*$/.test(receiver))
|
|
23528
|
+
return null;
|
|
23529
|
+
const start2 = Math.max(0, sinkLine - 31);
|
|
23530
|
+
const declRe = new RegExp(JAVA_DECL_RE_TEMPLATE.replace("RECV", receiver));
|
|
23531
|
+
for (let i2 = sinkLine - 2;i2 >= start2; i2--) {
|
|
23532
|
+
const ln = sourceLines[i2] ?? "";
|
|
23533
|
+
const m = ln.match(declRe);
|
|
23534
|
+
if (m) {
|
|
23535
|
+
const raw = m[1].replace(/<[^>]*>/g, "").trim();
|
|
23536
|
+
const parts2 = raw.split(".");
|
|
23537
|
+
return parts2[parts2.length - 1] ?? null;
|
|
23538
|
+
}
|
|
23539
|
+
}
|
|
23540
|
+
return null;
|
|
23541
|
+
}
|
|
23542
|
+
function isJavaLiteralOrAnnotationAccessor(expr) {
|
|
23543
|
+
const e = expr.trim();
|
|
23544
|
+
if (e === "")
|
|
23545
|
+
return true;
|
|
23546
|
+
if (/^"(?:[^"\\]|\\.)*"$/.test(e))
|
|
23547
|
+
return true;
|
|
23548
|
+
if (/^[A-Za-z_]\w*\s*\.\s*(?:value|name|key)\s*\(\s*\)$/.test(e))
|
|
23549
|
+
return true;
|
|
23550
|
+
return false;
|
|
23551
|
+
}
|
|
23552
|
+
function extractJavaCallArgs(method, line) {
|
|
23553
|
+
const re = new RegExp(`\\b${method}\\s*\\(([^()]*(?:\\([^()]*\\)[^()]*)*)\\)`);
|
|
23554
|
+
const m = line.match(re);
|
|
23555
|
+
if (!m)
|
|
23556
|
+
return null;
|
|
23557
|
+
const argsText = m[1].trim();
|
|
23558
|
+
if (argsText === "")
|
|
23559
|
+
return [];
|
|
23560
|
+
return argsText.split(",").map((s) => s.trim());
|
|
23561
|
+
}
|
|
23493
23562
|
|
|
23494
23563
|
class SinkFilterPass {
|
|
23495
23564
|
name = "sink-filter";
|
|
@@ -23627,7 +23696,7 @@ class SinkFilterPass {
|
|
|
23627
23696
|
if (["javascript", "typescript"].includes(language)) {
|
|
23628
23697
|
const sourceLines = ctx.code.split(`
|
|
23629
23698
|
`);
|
|
23630
|
-
const guardPatterns = /\b(?:includes|startsWith|endsWith|indexOf|test|match)\s*\(/;
|
|
23699
|
+
const guardPatterns = /\b(?:includes|startsWith|endsWith|indexOf|test|match|has)\s*\(/;
|
|
23631
23700
|
filtered = filtered.filter((sink) => {
|
|
23632
23701
|
if (sink.type !== "open_redirect" && sink.type !== "crlf") {
|
|
23633
23702
|
return true;
|
|
@@ -23647,6 +23716,48 @@ class SinkFilterPass {
|
|
|
23647
23716
|
if (setHeaderMatch) {
|
|
23648
23717
|
return false;
|
|
23649
23718
|
}
|
|
23719
|
+
if (sink.method === "cookie" && sink.type === "crlf") {
|
|
23720
|
+
return false;
|
|
23721
|
+
}
|
|
23722
|
+
return true;
|
|
23723
|
+
});
|
|
23724
|
+
}
|
|
23725
|
+
if (language === "java") {
|
|
23726
|
+
const sourceLines = ctx.code.split(`
|
|
23727
|
+
`);
|
|
23728
|
+
filtered = filtered.filter((sink) => {
|
|
23729
|
+
if (sink.type !== "code_injection")
|
|
23730
|
+
return true;
|
|
23731
|
+
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
23732
|
+
const receiverMatch = sinkLineText.match(/\b(\w+)\s*\.\s*(\w+)\s*\(/);
|
|
23733
|
+
const receiver = receiverMatch?.[1];
|
|
23734
|
+
const method = sink.method ?? receiverMatch?.[2];
|
|
23735
|
+
if (method === "parse" && receiver) {
|
|
23736
|
+
const recvType = resolveJavaReceiverType(receiver, sink.line, sourceLines);
|
|
23737
|
+
if (recvType && DATA_PARSER_TYPES.has(recvType))
|
|
23738
|
+
return false;
|
|
23739
|
+
}
|
|
23740
|
+
if ((method === "render" || method === "process" || method === "merge" || method === "renderTo") && receiver) {
|
|
23741
|
+
const recvType = resolveJavaReceiverType(receiver, sink.line, sourceLines);
|
|
23742
|
+
if (recvType && COMPILED_TEMPLATE_TYPES.has(recvType))
|
|
23743
|
+
return false;
|
|
23744
|
+
}
|
|
23745
|
+
if (method && REFLECTION_LITERAL_METHODS.has(method)) {
|
|
23746
|
+
const args2 = extractJavaCallArgs(method, sinkLineText);
|
|
23747
|
+
if (args2 !== null) {
|
|
23748
|
+
if (method === "invoke") {
|
|
23749
|
+
if (args2.length <= 1)
|
|
23750
|
+
return false;
|
|
23751
|
+
} else {
|
|
23752
|
+
const firstArg = args2[0] ?? "";
|
|
23753
|
+
if (isJavaLiteralOrAnnotationAccessor(firstArg))
|
|
23754
|
+
return false;
|
|
23755
|
+
}
|
|
23756
|
+
}
|
|
23757
|
+
}
|
|
23758
|
+
if (method === "newInstance" && /\.\s*newInstance\s*\(\s*\)/.test(sinkLineText)) {
|
|
23759
|
+
return false;
|
|
23760
|
+
}
|
|
23650
23761
|
return true;
|
|
23651
23762
|
});
|
|
23652
23763
|
}
|
|
@@ -31688,15 +31799,34 @@ function isExceptionExpression(expr) {
|
|
|
31688
31799
|
if (!expr)
|
|
31689
31800
|
return false;
|
|
31690
31801
|
const e = expr.trim();
|
|
31691
|
-
return /\b(err|error|exc|exception|e|t|throwable)\.(stack|
|
|
31802
|
+
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);
|
|
31803
|
+
}
|
|
31804
|
+
var PY_FILE_HANDLE_OPEN_RE = /^\s*(?:with\s+open\s*\([^)]*\)\s+as\s+(\w+)\s*:|(\w+)\s*=\s*open\s*\()/;
|
|
31805
|
+
function isPythonFileHandle(receiver, callLine, sourceLines) {
|
|
31806
|
+
if (!receiver || !/^[A-Za-z_]\w*$/.test(receiver))
|
|
31807
|
+
return false;
|
|
31808
|
+
const start2 = Math.max(0, callLine - 11);
|
|
31809
|
+
for (let i2 = callLine - 2;i2 >= start2; i2--) {
|
|
31810
|
+
const ln = sourceLines[i2] ?? "";
|
|
31811
|
+
const m = ln.match(PY_FILE_HANDLE_OPEN_RE);
|
|
31812
|
+
if (!m)
|
|
31813
|
+
continue;
|
|
31814
|
+
const name2 = m[1] ?? m[2];
|
|
31815
|
+
if (name2 === receiver)
|
|
31816
|
+
return true;
|
|
31817
|
+
}
|
|
31818
|
+
return false;
|
|
31692
31819
|
}
|
|
31693
31820
|
function argIsException(arg) {
|
|
31694
31821
|
if (!arg)
|
|
31695
31822
|
return false;
|
|
31823
|
+
const expr = (arg.expression ?? "").trim();
|
|
31696
31824
|
if (arg.variable && /^(err|error|exc|exception|e|t|throwable)$/i.test(arg.variable)) {
|
|
31697
|
-
|
|
31825
|
+
if (!expr || expr === arg.variable)
|
|
31826
|
+
return true;
|
|
31827
|
+
return isExceptionExpression(expr);
|
|
31698
31828
|
}
|
|
31699
|
-
return isExceptionExpression(
|
|
31829
|
+
return isExceptionExpression(expr);
|
|
31700
31830
|
}
|
|
31701
31831
|
function detectJavaPrintStackTrace(call) {
|
|
31702
31832
|
if (call.method_name !== "printStackTrace")
|
|
@@ -31713,13 +31843,16 @@ function detectJavaPrintStackTrace(call) {
|
|
|
31713
31843
|
}
|
|
31714
31844
|
return null;
|
|
31715
31845
|
}
|
|
31716
|
-
function detectResponseLeakCall(call) {
|
|
31846
|
+
function detectResponseLeakCall(call, language, sourceLines) {
|
|
31717
31847
|
const method = call.method_name ?? "";
|
|
31718
31848
|
const receiver = call.receiver ?? "";
|
|
31719
31849
|
if (!RESPONSE_SEND_METHODS.has(method))
|
|
31720
31850
|
return null;
|
|
31721
31851
|
if (LOGGER_RECEIVER_RE.test(receiver))
|
|
31722
31852
|
return null;
|
|
31853
|
+
if (language === "python" && (method === "write" || method === "writelines") && sourceLines && isPythonFileHandle(receiver, call.location.line, sourceLines)) {
|
|
31854
|
+
return null;
|
|
31855
|
+
}
|
|
31723
31856
|
const recTail = receiver.split(".").pop() ?? receiver;
|
|
31724
31857
|
const recHead = receiver.split(".")[0] ?? receiver;
|
|
31725
31858
|
if (!RESPONSE_RECEIVER_RE.test(recTail) && !RESPONSE_RECEIVER_RE.test(recHead)) {
|
|
@@ -31764,6 +31897,8 @@ class InfoDisclosureStacktracePass {
|
|
|
31764
31897
|
const { graph, language } = ctx;
|
|
31765
31898
|
const file = graph.ir.meta.file;
|
|
31766
31899
|
const findings = [];
|
|
31900
|
+
const sourceLines = ctx.code.split(`
|
|
31901
|
+
`);
|
|
31767
31902
|
if (language === "python") {
|
|
31768
31903
|
for (const f of detectPythonTracebackReturn(ctx)) {
|
|
31769
31904
|
findings.push({ line: f.line, api: f.api, language });
|
|
@@ -31775,9 +31910,9 @@ class InfoDisclosureStacktracePass {
|
|
|
31775
31910
|
if (language === "java") {
|
|
31776
31911
|
api = detectJavaPrintStackTrace(call);
|
|
31777
31912
|
if (!api)
|
|
31778
|
-
api = detectResponseLeakCall(call);
|
|
31913
|
+
api = detectResponseLeakCall(call, language, sourceLines);
|
|
31779
31914
|
} else if (language === "javascript" || language === "typescript") {
|
|
31780
|
-
api = detectResponseLeakCall(call);
|
|
31915
|
+
api = detectResponseLeakCall(call, language, sourceLines);
|
|
31781
31916
|
} else if (language === "go") {
|
|
31782
31917
|
const method = call.method_name ?? "";
|
|
31783
31918
|
const rec = call.receiver ?? "";
|
|
@@ -31798,10 +31933,10 @@ class InfoDisclosureStacktracePass {
|
|
|
31798
31933
|
}
|
|
31799
31934
|
}
|
|
31800
31935
|
} else {
|
|
31801
|
-
api = detectResponseLeakCall(call);
|
|
31936
|
+
api = detectResponseLeakCall(call, language, sourceLines);
|
|
31802
31937
|
}
|
|
31803
31938
|
} else if (language === "python") {
|
|
31804
|
-
api = detectResponseLeakCall(call);
|
|
31939
|
+
api = detectResponseLeakCall(call, language, sourceLines);
|
|
31805
31940
|
}
|
|
31806
31941
|
if (!api)
|
|
31807
31942
|
continue;
|
|
@@ -34741,7 +34876,7 @@ var colors = {
|
|
|
34741
34876
|
};
|
|
34742
34877
|
|
|
34743
34878
|
// src/version.ts
|
|
34744
|
-
var version = "3.
|
|
34879
|
+
var version = "3.100.0";
|
|
34745
34880
|
|
|
34746
34881
|
// src/formatters.ts
|
|
34747
34882
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.100.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.100.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|