cognium-dev 3.83.0 → 3.84.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 +271 -1
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -12047,6 +12047,61 @@ function isSafeGoExecCommandCall(call, pattern, language) {
12047
12047
  return false;
12048
12048
  return true;
12049
12049
  }
12050
+ function isSafeRustCommandCall(call, pattern, language) {
12051
+ if (language !== "rust")
12052
+ return false;
12053
+ if (pattern.type !== "command_injection")
12054
+ return false;
12055
+ if (pattern.class !== undefined && pattern.class !== "Command")
12056
+ return false;
12057
+ const SHELL_PROGRAMS = new Set([
12058
+ "sh",
12059
+ "bash",
12060
+ "zsh",
12061
+ "dash",
12062
+ "ash",
12063
+ "ksh",
12064
+ "cmd",
12065
+ "cmd.exe",
12066
+ "powershell",
12067
+ "pwsh",
12068
+ "powershell.exe",
12069
+ "pwsh.exe"
12070
+ ]);
12071
+ const PROGRAM_RE = /\bCommand\s*::\s*new\s*\(\s*(?:r?"([^"]*)"|'([^']*)')/;
12072
+ const extractProgram = (text) => {
12073
+ const m = PROGRAM_RE.exec(text);
12074
+ if (!m)
12075
+ return null;
12076
+ const lit = m[1] ?? m[2] ?? "";
12077
+ return lit.split("/").pop() ?? lit;
12078
+ };
12079
+ if (pattern.method === "new") {
12080
+ const programArg = call.arguments.find((a) => a.position === 0);
12081
+ if (!programArg)
12082
+ return false;
12083
+ let program;
12084
+ if (programArg.literal !== null && programArg.literal !== undefined) {
12085
+ program = String(programArg.literal).split("/").pop() ?? String(programArg.literal);
12086
+ } else {
12087
+ const expr = (programArg.expression ?? "").trim();
12088
+ if (!(expr.startsWith('"') || expr.startsWith("'"))) {
12089
+ return false;
12090
+ }
12091
+ const stripped = expr.slice(1, -1);
12092
+ program = stripped.split("/").pop() ?? stripped;
12093
+ }
12094
+ return !SHELL_PROGRAMS.has(program);
12095
+ }
12096
+ if (pattern.method === "arg" || pattern.method === "args" || pattern.method === "spawn" || pattern.method === "output") {
12097
+ const receiverText = call.receiver ?? "";
12098
+ const program = extractProgram(receiverText);
12099
+ if (program === null)
12100
+ return false;
12101
+ return !SHELL_PROGRAMS.has(program);
12102
+ }
12103
+ return false;
12104
+ }
12050
12105
  var CLASS_LITERAL_RE = /^(?:[A-Za-z_][\w]*\.)*[A-Z][\w]*(?:\[\])*\.class$/;
12051
12106
  function argIsClassLiteral(call, position) {
12052
12107
  const arg = call.arguments.find((a) => a.position === position);
@@ -12071,6 +12126,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
12071
12126
  if (isSafeGoExecCommandCall(call, pattern, language)) {
12072
12127
  continue;
12073
12128
  }
12129
+ if (isSafeRustCommandCall(call, pattern, language)) {
12130
+ continue;
12131
+ }
12074
12132
  if (pattern.safe_if_class_literal_at !== undefined && argIsClassLiteral(call, pattern.safe_if_class_literal_at)) {
12075
12133
  continue;
12076
12134
  }
@@ -21307,6 +21365,14 @@ class LanguageSourcesPass {
21307
21365
  additionalSanitizers.push(...findGoMapAllowlistGuardSanitizers(code));
21308
21366
  additionalSanitizers.push(...findGoHtmlTemplateImportSanitizers(code));
21309
21367
  }
21368
+ if (language === "python") {
21369
+ additionalSanitizers.push(...findPythonNetlocAllowlistGuardSanitizers(code));
21370
+ additionalSanitizers.push(...findPythonRangeCheckGuardSanitizers(code));
21371
+ }
21372
+ if (language === "rust") {
21373
+ additionalSanitizers.push(...findRustSetAllowlistGuardSanitizers(code));
21374
+ additionalSanitizers.push(...findRustCanonicalizeGuardSanitizers(code));
21375
+ }
21310
21376
  attachSourceLineCode(additionalSources, additionalSinks, code);
21311
21377
  return { additionalSources, additionalSinks, additionalSanitizers, pyTaintedVars, pySanitizedVars, jsTaintedVars };
21312
21378
  }
@@ -22421,6 +22487,210 @@ function findGoHtmlTemplateImportSanitizers(code) {
22421
22487
  }
22422
22488
  return sanitizers;
22423
22489
  }
22490
+ function findPythonNetlocAllowlistGuardSanitizers(code) {
22491
+ const sanitizers = [];
22492
+ const lines = code.split(`
22493
+ `);
22494
+ const guardOpen = /^(\s*)if\s+.+?\s+not\s+in\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*$/;
22495
+ const allowlistName = /^(?:[A-Z][A-Z0-9_]+|.*?(allowed|accepted|whitelist|permitted|valid|approved).*)$/i;
22496
+ const terminator = /\b(return|raise|abort\s*\(|sys\.exit\s*\()/;
22497
+ for (let i2 = 0;i2 < lines.length; i2++) {
22498
+ const m = guardOpen.exec(lines[i2]);
22499
+ if (!m)
22500
+ continue;
22501
+ const guardIndent = m[1].length;
22502
+ const allowName = m[2];
22503
+ if (!allowlistName.test(allowName))
22504
+ continue;
22505
+ let bodyHasTerminator = false;
22506
+ let blockEnd = -1;
22507
+ const maxScan = Math.min(lines.length, i2 + 26);
22508
+ for (let j = i2 + 1;j < maxScan; j++) {
22509
+ const line = lines[j];
22510
+ if (line.trim() === "")
22511
+ continue;
22512
+ const indent = line.length - line.trimStart().length;
22513
+ if (indent <= guardIndent) {
22514
+ blockEnd = j - 1;
22515
+ break;
22516
+ }
22517
+ if (terminator.test(line))
22518
+ bodyHasTerminator = true;
22519
+ }
22520
+ if (blockEnd === -1)
22521
+ blockEnd = Math.min(lines.length - 1, i2 + 25);
22522
+ if (!bodyHasTerminator)
22523
+ continue;
22524
+ for (let l = blockEnd + 2;l <= lines.length; l++) {
22525
+ sanitizers.push({
22526
+ type: "python_netloc_allowlist_guard",
22527
+ method: "if",
22528
+ line: l,
22529
+ sanitizes: [
22530
+ "open_redirect",
22531
+ "ssrf",
22532
+ "path_traversal",
22533
+ "external_taint_escape"
22534
+ ]
22535
+ });
22536
+ }
22537
+ }
22538
+ return sanitizers;
22539
+ }
22540
+ function findPythonRangeCheckGuardSanitizers(code) {
22541
+ const sanitizers = [];
22542
+ const lines = code.split(`
22543
+ `);
22544
+ const rangeGuard = /^(\s*)if\s+([A-Za-z_][A-Za-z0-9_]*)\s*[<>]=?\s*(?:\d+|-?\d+\.?\d*|[A-Z][A-Z0-9_]+)\s*(?:(?:or|and)\s+\2\s*[<>]=?\s*(?:\d+|-?\d+\.?\d*|[A-Z][A-Z0-9_]+)\s*)?:\s*$/;
22545
+ const terminator = /\b(return|raise|abort\s*\(|sys\.exit\s*\()/;
22546
+ for (let i2 = 0;i2 < lines.length; i2++) {
22547
+ const m = rangeGuard.exec(lines[i2]);
22548
+ if (!m)
22549
+ continue;
22550
+ const guardIndent = m[1].length;
22551
+ let bodyHasTerminator = false;
22552
+ let blockEnd = -1;
22553
+ const maxScan = Math.min(lines.length, i2 + 26);
22554
+ for (let j = i2 + 1;j < maxScan; j++) {
22555
+ const line = lines[j];
22556
+ if (line.trim() === "")
22557
+ continue;
22558
+ const indent = line.length - line.trimStart().length;
22559
+ if (indent <= guardIndent) {
22560
+ blockEnd = j - 1;
22561
+ break;
22562
+ }
22563
+ if (terminator.test(line))
22564
+ bodyHasTerminator = true;
22565
+ }
22566
+ if (blockEnd === -1)
22567
+ blockEnd = Math.min(lines.length - 1, i2 + 25);
22568
+ if (!bodyHasTerminator)
22569
+ continue;
22570
+ for (let l = blockEnd + 2;l <= lines.length; l++) {
22571
+ sanitizers.push({
22572
+ type: "python_range_check_guard",
22573
+ method: "if",
22574
+ line: l,
22575
+ sanitizes: ["xss", "external_taint_escape"]
22576
+ });
22577
+ }
22578
+ }
22579
+ return sanitizers;
22580
+ }
22581
+ function findRustSetAllowlistGuardSanitizers(code) {
22582
+ const sanitizers = [];
22583
+ const lines = code.split(`
22584
+ `);
22585
+ const guardOpen = /^\s*if\s+!\s*([A-Za-z_][A-Za-z0-9_]*)\s*\.\s*(?:contains|contains_key)\s*\(/;
22586
+ const allowlistName = /^(?:[A-Z][A-Z0-9_]+|.*?(allowed|accepted|whitelist|permitted|valid|approved).*)$/i;
22587
+ const terminator = /\b(return|Err\s*\(|panic!\s*\(|HttpResponse::(?:Forbidden|BadRequest|Unauthorized))/;
22588
+ for (let i2 = 0;i2 < lines.length; i2++) {
22589
+ const m = guardOpen.exec(lines[i2]);
22590
+ if (!m)
22591
+ continue;
22592
+ const setName = m[1];
22593
+ if (!allowlistName.test(setName))
22594
+ continue;
22595
+ let depth = 0;
22596
+ for (const ch of lines[i2]) {
22597
+ if (ch === "{")
22598
+ depth++;
22599
+ else if (ch === "}")
22600
+ depth--;
22601
+ }
22602
+ if (depth <= 0)
22603
+ continue;
22604
+ let closeLine = -1;
22605
+ let bodyHasTerminator = false;
22606
+ const maxScan = Math.min(lines.length, i2 + 26);
22607
+ for (let j = i2 + 1;j < maxScan; j++) {
22608
+ const line = lines[j];
22609
+ if (terminator.test(line))
22610
+ bodyHasTerminator = true;
22611
+ for (const ch of line) {
22612
+ if (ch === "{")
22613
+ depth++;
22614
+ else if (ch === "}")
22615
+ depth--;
22616
+ }
22617
+ if (depth === 0) {
22618
+ closeLine = j;
22619
+ break;
22620
+ }
22621
+ }
22622
+ if (closeLine === -1 || !bodyHasTerminator)
22623
+ continue;
22624
+ for (let l = closeLine + 2;l <= lines.length; l++) {
22625
+ sanitizers.push({
22626
+ type: "rust_set_allowlist_guard",
22627
+ method: "if",
22628
+ line: l,
22629
+ sanitizes: [
22630
+ "ssrf",
22631
+ "open_redirect",
22632
+ "command_injection",
22633
+ "external_taint_escape"
22634
+ ]
22635
+ });
22636
+ }
22637
+ }
22638
+ return sanitizers;
22639
+ }
22640
+ function findRustCanonicalizeGuardSanitizers(code) {
22641
+ const sanitizers = [];
22642
+ const lines = code.split(`
22643
+ `);
22644
+ const guardOpen = /^\s*if\s+!\s*[A-Za-z_][\w?.()&]*\.starts_with\s*\(/;
22645
+ const terminator = /\b(return|Err\s*\(|panic!\s*\(|HttpResponse::(?:Forbidden|BadRequest|Unauthorized|NotFound))/;
22646
+ for (let i2 = 0;i2 < lines.length; i2++) {
22647
+ if (!guardOpen.test(lines[i2]))
22648
+ continue;
22649
+ let depth = 0;
22650
+ for (const ch of lines[i2]) {
22651
+ if (ch === "{")
22652
+ depth++;
22653
+ else if (ch === "}")
22654
+ depth--;
22655
+ }
22656
+ if (depth <= 0)
22657
+ continue;
22658
+ let closeLine = -1;
22659
+ let bodyHasTerminator = false;
22660
+ const maxScan = Math.min(lines.length, i2 + 26);
22661
+ for (let j = i2 + 1;j < maxScan; j++) {
22662
+ const line = lines[j];
22663
+ if (terminator.test(line))
22664
+ bodyHasTerminator = true;
22665
+ for (const ch of line) {
22666
+ if (ch === "{")
22667
+ depth++;
22668
+ else if (ch === "}")
22669
+ depth--;
22670
+ }
22671
+ if (depth === 0) {
22672
+ closeLine = j;
22673
+ break;
22674
+ }
22675
+ }
22676
+ if (closeLine === -1 || !bodyHasTerminator)
22677
+ continue;
22678
+ for (let l = closeLine + 2;l <= lines.length; l++) {
22679
+ sanitizers.push({
22680
+ type: "rust_canonicalize_guard",
22681
+ method: "if",
22682
+ line: l,
22683
+ sanitizes: [
22684
+ "path_traversal",
22685
+ "xss",
22686
+ "ssrf",
22687
+ "external_taint_escape"
22688
+ ]
22689
+ });
22690
+ }
22691
+ }
22692
+ return sanitizers;
22693
+ }
22424
22694
 
22425
22695
  // ../circle-ir/dist/analysis/passes/sink-filter-pass.js
22426
22696
  var JS_XSS_SANITIZERS = [
@@ -33586,7 +33856,7 @@ var colors = {
33586
33856
  };
33587
33857
 
33588
33858
  // src/version.ts
33589
- var version = "3.83.0";
33859
+ var version = "3.84.0";
33590
33860
 
33591
33861
  // src/formatters.ts
33592
33862
  var SINK_SEVERITY = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "3.83.0",
3
+ "version": "3.84.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.83.0"
68
+ "circle-ir": "^3.84.0"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^25.5.0",