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.
Files changed (2) hide show
  1. package/dist/cli.js +228 -8
  2. 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
- return { additionalSinks, additionalFlows, interprocedural };
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.73.0";
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.73.0",
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.73.0"
68
+ "circle-ir": "^3.74.0"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^25.5.0",