cognium-dev 3.125.0 → 3.128.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 +245 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -22607,6 +22607,7 @@ class LanguageSourcesPass {
|
|
|
22607
22607
|
additionalSanitizers.push(...findPythonRegexAllowlistWrapperSanitizers(code));
|
|
22608
22608
|
additionalSanitizers.push(...findPythonSetMembershipXssGuardSanitizers(code));
|
|
22609
22609
|
additionalSanitizers.push(...findPythonDefusedXmlSanitizers(code));
|
|
22610
|
+
additionalSanitizers.push(...findPythonJinjaAutoescapeSanitizers(code));
|
|
22610
22611
|
const pyMisconfigFindings = findPythonPatternFindings(code, graph.ir.meta.file);
|
|
22611
22612
|
for (const finding of pyMisconfigFindings) {
|
|
22612
22613
|
ctx.addFinding(finding);
|
|
@@ -22615,6 +22616,7 @@ class LanguageSourcesPass {
|
|
|
22615
22616
|
if (language === "rust") {
|
|
22616
22617
|
additionalSanitizers.push(...findRustSetAllowlistGuardSanitizers(code));
|
|
22617
22618
|
additionalSanitizers.push(...findRustCanonicalizeGuardSanitizers(code));
|
|
22619
|
+
additionalSanitizers.push(...findRustArgvCommandSanitizers(code));
|
|
22618
22620
|
const rustMisconfigFindings = findRustPatternFindings(code, graph.ir.meta.file);
|
|
22619
22621
|
for (const finding of rustMisconfigFindings) {
|
|
22620
22622
|
ctx.addFinding(finding);
|
|
@@ -22625,9 +22627,13 @@ class LanguageSourcesPass {
|
|
|
22625
22627
|
additionalSanitizers.push(...findJsCryptoHashSanitizers(code));
|
|
22626
22628
|
additionalSanitizers.push(...findJsCsvFormulaPrefixSanitizers(code));
|
|
22627
22629
|
additionalSanitizers.push(...findJsWrapperFunctionSanitizers(code));
|
|
22630
|
+
additionalSanitizers.push(...findJsSsrfAllowlistGuardSanitizers(code));
|
|
22628
22631
|
}
|
|
22629
22632
|
if (language === "java") {
|
|
22630
22633
|
additionalSanitizers.push(...findJavaSafeJsonParseSanitizers(code));
|
|
22634
|
+
additionalSanitizers.push(...findJavaPathNormalizeStartsWithGuardSanitizers(code));
|
|
22635
|
+
additionalSanitizers.push(...findJavaInlineCrlfStripLogSanitizers(code));
|
|
22636
|
+
additionalSanitizers.push(...findJavaArgvFormExecSanitizers(code));
|
|
22631
22637
|
}
|
|
22632
22638
|
if (language === "python" || language === "javascript" || language === "typescript" || language === "go") {
|
|
22633
22639
|
const exfilFindings = findExternalSecretExfiltrationFindings(code, graph.ir.meta.file, language);
|
|
@@ -24487,6 +24493,244 @@ function findJsWrapperFunctionSanitizers(code) {
|
|
|
24487
24493
|
}
|
|
24488
24494
|
return sanitizers;
|
|
24489
24495
|
}
|
|
24496
|
+
function findJsSsrfAllowlistGuardSanitizers(code) {
|
|
24497
|
+
const sanitizers = [];
|
|
24498
|
+
const lines = code.split(`
|
|
24499
|
+
`);
|
|
24500
|
+
const urlAliasToHostAlias = new Map;
|
|
24501
|
+
const urlAliasDecl = /\b(?:const|let|var)\s+([A-Za-z_]\w*)\s*=\s*new\s+URL\s*\(\s*([A-Za-z_][\w.[\]'"`]*)\s*\)/;
|
|
24502
|
+
const hostAliasDecl = /\b(?:const|let|var)\s+([A-Za-z_]\w*)\s*=\s*([A-Za-z_]\w*)\.(?:hostname|host)\b/;
|
|
24503
|
+
const hostFromUrl = new Map;
|
|
24504
|
+
for (const line of lines) {
|
|
24505
|
+
const ua = urlAliasDecl.exec(line);
|
|
24506
|
+
if (ua)
|
|
24507
|
+
urlAliasToHostAlias.set(ua[1], ua[2]);
|
|
24508
|
+
const ha = hostAliasDecl.exec(line);
|
|
24509
|
+
if (ha)
|
|
24510
|
+
hostFromUrl.set(ha[1], ha[2]);
|
|
24511
|
+
}
|
|
24512
|
+
const allowlistName = /^(?:[A-Z][A-Z0-9_]+|.*?(allowed|accepted|whitelist|permitted|valid|approved).*)$/i;
|
|
24513
|
+
const guardHas = /if\s*\(\s*!\s*([A-Za-z_]\w*)\s*\.\s*(?:has|includes)\s*\(\s*([A-Za-z_]\w*)(?:\s*\.\s*(?:hostname|host))?\s*\)\s*\)/;
|
|
24514
|
+
const guardIndexOf = /if\s*\(\s*([A-Za-z_]\w*)\s*\.\s*indexOf\s*\(\s*([A-Za-z_]\w*)(?:\s*\.\s*(?:hostname|host))?\s*\)\s*<\s*0\s*\)/;
|
|
24515
|
+
const terminator = /\b(return|throw|res\s*\.\s*status\s*\([^)]*\)\s*\.\s*(?:send|end|json)\s*\()/;
|
|
24516
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
24517
|
+
const line = lines[i2];
|
|
24518
|
+
let m = guardHas.exec(line);
|
|
24519
|
+
let allow = null;
|
|
24520
|
+
let guardedVar = null;
|
|
24521
|
+
if (m) {
|
|
24522
|
+
allow = m[1];
|
|
24523
|
+
guardedVar = m[2];
|
|
24524
|
+
} else {
|
|
24525
|
+
m = guardIndexOf.exec(line);
|
|
24526
|
+
if (m) {
|
|
24527
|
+
allow = m[1];
|
|
24528
|
+
guardedVar = m[2];
|
|
24529
|
+
}
|
|
24530
|
+
}
|
|
24531
|
+
if (!allow || !guardedVar)
|
|
24532
|
+
continue;
|
|
24533
|
+
if (!allowlistName.test(allow))
|
|
24534
|
+
continue;
|
|
24535
|
+
let bodyHasTerminator = false;
|
|
24536
|
+
let blockEnd = -1;
|
|
24537
|
+
const maxScan = Math.min(lines.length, i2 + 26);
|
|
24538
|
+
let braceDepth = 0;
|
|
24539
|
+
let started = false;
|
|
24540
|
+
for (let j = i2;j < maxScan; j++) {
|
|
24541
|
+
const ln = lines[j];
|
|
24542
|
+
for (const ch of ln) {
|
|
24543
|
+
if (ch === "{") {
|
|
24544
|
+
braceDepth++;
|
|
24545
|
+
started = true;
|
|
24546
|
+
} else if (ch === "}") {
|
|
24547
|
+
braceDepth--;
|
|
24548
|
+
if (started && braceDepth === 0) {
|
|
24549
|
+
blockEnd = j;
|
|
24550
|
+
break;
|
|
24551
|
+
}
|
|
24552
|
+
}
|
|
24553
|
+
}
|
|
24554
|
+
if (started && j > i2 && terminator.test(ln))
|
|
24555
|
+
bodyHasTerminator = true;
|
|
24556
|
+
if (blockEnd !== -1)
|
|
24557
|
+
break;
|
|
24558
|
+
}
|
|
24559
|
+
if (!started)
|
|
24560
|
+
continue;
|
|
24561
|
+
if (blockEnd === -1)
|
|
24562
|
+
continue;
|
|
24563
|
+
if (!bodyHasTerminator)
|
|
24564
|
+
continue;
|
|
24565
|
+
const safeNames = new Set([guardedVar]);
|
|
24566
|
+
if (hostFromUrl.has(guardedVar)) {
|
|
24567
|
+
safeNames.add(hostFromUrl.get(guardedVar));
|
|
24568
|
+
}
|
|
24569
|
+
for (const [hostName, urlName] of hostFromUrl) {
|
|
24570
|
+
if (urlName === guardedVar)
|
|
24571
|
+
safeNames.add(hostName);
|
|
24572
|
+
}
|
|
24573
|
+
const refRes = [...safeNames].map((n) => new RegExp(`\\b${n}\\b`));
|
|
24574
|
+
for (let l = blockEnd + 2;l <= lines.length; l++) {
|
|
24575
|
+
const lineText = lines[l - 1];
|
|
24576
|
+
if (!refRes.some((re) => re.test(lineText)))
|
|
24577
|
+
continue;
|
|
24578
|
+
sanitizers.push({
|
|
24579
|
+
type: "js_ssrf_allowlist_guard",
|
|
24580
|
+
method: "if",
|
|
24581
|
+
line: l,
|
|
24582
|
+
sanitizes: ["ssrf", "external_taint_escape"]
|
|
24583
|
+
});
|
|
24584
|
+
}
|
|
24585
|
+
}
|
|
24586
|
+
return sanitizers;
|
|
24587
|
+
}
|
|
24588
|
+
function findJavaPathNormalizeStartsWithGuardSanitizers(code) {
|
|
24589
|
+
const sanitizers = [];
|
|
24590
|
+
const lines = code.split(`
|
|
24591
|
+
`);
|
|
24592
|
+
const resolveNormalizeRe = /\b([A-Za-z_]\w*)\s*=\s*([A-Za-z_]\w*)\s*\.\s*resolve\s*\([^)]*\)\s*\.\s*normalize\s*\(\s*\)/;
|
|
24593
|
+
const startsWithGuardRe = (varName, rootName) => new RegExp(`if\\s*\\(\\s*!\\s*${varName}\\s*\\.\\s*startsWith\\s*\\(\\s*${rootName}\\s*\\)\\s*\\)`);
|
|
24594
|
+
const terminatorRe = /\b(throw|return)\b/;
|
|
24595
|
+
const candidates = [];
|
|
24596
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
24597
|
+
const m = resolveNormalizeRe.exec(lines[i2]);
|
|
24598
|
+
if (!m)
|
|
24599
|
+
continue;
|
|
24600
|
+
candidates.push({ line: i2 + 1, fullVar: m[1], rootVar: m[2] });
|
|
24601
|
+
}
|
|
24602
|
+
if (candidates.length === 0)
|
|
24603
|
+
return sanitizers;
|
|
24604
|
+
for (const c of candidates) {
|
|
24605
|
+
const guardRe = startsWithGuardRe(c.fullVar, c.rootVar);
|
|
24606
|
+
let guardLine = -1;
|
|
24607
|
+
for (let l = c.line;l < Math.min(lines.length, c.line + 6); l++) {
|
|
24608
|
+
if (!guardRe.test(lines[l]))
|
|
24609
|
+
continue;
|
|
24610
|
+
if (terminatorRe.test(lines[l]) || l + 1 < lines.length && terminatorRe.test(lines[l + 1])) {
|
|
24611
|
+
guardLine = l + 1;
|
|
24612
|
+
break;
|
|
24613
|
+
}
|
|
24614
|
+
}
|
|
24615
|
+
if (guardLine < 0)
|
|
24616
|
+
continue;
|
|
24617
|
+
const varRefRe = new RegExp(`\\b${c.fullVar}\\b`);
|
|
24618
|
+
sanitizers.push({
|
|
24619
|
+
type: "java_path_normalize_startswith_guard",
|
|
24620
|
+
method: "normalize",
|
|
24621
|
+
line: c.line,
|
|
24622
|
+
sanitizes: ["path_traversal", "external_taint_escape"]
|
|
24623
|
+
});
|
|
24624
|
+
for (let l = c.line;l < lines.length; l++) {
|
|
24625
|
+
if (!varRefRe.test(lines[l]))
|
|
24626
|
+
continue;
|
|
24627
|
+
sanitizers.push({
|
|
24628
|
+
type: "java_path_normalize_startswith_guard",
|
|
24629
|
+
method: "normalize",
|
|
24630
|
+
line: l + 1,
|
|
24631
|
+
sanitizes: ["path_traversal", "external_taint_escape"]
|
|
24632
|
+
});
|
|
24633
|
+
}
|
|
24634
|
+
}
|
|
24635
|
+
return sanitizers;
|
|
24636
|
+
}
|
|
24637
|
+
function findJavaInlineCrlfStripLogSanitizers(code) {
|
|
24638
|
+
const sanitizers = [];
|
|
24639
|
+
const lines = code.split(`
|
|
24640
|
+
`);
|
|
24641
|
+
const logCallStart = /\b(?:log|logger|LOG|LOGGER|slog|LOGGER_)\s*\.\s*(?:info|warn|error|debug|trace|fatal|severe|warning|fine|finer|finest|config)\s*\(/;
|
|
24642
|
+
const crlfReplaceAll = /\.\s*replaceAll\s*\(\s*"\[[^"]*\\\\?[rnt][^"]*\]"/;
|
|
24643
|
+
const crlfReplaceChar = /\.\s*replace\s*\(\s*'(?:\\\\?[rnt])'\s*,/;
|
|
24644
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
24645
|
+
const text = lines[i2];
|
|
24646
|
+
if (!logCallStart.test(text))
|
|
24647
|
+
continue;
|
|
24648
|
+
if (!crlfReplaceAll.test(text) && !crlfReplaceChar.test(text))
|
|
24649
|
+
continue;
|
|
24650
|
+
sanitizers.push({
|
|
24651
|
+
type: "java_inline_crlf_strip_log",
|
|
24652
|
+
method: "replaceAll",
|
|
24653
|
+
line: i2 + 1,
|
|
24654
|
+
sanitizes: ["log_injection", "external_taint_escape"]
|
|
24655
|
+
});
|
|
24656
|
+
}
|
|
24657
|
+
return sanitizers;
|
|
24658
|
+
}
|
|
24659
|
+
function findJavaArgvFormExecSanitizers(code) {
|
|
24660
|
+
const sanitizers = [];
|
|
24661
|
+
const lines = code.split(`
|
|
24662
|
+
`);
|
|
24663
|
+
const argvExecRe = /\.\s*exec\s*\(\s*new\s+String\s*\[\s*\]\s*\{/;
|
|
24664
|
+
const argvPbRe = /\bnew\s+ProcessBuilder\s*\(\s*new\s+String\s*\[\s*\]\s*\{/;
|
|
24665
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
24666
|
+
const text = lines[i2];
|
|
24667
|
+
if (!argvExecRe.test(text) && !argvPbRe.test(text))
|
|
24668
|
+
continue;
|
|
24669
|
+
sanitizers.push({
|
|
24670
|
+
type: "java_argv_form_exec",
|
|
24671
|
+
method: "exec",
|
|
24672
|
+
line: i2 + 1,
|
|
24673
|
+
sanitizes: ["command_injection", "external_taint_escape"]
|
|
24674
|
+
});
|
|
24675
|
+
}
|
|
24676
|
+
return sanitizers;
|
|
24677
|
+
}
|
|
24678
|
+
function findRustArgvCommandSanitizers(code) {
|
|
24679
|
+
const sanitizers = [];
|
|
24680
|
+
const lines = code.split(`
|
|
24681
|
+
`);
|
|
24682
|
+
const argvCommandRe = /\bCommand\s*::\s*new\s*\(\s*"[^"]*"\s*\)\s*\.\s*arg\s*\(/;
|
|
24683
|
+
const shellArgvRe = /\bCommand\s*::\s*new\s*\(\s*"(?:sh|bash|zsh|ksh|dash|cmd(?:\.exe)?|powershell|pwsh)"\s*\)\s*\.\s*arg\s*\(\s*"-c"/;
|
|
24684
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
24685
|
+
const text = lines[i2];
|
|
24686
|
+
if (!argvCommandRe.test(text))
|
|
24687
|
+
continue;
|
|
24688
|
+
if (shellArgvRe.test(text))
|
|
24689
|
+
continue;
|
|
24690
|
+
sanitizers.push({
|
|
24691
|
+
type: "rust_argv_command",
|
|
24692
|
+
method: "arg",
|
|
24693
|
+
line: i2 + 1,
|
|
24694
|
+
sanitizes: ["command_injection", "external_taint_escape"]
|
|
24695
|
+
});
|
|
24696
|
+
}
|
|
24697
|
+
return sanitizers;
|
|
24698
|
+
}
|
|
24699
|
+
function findPythonJinjaAutoescapeSanitizers(code) {
|
|
24700
|
+
const sanitizers = [];
|
|
24701
|
+
const lines = code.split(`
|
|
24702
|
+
`);
|
|
24703
|
+
const envAssignRe = /\b([A-Za-z_]\w*)\s*=\s*Environment\s*\(/;
|
|
24704
|
+
const autoescapeOnRe = /\bautoescape\s*=/;
|
|
24705
|
+
const autoescapeOffRe = /\bautoescape\s*=\s*(?:False|None|0)\b/;
|
|
24706
|
+
const envVars = new Set;
|
|
24707
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
24708
|
+
const m = envAssignRe.exec(lines[i2]);
|
|
24709
|
+
if (!m)
|
|
24710
|
+
continue;
|
|
24711
|
+
if (!autoescapeOnRe.test(lines[i2]))
|
|
24712
|
+
continue;
|
|
24713
|
+
if (autoescapeOffRe.test(lines[i2]))
|
|
24714
|
+
continue;
|
|
24715
|
+
envVars.add(m[1]);
|
|
24716
|
+
}
|
|
24717
|
+
if (envVars.size === 0)
|
|
24718
|
+
return sanitizers;
|
|
24719
|
+
for (const envVar of envVars) {
|
|
24720
|
+
const renderChainRe = new RegExp(`\\b${envVar}\\s*\\.\\s*get_template\\s*\\([^)]*\\)\\s*\\.\\s*render\\s*\\(`);
|
|
24721
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
24722
|
+
if (!renderChainRe.test(lines[i2]))
|
|
24723
|
+
continue;
|
|
24724
|
+
sanitizers.push({
|
|
24725
|
+
type: "python_jinja_autoescape",
|
|
24726
|
+
method: "render",
|
|
24727
|
+
line: i2 + 1,
|
|
24728
|
+
sanitizes: ["xss", "external_taint_escape"]
|
|
24729
|
+
});
|
|
24730
|
+
}
|
|
24731
|
+
}
|
|
24732
|
+
return sanitizers;
|
|
24733
|
+
}
|
|
24490
24734
|
function collectEnvSecretVars(lines, language) {
|
|
24491
24735
|
const out2 = new Map;
|
|
24492
24736
|
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
@@ -38028,7 +38272,7 @@ var colors = {
|
|
|
38028
38272
|
};
|
|
38029
38273
|
|
|
38030
38274
|
// src/version.ts
|
|
38031
|
-
var version = "3.
|
|
38275
|
+
var version = "3.128.0";
|
|
38032
38276
|
|
|
38033
38277
|
// src/formatters.ts
|
|
38034
38278
|
var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.128.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",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@cognium/project-profile-detect": "^1.1.0",
|
|
69
|
-
"circle-ir": "^3.
|
|
69
|
+
"circle-ir": "^3.128.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^25.5.0",
|