cognium-dev 3.73.0 → 3.74.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 +228 -8
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -11437,11 +11437,17 @@ var DEFAULT_SANITIZERS = [
|
|
|
11437
11437
|
{ method: "toRealPath", class: "Path", removes: ["path_traversal"] },
|
|
11438
11438
|
{ method: "file_name", removes: ["path_traversal"] },
|
|
11439
11439
|
{ method: "canonicalize", removes: ["path_traversal"] },
|
|
11440
|
-
{ method: "Base", class: "filepath", removes: ["path_traversal"] },
|
|
11441
|
-
{ method: "Base", class: "path", removes: ["path_traversal"] },
|
|
11442
|
-
{ method: "Clean", class: "filepath", removes: ["path_traversal"] },
|
|
11443
|
-
{ method: "Clean", class: "path", removes: ["path_traversal"] },
|
|
11444
|
-
{ method: "EvalSymlinks", class: "filepath", removes: ["path_traversal"] },
|
|
11440
|
+
{ method: "Base", class: "filepath", removes: ["path_traversal", "external_taint_escape"] },
|
|
11441
|
+
{ method: "Base", class: "path", removes: ["path_traversal", "external_taint_escape"] },
|
|
11442
|
+
{ method: "Clean", class: "filepath", removes: ["path_traversal", "external_taint_escape"] },
|
|
11443
|
+
{ method: "Clean", class: "path", removes: ["path_traversal", "external_taint_escape"] },
|
|
11444
|
+
{ method: "EvalSymlinks", class: "filepath", removes: ["path_traversal", "external_taint_escape"] },
|
|
11445
|
+
{ method: "EscapeString", class: "html", removes: ["xss", "external_taint_escape", "log_injection", "open_redirect"] },
|
|
11446
|
+
{ method: "HTMLEscapeString", class: "template", removes: ["xss", "external_taint_escape", "log_injection", "open_redirect"] },
|
|
11447
|
+
{ method: "JSEscapeString", class: "template", removes: ["xss", "external_taint_escape", "log_injection"] },
|
|
11448
|
+
{ method: "URLQueryEscaper", class: "template", removes: ["xss", "external_taint_escape", "open_redirect"] },
|
|
11449
|
+
{ method: "QueryEscape", class: "url", removes: ["xss", "external_taint_escape", "open_redirect"] },
|
|
11450
|
+
{ method: "PathEscape", class: "url", removes: ["xss", "external_taint_escape", "open_redirect"] },
|
|
11445
11451
|
{ method: "replace", removes: ["log_injection"] },
|
|
11446
11452
|
{ method: "encodeForLDAP", removes: ["ldap_injection"] },
|
|
11447
11453
|
{ method: "encodeForDN", removes: ["ldap_injection"] },
|
|
@@ -11992,6 +11998,48 @@ function isSafePythonSubprocessCall(call, pattern, language) {
|
|
|
11992
11998
|
}
|
|
11993
11999
|
return true;
|
|
11994
12000
|
}
|
|
12001
|
+
function isSafeGoExecCommandCall(call, pattern, language) {
|
|
12002
|
+
if (language !== "go")
|
|
12003
|
+
return false;
|
|
12004
|
+
if (pattern.type !== "command_injection")
|
|
12005
|
+
return false;
|
|
12006
|
+
if (pattern.class !== "exec")
|
|
12007
|
+
return false;
|
|
12008
|
+
if (pattern.method !== "Command" && pattern.method !== "CommandContext")
|
|
12009
|
+
return false;
|
|
12010
|
+
const programArgPos = pattern.method === "CommandContext" ? 1 : 0;
|
|
12011
|
+
const programArg = call.arguments.find((a) => a.position === programArgPos);
|
|
12012
|
+
if (!programArg)
|
|
12013
|
+
return false;
|
|
12014
|
+
let program;
|
|
12015
|
+
if (programArg.literal !== null && programArg.literal !== undefined) {
|
|
12016
|
+
program = String(programArg.literal).split("/").pop() ?? String(programArg.literal);
|
|
12017
|
+
} else {
|
|
12018
|
+
const expr = (programArg.expression ?? "").trim();
|
|
12019
|
+
if (!(expr.startsWith('"') || expr.startsWith("`") || expr.startsWith("'"))) {
|
|
12020
|
+
return false;
|
|
12021
|
+
}
|
|
12022
|
+
const stripped = expr.slice(1, -1);
|
|
12023
|
+
program = stripped.split("/").pop() ?? stripped;
|
|
12024
|
+
}
|
|
12025
|
+
const SHELL_PROGRAMS = new Set([
|
|
12026
|
+
"sh",
|
|
12027
|
+
"bash",
|
|
12028
|
+
"zsh",
|
|
12029
|
+
"dash",
|
|
12030
|
+
"ash",
|
|
12031
|
+
"ksh",
|
|
12032
|
+
"cmd",
|
|
12033
|
+
"cmd.exe",
|
|
12034
|
+
"powershell",
|
|
12035
|
+
"pwsh",
|
|
12036
|
+
"powershell.exe",
|
|
12037
|
+
"pwsh.exe"
|
|
12038
|
+
]);
|
|
12039
|
+
if (SHELL_PROGRAMS.has(program))
|
|
12040
|
+
return false;
|
|
12041
|
+
return true;
|
|
12042
|
+
}
|
|
11995
12043
|
var CLASS_LITERAL_RE = /^(?:[A-Za-z_][\w]*\.)*[A-Z][\w]*(?:\[\])*\.class$/;
|
|
11996
12044
|
function argIsClassLiteral(call, position) {
|
|
11997
12045
|
const arg = call.arguments.find((a) => a.position === position);
|
|
@@ -12013,6 +12061,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
|
|
|
12013
12061
|
if (isSafePythonSubprocessCall(call, pattern, language)) {
|
|
12014
12062
|
continue;
|
|
12015
12063
|
}
|
|
12064
|
+
if (isSafeGoExecCommandCall(call, pattern, language)) {
|
|
12065
|
+
continue;
|
|
12066
|
+
}
|
|
12016
12067
|
if (pattern.safe_if_class_literal_at !== undefined && argIsClassLiteral(call, pattern.safe_if_class_literal_at)) {
|
|
12017
12068
|
continue;
|
|
12018
12069
|
}
|
|
@@ -21241,6 +21292,10 @@ class LanguageSourcesPass {
|
|
|
21241
21292
|
additionalSanitizers.push(...findBashRegexAllowlistSanitizers(code));
|
|
21242
21293
|
additionalSanitizers.push(...findBashRealpathPrefixGuardSanitizers(code));
|
|
21243
21294
|
}
|
|
21295
|
+
if (language === "go") {
|
|
21296
|
+
additionalSanitizers.push(...findGoMapAllowlistGuardSanitizers(code));
|
|
21297
|
+
additionalSanitizers.push(...findGoHtmlTemplateImportSanitizers(code));
|
|
21298
|
+
}
|
|
21244
21299
|
attachSourceLineCode(additionalSources, additionalSinks, code);
|
|
21245
21300
|
return { additionalSources, additionalSinks, additionalSanitizers, pyTaintedVars, pySanitizedVars, jsTaintedVars };
|
|
21246
21301
|
}
|
|
@@ -22279,6 +22334,82 @@ function findBashRealpathPrefixGuardSanitizers(code) {
|
|
|
22279
22334
|
}
|
|
22280
22335
|
return sanitizers;
|
|
22281
22336
|
}
|
|
22337
|
+
function findGoMapAllowlistGuardSanitizers(code) {
|
|
22338
|
+
const sanitizers = [];
|
|
22339
|
+
const lines = code.split(`
|
|
22340
|
+
`);
|
|
22341
|
+
const guardOpen = /^\s*if\s+!\s*([A-Za-z_][A-Za-z0-9_]*)\s*\[\s*[A-Za-z_][A-Za-z0-9_]*\s*\]\s*\{/;
|
|
22342
|
+
const allowlistName = /^(?:[A-Z][A-Z0-9_]+|.*?(allowed|accepted|whitelist|permitted|valid|approved).*)$/i;
|
|
22343
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
22344
|
+
const m = guardOpen.exec(lines[i2]);
|
|
22345
|
+
if (!m)
|
|
22346
|
+
continue;
|
|
22347
|
+
const mapName = m[1];
|
|
22348
|
+
if (!allowlistName.test(mapName))
|
|
22349
|
+
continue;
|
|
22350
|
+
let depth = 1;
|
|
22351
|
+
let closeLine = -1;
|
|
22352
|
+
let bodyHasTerminator = false;
|
|
22353
|
+
const maxScan = Math.min(lines.length, i2 + 26);
|
|
22354
|
+
for (let j = i2 + 1;j < maxScan; j++) {
|
|
22355
|
+
const line = lines[j];
|
|
22356
|
+
if (/\b(return|panic\s*\(|os\.Exit\s*\()/.test(line)) {
|
|
22357
|
+
bodyHasTerminator = true;
|
|
22358
|
+
}
|
|
22359
|
+
for (const ch of line) {
|
|
22360
|
+
if (ch === "{")
|
|
22361
|
+
depth++;
|
|
22362
|
+
else if (ch === "}")
|
|
22363
|
+
depth--;
|
|
22364
|
+
}
|
|
22365
|
+
if (depth === 0) {
|
|
22366
|
+
closeLine = j;
|
|
22367
|
+
break;
|
|
22368
|
+
}
|
|
22369
|
+
}
|
|
22370
|
+
if (closeLine === -1 || !bodyHasTerminator)
|
|
22371
|
+
continue;
|
|
22372
|
+
for (let l = closeLine + 2;l <= lines.length; l++) {
|
|
22373
|
+
sanitizers.push({
|
|
22374
|
+
type: "go_map_allowlist_guard",
|
|
22375
|
+
method: "if",
|
|
22376
|
+
line: l,
|
|
22377
|
+
sanitizes: [
|
|
22378
|
+
"ssrf",
|
|
22379
|
+
"open_redirect",
|
|
22380
|
+
"path_traversal",
|
|
22381
|
+
"sql_injection",
|
|
22382
|
+
"command_injection",
|
|
22383
|
+
"external_taint_escape"
|
|
22384
|
+
]
|
|
22385
|
+
});
|
|
22386
|
+
}
|
|
22387
|
+
}
|
|
22388
|
+
return sanitizers;
|
|
22389
|
+
}
|
|
22390
|
+
function findGoHtmlTemplateImportSanitizers(code) {
|
|
22391
|
+
const sanitizers = [];
|
|
22392
|
+
const hasHtmlTemplate = /["\s]html\/template["\s]/.test(code);
|
|
22393
|
+
const hasTextTemplate = /["\s]text\/template["\s]/.test(code);
|
|
22394
|
+
if (!hasHtmlTemplate)
|
|
22395
|
+
return sanitizers;
|
|
22396
|
+
if (hasTextTemplate)
|
|
22397
|
+
return sanitizers;
|
|
22398
|
+
const lines = code.split(`
|
|
22399
|
+
`);
|
|
22400
|
+
const execCall = /\.(Execute|ExecuteTemplate)\s*\(/;
|
|
22401
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
22402
|
+
if (!execCall.test(lines[i2]))
|
|
22403
|
+
continue;
|
|
22404
|
+
sanitizers.push({
|
|
22405
|
+
type: "html_template_auto_escape",
|
|
22406
|
+
method: "Execute",
|
|
22407
|
+
line: i2 + 1,
|
|
22408
|
+
sanitizes: ["xss", "external_taint_escape", "open_redirect"]
|
|
22409
|
+
});
|
|
22410
|
+
}
|
|
22411
|
+
return sanitizers;
|
|
22412
|
+
}
|
|
22282
22413
|
|
|
22283
22414
|
// ../circle-ir/dist/analysis/passes/sink-filter-pass.js
|
|
22284
22415
|
var JS_XSS_SANITIZERS = [
|
|
@@ -23103,6 +23234,40 @@ class TaintPropagationPass {
|
|
|
23103
23234
|
}
|
|
23104
23235
|
return true;
|
|
23105
23236
|
});
|
|
23237
|
+
if (sanitizers && sanitizers.length > 0) {
|
|
23238
|
+
const sanitizersByLine = new Map;
|
|
23239
|
+
for (const san of sanitizers) {
|
|
23240
|
+
const arr = sanitizersByLine.get(san.line) ?? [];
|
|
23241
|
+
arr.push(san);
|
|
23242
|
+
sanitizersByLine.set(san.line, arr);
|
|
23243
|
+
}
|
|
23244
|
+
finalFlows = finalFlows.filter((f) => {
|
|
23245
|
+
if (f.sink_type === "external_taint_escape") {
|
|
23246
|
+
const lo = Math.min(f.source_line, f.sink_line);
|
|
23247
|
+
const hi = Math.max(f.source_line, f.sink_line);
|
|
23248
|
+
for (let line = lo;line <= hi; line++) {
|
|
23249
|
+
const sansAtLine = sanitizersByLine.get(line);
|
|
23250
|
+
if (!sansAtLine)
|
|
23251
|
+
continue;
|
|
23252
|
+
for (const san of sansAtLine) {
|
|
23253
|
+
if (san.sanitizes.includes(f.sink_type)) {
|
|
23254
|
+
return false;
|
|
23255
|
+
}
|
|
23256
|
+
}
|
|
23257
|
+
}
|
|
23258
|
+
return true;
|
|
23259
|
+
}
|
|
23260
|
+
const sansAtSink = sanitizersByLine.get(f.sink_line);
|
|
23261
|
+
if (!sansAtSink || sansAtSink.length === 0)
|
|
23262
|
+
return true;
|
|
23263
|
+
for (const san of sansAtSink) {
|
|
23264
|
+
if (san.sanitizes.includes(f.sink_type)) {
|
|
23265
|
+
return false;
|
|
23266
|
+
}
|
|
23267
|
+
}
|
|
23268
|
+
return true;
|
|
23269
|
+
});
|
|
23270
|
+
}
|
|
23106
23271
|
if (ctx.language === "java" && typeof ctx.code === "string") {
|
|
23107
23272
|
finalFlows = finalFlows.filter((f) => {
|
|
23108
23273
|
if (f.sink_type !== "path_traversal" && f.sink_type !== "xxe")
|
|
@@ -23896,7 +24061,19 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
|
|
|
23896
24061
|
"BufferedWriter",
|
|
23897
24062
|
"PrintStream",
|
|
23898
24063
|
"PrintWriter",
|
|
23899
|
-
"ObjectOutputStream"
|
|
24064
|
+
"ObjectOutputStream",
|
|
24065
|
+
"Query",
|
|
24066
|
+
"QueryRow",
|
|
24067
|
+
"QueryContext",
|
|
24068
|
+
"QueryRowContext",
|
|
24069
|
+
"Exec",
|
|
24070
|
+
"ExecContext",
|
|
24071
|
+
"EscapeString",
|
|
24072
|
+
"HTMLEscapeString",
|
|
24073
|
+
"JSEscapeString",
|
|
24074
|
+
"URLQueryEscaper",
|
|
24075
|
+
"Command",
|
|
24076
|
+
"CommandContext"
|
|
23900
24077
|
]);
|
|
23901
24078
|
const sanitizerMethods = new Set;
|
|
23902
24079
|
for (const san of sanitizers) {
|
|
@@ -24314,7 +24491,50 @@ class InterproceduralPass {
|
|
|
24314
24491
|
if (additionalSinks.length > 0) {
|
|
24315
24492
|
attachSourceLineCode([], additionalSinks, ctx.code);
|
|
24316
24493
|
}
|
|
24317
|
-
|
|
24494
|
+
let filteredAdditionalFlows = additionalFlows;
|
|
24495
|
+
let filteredAdditionalSinks = additionalSinks;
|
|
24496
|
+
if (sanitizers && sanitizers.length > 0) {
|
|
24497
|
+
const sanitizersByLine = new Map;
|
|
24498
|
+
for (const san of sanitizers) {
|
|
24499
|
+
const arr = sanitizersByLine.get(san.line) ?? [];
|
|
24500
|
+
arr.push(san);
|
|
24501
|
+
sanitizersByLine.set(san.line, arr);
|
|
24502
|
+
}
|
|
24503
|
+
const sanitizedSinkKeys = new Set;
|
|
24504
|
+
filteredAdditionalFlows = additionalFlows.filter((f) => {
|
|
24505
|
+
if (f.sink_type === "external_taint_escape") {
|
|
24506
|
+
const lo = Math.min(f.source_line, f.sink_line);
|
|
24507
|
+
const hi = Math.max(f.source_line, f.sink_line);
|
|
24508
|
+
for (let line = lo;line <= hi; line++) {
|
|
24509
|
+
const sansAtLine = sanitizersByLine.get(line);
|
|
24510
|
+
if (!sansAtLine)
|
|
24511
|
+
continue;
|
|
24512
|
+
for (const san of sansAtLine) {
|
|
24513
|
+
if (san.sanitizes.includes(f.sink_type)) {
|
|
24514
|
+
sanitizedSinkKeys.add(`${f.sink_line}:${f.sink_type}`);
|
|
24515
|
+
return false;
|
|
24516
|
+
}
|
|
24517
|
+
}
|
|
24518
|
+
}
|
|
24519
|
+
return true;
|
|
24520
|
+
}
|
|
24521
|
+
const sansAtSink = sanitizersByLine.get(f.sink_line);
|
|
24522
|
+
if (!sansAtSink || sansAtSink.length === 0)
|
|
24523
|
+
return true;
|
|
24524
|
+
for (const san of sansAtSink) {
|
|
24525
|
+
if (san.sanitizes.includes(f.sink_type)) {
|
|
24526
|
+
return false;
|
|
24527
|
+
}
|
|
24528
|
+
}
|
|
24529
|
+
return true;
|
|
24530
|
+
});
|
|
24531
|
+
filteredAdditionalSinks = additionalSinks.filter((s) => {
|
|
24532
|
+
if (s.type !== "external_taint_escape")
|
|
24533
|
+
return true;
|
|
24534
|
+
return !sanitizedSinkKeys.has(`${s.line}:${s.type}`);
|
|
24535
|
+
});
|
|
24536
|
+
}
|
|
24537
|
+
return { additionalSinks: filteredAdditionalSinks, additionalFlows: filteredAdditionalFlows, interprocedural };
|
|
24318
24538
|
}
|
|
24319
24539
|
}
|
|
24320
24540
|
|
|
@@ -32262,7 +32482,7 @@ var colors = {
|
|
|
32262
32482
|
};
|
|
32263
32483
|
|
|
32264
32484
|
// src/version.ts
|
|
32265
|
-
var version = "3.
|
|
32485
|
+
var version = "3.74.0";
|
|
32266
32486
|
|
|
32267
32487
|
// src/formatters.ts
|
|
32268
32488
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.74.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.74.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|