cognium-dev 3.73.0 → 3.75.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 +244 -8
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -11388,6 +11388,12 @@ var DEFAULT_SINKS = [
|
|
|
11388
11388
|
{ method: "redirect", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11389
11389
|
{ method: "Set", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11390
11390
|
{ method: "Add", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11391
|
+
{ method: "set", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
|
|
11392
|
+
{ method: "add", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
|
|
11393
|
+
{ method: "setdefault", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
|
|
11394
|
+
{ method: "extend", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["python"] },
|
|
11395
|
+
{ method: "__setitem__", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
|
|
11396
|
+
{ method: "set_cookie", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
|
|
11391
11397
|
{ method: "assign", class: "Object", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11392
11398
|
{ method: "defineProperty", class: "Object", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2], languages: ["javascript", "typescript"] },
|
|
11393
11399
|
{ method: "defineProperties", class: "Object", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1], languages: ["javascript", "typescript"] },
|
|
@@ -11437,11 +11443,17 @@ var DEFAULT_SANITIZERS = [
|
|
|
11437
11443
|
{ method: "toRealPath", class: "Path", removes: ["path_traversal"] },
|
|
11438
11444
|
{ method: "file_name", removes: ["path_traversal"] },
|
|
11439
11445
|
{ 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"] },
|
|
11446
|
+
{ method: "Base", class: "filepath", removes: ["path_traversal", "external_taint_escape"] },
|
|
11447
|
+
{ method: "Base", class: "path", removes: ["path_traversal", "external_taint_escape"] },
|
|
11448
|
+
{ method: "Clean", class: "filepath", removes: ["path_traversal", "external_taint_escape"] },
|
|
11449
|
+
{ method: "Clean", class: "path", removes: ["path_traversal", "external_taint_escape"] },
|
|
11450
|
+
{ method: "EvalSymlinks", class: "filepath", removes: ["path_traversal", "external_taint_escape"] },
|
|
11451
|
+
{ method: "EscapeString", class: "html", removes: ["xss", "external_taint_escape", "log_injection", "open_redirect"] },
|
|
11452
|
+
{ method: "HTMLEscapeString", class: "template", removes: ["xss", "external_taint_escape", "log_injection", "open_redirect"] },
|
|
11453
|
+
{ method: "JSEscapeString", class: "template", removes: ["xss", "external_taint_escape", "log_injection"] },
|
|
11454
|
+
{ method: "URLQueryEscaper", class: "template", removes: ["xss", "external_taint_escape", "open_redirect"] },
|
|
11455
|
+
{ method: "QueryEscape", class: "url", removes: ["xss", "external_taint_escape", "open_redirect"] },
|
|
11456
|
+
{ method: "PathEscape", class: "url", removes: ["xss", "external_taint_escape", "open_redirect"] },
|
|
11445
11457
|
{ method: "replace", removes: ["log_injection"] },
|
|
11446
11458
|
{ method: "encodeForLDAP", removes: ["ldap_injection"] },
|
|
11447
11459
|
{ method: "encodeForDN", removes: ["ldap_injection"] },
|
|
@@ -11992,6 +12004,48 @@ function isSafePythonSubprocessCall(call, pattern, language) {
|
|
|
11992
12004
|
}
|
|
11993
12005
|
return true;
|
|
11994
12006
|
}
|
|
12007
|
+
function isSafeGoExecCommandCall(call, pattern, language) {
|
|
12008
|
+
if (language !== "go")
|
|
12009
|
+
return false;
|
|
12010
|
+
if (pattern.type !== "command_injection")
|
|
12011
|
+
return false;
|
|
12012
|
+
if (pattern.class !== "exec")
|
|
12013
|
+
return false;
|
|
12014
|
+
if (pattern.method !== "Command" && pattern.method !== "CommandContext")
|
|
12015
|
+
return false;
|
|
12016
|
+
const programArgPos = pattern.method === "CommandContext" ? 1 : 0;
|
|
12017
|
+
const programArg = call.arguments.find((a) => a.position === programArgPos);
|
|
12018
|
+
if (!programArg)
|
|
12019
|
+
return false;
|
|
12020
|
+
let program;
|
|
12021
|
+
if (programArg.literal !== null && programArg.literal !== undefined) {
|
|
12022
|
+
program = String(programArg.literal).split("/").pop() ?? String(programArg.literal);
|
|
12023
|
+
} else {
|
|
12024
|
+
const expr = (programArg.expression ?? "").trim();
|
|
12025
|
+
if (!(expr.startsWith('"') || expr.startsWith("`") || expr.startsWith("'"))) {
|
|
12026
|
+
return false;
|
|
12027
|
+
}
|
|
12028
|
+
const stripped = expr.slice(1, -1);
|
|
12029
|
+
program = stripped.split("/").pop() ?? stripped;
|
|
12030
|
+
}
|
|
12031
|
+
const SHELL_PROGRAMS = new Set([
|
|
12032
|
+
"sh",
|
|
12033
|
+
"bash",
|
|
12034
|
+
"zsh",
|
|
12035
|
+
"dash",
|
|
12036
|
+
"ash",
|
|
12037
|
+
"ksh",
|
|
12038
|
+
"cmd",
|
|
12039
|
+
"cmd.exe",
|
|
12040
|
+
"powershell",
|
|
12041
|
+
"pwsh",
|
|
12042
|
+
"powershell.exe",
|
|
12043
|
+
"pwsh.exe"
|
|
12044
|
+
]);
|
|
12045
|
+
if (SHELL_PROGRAMS.has(program))
|
|
12046
|
+
return false;
|
|
12047
|
+
return true;
|
|
12048
|
+
}
|
|
11995
12049
|
var CLASS_LITERAL_RE = /^(?:[A-Za-z_][\w]*\.)*[A-Z][\w]*(?:\[\])*\.class$/;
|
|
11996
12050
|
function argIsClassLiteral(call, position) {
|
|
11997
12051
|
const arg = call.arguments.find((a) => a.position === position);
|
|
@@ -12013,6 +12067,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
|
|
|
12013
12067
|
if (isSafePythonSubprocessCall(call, pattern, language)) {
|
|
12014
12068
|
continue;
|
|
12015
12069
|
}
|
|
12070
|
+
if (isSafeGoExecCommandCall(call, pattern, language)) {
|
|
12071
|
+
continue;
|
|
12072
|
+
}
|
|
12016
12073
|
if (pattern.safe_if_class_literal_at !== undefined && argIsClassLiteral(call, pattern.safe_if_class_literal_at)) {
|
|
12017
12074
|
continue;
|
|
12018
12075
|
}
|
|
@@ -12357,6 +12414,10 @@ function receiverMightBeClass(receiver, className) {
|
|
|
12357
12414
|
}
|
|
12358
12415
|
}
|
|
12359
12416
|
}
|
|
12417
|
+
const chainedCallSuffix = `.${className}()`;
|
|
12418
|
+
if (receiver.endsWith(chainedCallSuffix) || receiver.toLowerCase().endsWith(chainedCallSuffix.toLowerCase())) {
|
|
12419
|
+
return true;
|
|
12420
|
+
}
|
|
12360
12421
|
if (receiver.includes("::")) {
|
|
12361
12422
|
const scopePrefix = receiver.match(/^(\w+)::/);
|
|
12362
12423
|
if (scopePrefix) {
|
|
@@ -21241,6 +21302,10 @@ class LanguageSourcesPass {
|
|
|
21241
21302
|
additionalSanitizers.push(...findBashRegexAllowlistSanitizers(code));
|
|
21242
21303
|
additionalSanitizers.push(...findBashRealpathPrefixGuardSanitizers(code));
|
|
21243
21304
|
}
|
|
21305
|
+
if (language === "go") {
|
|
21306
|
+
additionalSanitizers.push(...findGoMapAllowlistGuardSanitizers(code));
|
|
21307
|
+
additionalSanitizers.push(...findGoHtmlTemplateImportSanitizers(code));
|
|
21308
|
+
}
|
|
21244
21309
|
attachSourceLineCode(additionalSources, additionalSinks, code);
|
|
21245
21310
|
return { additionalSources, additionalSinks, additionalSanitizers, pyTaintedVars, pySanitizedVars, jsTaintedVars };
|
|
21246
21311
|
}
|
|
@@ -22279,6 +22344,82 @@ function findBashRealpathPrefixGuardSanitizers(code) {
|
|
|
22279
22344
|
}
|
|
22280
22345
|
return sanitizers;
|
|
22281
22346
|
}
|
|
22347
|
+
function findGoMapAllowlistGuardSanitizers(code) {
|
|
22348
|
+
const sanitizers = [];
|
|
22349
|
+
const lines = code.split(`
|
|
22350
|
+
`);
|
|
22351
|
+
const guardOpen = /^\s*if\s+!\s*([A-Za-z_][A-Za-z0-9_]*)\s*\[\s*[A-Za-z_][A-Za-z0-9_]*\s*\]\s*\{/;
|
|
22352
|
+
const allowlistName = /^(?:[A-Z][A-Z0-9_]+|.*?(allowed|accepted|whitelist|permitted|valid|approved).*)$/i;
|
|
22353
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
22354
|
+
const m = guardOpen.exec(lines[i2]);
|
|
22355
|
+
if (!m)
|
|
22356
|
+
continue;
|
|
22357
|
+
const mapName = m[1];
|
|
22358
|
+
if (!allowlistName.test(mapName))
|
|
22359
|
+
continue;
|
|
22360
|
+
let depth = 1;
|
|
22361
|
+
let closeLine = -1;
|
|
22362
|
+
let bodyHasTerminator = false;
|
|
22363
|
+
const maxScan = Math.min(lines.length, i2 + 26);
|
|
22364
|
+
for (let j = i2 + 1;j < maxScan; j++) {
|
|
22365
|
+
const line = lines[j];
|
|
22366
|
+
if (/\b(return|panic\s*\(|os\.Exit\s*\()/.test(line)) {
|
|
22367
|
+
bodyHasTerminator = true;
|
|
22368
|
+
}
|
|
22369
|
+
for (const ch of line) {
|
|
22370
|
+
if (ch === "{")
|
|
22371
|
+
depth++;
|
|
22372
|
+
else if (ch === "}")
|
|
22373
|
+
depth--;
|
|
22374
|
+
}
|
|
22375
|
+
if (depth === 0) {
|
|
22376
|
+
closeLine = j;
|
|
22377
|
+
break;
|
|
22378
|
+
}
|
|
22379
|
+
}
|
|
22380
|
+
if (closeLine === -1 || !bodyHasTerminator)
|
|
22381
|
+
continue;
|
|
22382
|
+
for (let l = closeLine + 2;l <= lines.length; l++) {
|
|
22383
|
+
sanitizers.push({
|
|
22384
|
+
type: "go_map_allowlist_guard",
|
|
22385
|
+
method: "if",
|
|
22386
|
+
line: l,
|
|
22387
|
+
sanitizes: [
|
|
22388
|
+
"ssrf",
|
|
22389
|
+
"open_redirect",
|
|
22390
|
+
"path_traversal",
|
|
22391
|
+
"sql_injection",
|
|
22392
|
+
"command_injection",
|
|
22393
|
+
"external_taint_escape"
|
|
22394
|
+
]
|
|
22395
|
+
});
|
|
22396
|
+
}
|
|
22397
|
+
}
|
|
22398
|
+
return sanitizers;
|
|
22399
|
+
}
|
|
22400
|
+
function findGoHtmlTemplateImportSanitizers(code) {
|
|
22401
|
+
const sanitizers = [];
|
|
22402
|
+
const hasHtmlTemplate = /["\s]html\/template["\s]/.test(code);
|
|
22403
|
+
const hasTextTemplate = /["\s]text\/template["\s]/.test(code);
|
|
22404
|
+
if (!hasHtmlTemplate)
|
|
22405
|
+
return sanitizers;
|
|
22406
|
+
if (hasTextTemplate)
|
|
22407
|
+
return sanitizers;
|
|
22408
|
+
const lines = code.split(`
|
|
22409
|
+
`);
|
|
22410
|
+
const execCall = /\.(Execute|ExecuteTemplate)\s*\(/;
|
|
22411
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
22412
|
+
if (!execCall.test(lines[i2]))
|
|
22413
|
+
continue;
|
|
22414
|
+
sanitizers.push({
|
|
22415
|
+
type: "html_template_auto_escape",
|
|
22416
|
+
method: "Execute",
|
|
22417
|
+
line: i2 + 1,
|
|
22418
|
+
sanitizes: ["xss", "external_taint_escape", "open_redirect"]
|
|
22419
|
+
});
|
|
22420
|
+
}
|
|
22421
|
+
return sanitizers;
|
|
22422
|
+
}
|
|
22282
22423
|
|
|
22283
22424
|
// ../circle-ir/dist/analysis/passes/sink-filter-pass.js
|
|
22284
22425
|
var JS_XSS_SANITIZERS = [
|
|
@@ -23103,6 +23244,40 @@ class TaintPropagationPass {
|
|
|
23103
23244
|
}
|
|
23104
23245
|
return true;
|
|
23105
23246
|
});
|
|
23247
|
+
if (sanitizers && sanitizers.length > 0) {
|
|
23248
|
+
const sanitizersByLine = new Map;
|
|
23249
|
+
for (const san of sanitizers) {
|
|
23250
|
+
const arr = sanitizersByLine.get(san.line) ?? [];
|
|
23251
|
+
arr.push(san);
|
|
23252
|
+
sanitizersByLine.set(san.line, arr);
|
|
23253
|
+
}
|
|
23254
|
+
finalFlows = finalFlows.filter((f) => {
|
|
23255
|
+
if (f.sink_type === "external_taint_escape") {
|
|
23256
|
+
const lo = Math.min(f.source_line, f.sink_line);
|
|
23257
|
+
const hi = Math.max(f.source_line, f.sink_line);
|
|
23258
|
+
for (let line = lo;line <= hi; line++) {
|
|
23259
|
+
const sansAtLine = sanitizersByLine.get(line);
|
|
23260
|
+
if (!sansAtLine)
|
|
23261
|
+
continue;
|
|
23262
|
+
for (const san of sansAtLine) {
|
|
23263
|
+
if (san.sanitizes.includes(f.sink_type)) {
|
|
23264
|
+
return false;
|
|
23265
|
+
}
|
|
23266
|
+
}
|
|
23267
|
+
}
|
|
23268
|
+
return true;
|
|
23269
|
+
}
|
|
23270
|
+
const sansAtSink = sanitizersByLine.get(f.sink_line);
|
|
23271
|
+
if (!sansAtSink || sansAtSink.length === 0)
|
|
23272
|
+
return true;
|
|
23273
|
+
for (const san of sansAtSink) {
|
|
23274
|
+
if (san.sanitizes.includes(f.sink_type)) {
|
|
23275
|
+
return false;
|
|
23276
|
+
}
|
|
23277
|
+
}
|
|
23278
|
+
return true;
|
|
23279
|
+
});
|
|
23280
|
+
}
|
|
23106
23281
|
if (ctx.language === "java" && typeof ctx.code === "string") {
|
|
23107
23282
|
finalFlows = finalFlows.filter((f) => {
|
|
23108
23283
|
if (f.sink_type !== "path_traversal" && f.sink_type !== "xxe")
|
|
@@ -23896,7 +24071,19 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
|
|
|
23896
24071
|
"BufferedWriter",
|
|
23897
24072
|
"PrintStream",
|
|
23898
24073
|
"PrintWriter",
|
|
23899
|
-
"ObjectOutputStream"
|
|
24074
|
+
"ObjectOutputStream",
|
|
24075
|
+
"Query",
|
|
24076
|
+
"QueryRow",
|
|
24077
|
+
"QueryContext",
|
|
24078
|
+
"QueryRowContext",
|
|
24079
|
+
"Exec",
|
|
24080
|
+
"ExecContext",
|
|
24081
|
+
"EscapeString",
|
|
24082
|
+
"HTMLEscapeString",
|
|
24083
|
+
"JSEscapeString",
|
|
24084
|
+
"URLQueryEscaper",
|
|
24085
|
+
"Command",
|
|
24086
|
+
"CommandContext"
|
|
23900
24087
|
]);
|
|
23901
24088
|
const sanitizerMethods = new Set;
|
|
23902
24089
|
for (const san of sanitizers) {
|
|
@@ -24314,7 +24501,50 @@ class InterproceduralPass {
|
|
|
24314
24501
|
if (additionalSinks.length > 0) {
|
|
24315
24502
|
attachSourceLineCode([], additionalSinks, ctx.code);
|
|
24316
24503
|
}
|
|
24317
|
-
|
|
24504
|
+
let filteredAdditionalFlows = additionalFlows;
|
|
24505
|
+
let filteredAdditionalSinks = additionalSinks;
|
|
24506
|
+
if (sanitizers && sanitizers.length > 0) {
|
|
24507
|
+
const sanitizersByLine = new Map;
|
|
24508
|
+
for (const san of sanitizers) {
|
|
24509
|
+
const arr = sanitizersByLine.get(san.line) ?? [];
|
|
24510
|
+
arr.push(san);
|
|
24511
|
+
sanitizersByLine.set(san.line, arr);
|
|
24512
|
+
}
|
|
24513
|
+
const sanitizedSinkKeys = new Set;
|
|
24514
|
+
filteredAdditionalFlows = additionalFlows.filter((f) => {
|
|
24515
|
+
if (f.sink_type === "external_taint_escape") {
|
|
24516
|
+
const lo = Math.min(f.source_line, f.sink_line);
|
|
24517
|
+
const hi = Math.max(f.source_line, f.sink_line);
|
|
24518
|
+
for (let line = lo;line <= hi; line++) {
|
|
24519
|
+
const sansAtLine = sanitizersByLine.get(line);
|
|
24520
|
+
if (!sansAtLine)
|
|
24521
|
+
continue;
|
|
24522
|
+
for (const san of sansAtLine) {
|
|
24523
|
+
if (san.sanitizes.includes(f.sink_type)) {
|
|
24524
|
+
sanitizedSinkKeys.add(`${f.sink_line}:${f.sink_type}`);
|
|
24525
|
+
return false;
|
|
24526
|
+
}
|
|
24527
|
+
}
|
|
24528
|
+
}
|
|
24529
|
+
return true;
|
|
24530
|
+
}
|
|
24531
|
+
const sansAtSink = sanitizersByLine.get(f.sink_line);
|
|
24532
|
+
if (!sansAtSink || sansAtSink.length === 0)
|
|
24533
|
+
return true;
|
|
24534
|
+
for (const san of sansAtSink) {
|
|
24535
|
+
if (san.sanitizes.includes(f.sink_type)) {
|
|
24536
|
+
return false;
|
|
24537
|
+
}
|
|
24538
|
+
}
|
|
24539
|
+
return true;
|
|
24540
|
+
});
|
|
24541
|
+
filteredAdditionalSinks = additionalSinks.filter((s) => {
|
|
24542
|
+
if (s.type !== "external_taint_escape")
|
|
24543
|
+
return true;
|
|
24544
|
+
return !sanitizedSinkKeys.has(`${s.line}:${s.type}`);
|
|
24545
|
+
});
|
|
24546
|
+
}
|
|
24547
|
+
return { additionalSinks: filteredAdditionalSinks, additionalFlows: filteredAdditionalFlows, interprocedural };
|
|
24318
24548
|
}
|
|
24319
24549
|
}
|
|
24320
24550
|
|
|
@@ -29709,6 +29939,12 @@ class WeakRandomPass {
|
|
|
29709
29939
|
return `${rt}.${method}`;
|
|
29710
29940
|
}
|
|
29711
29941
|
}
|
|
29942
|
+
if (JAVA_RANDOM_METHODS.has(method)) {
|
|
29943
|
+
if (/^new\s+Random\s*\(/.test(receiver))
|
|
29944
|
+
return `new Random.${method}`;
|
|
29945
|
+
if (/^new\s+SplittableRandom\s*\(/.test(receiver))
|
|
29946
|
+
return `new SplittableRandom.${method}`;
|
|
29947
|
+
}
|
|
29712
29948
|
if (JAVA_RANDOM_METHODS.has(method) && /ThreadLocalRandom\.current\(\)/.test(receiver)) {
|
|
29713
29949
|
return `ThreadLocalRandom.current.${method}`;
|
|
29714
29950
|
}
|
|
@@ -32262,7 +32498,7 @@ var colors = {
|
|
|
32262
32498
|
};
|
|
32263
32499
|
|
|
32264
32500
|
// src/version.ts
|
|
32265
|
-
var version = "3.
|
|
32501
|
+
var version = "3.75.0";
|
|
32266
32502
|
|
|
32267
32503
|
// src/formatters.ts
|
|
32268
32504
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.75.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.75.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|