cognium-dev 3.124.0 → 3.126.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 +343 -1
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -22604,6 +22604,9 @@ class LanguageSourcesPass {
22604
22604
  if (language === "python") {
22605
22605
  additionalSanitizers.push(...findPythonNetlocAllowlistGuardSanitizers(code));
22606
22606
  additionalSanitizers.push(...findPythonRangeCheckGuardSanitizers(code));
22607
+ additionalSanitizers.push(...findPythonRegexAllowlistWrapperSanitizers(code));
22608
+ additionalSanitizers.push(...findPythonSetMembershipXssGuardSanitizers(code));
22609
+ additionalSanitizers.push(...findPythonDefusedXmlSanitizers(code));
22607
22610
  const pyMisconfigFindings = findPythonPatternFindings(code, graph.ir.meta.file);
22608
22611
  for (const finding of pyMisconfigFindings) {
22609
22612
  ctx.addFinding(finding);
@@ -22622,6 +22625,7 @@ class LanguageSourcesPass {
22622
22625
  additionalSanitizers.push(...findJsCryptoHashSanitizers(code));
22623
22626
  additionalSanitizers.push(...findJsCsvFormulaPrefixSanitizers(code));
22624
22627
  additionalSanitizers.push(...findJsWrapperFunctionSanitizers(code));
22628
+ additionalSanitizers.push(...findJsSsrfAllowlistGuardSanitizers(code));
22625
22629
  }
22626
22630
  if (language === "java") {
22627
22631
  additionalSanitizers.push(...findJavaSafeJsonParseSanitizers(code));
@@ -24008,6 +24012,252 @@ function findPythonRangeCheckGuardSanitizers(code) {
24008
24012
  }
24009
24013
  return sanitizers;
24010
24014
  }
24015
+ function findPythonRegexAllowlistWrapperSanitizers(code) {
24016
+ const sanitizers = [];
24017
+ const lines = code.split(`
24018
+ `);
24019
+ const defOpen = /^(\s*)def\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)\s*:\s*$/;
24020
+ const tightRegex = /r?["'](?:\^)?(?:\\w[+*]|\\d[+*]|\[[A-Za-z0-9_\\\-\.\s]+\][+*]|[A-Za-z0-9_\-\.]+)(?:\$)?["']/;
24021
+ const terminator = /\b(return\s+(?:None|"|'|\(|\[|\{|False|jsonify|make_response|redirect|abort)|raise\s+\w|abort\s*\(|sys\.exit\s*\()/;
24022
+ const wrappers = [];
24023
+ for (let i2 = 0;i2 < lines.length; i2++) {
24024
+ const m = defOpen.exec(lines[i2]);
24025
+ if (!m)
24026
+ continue;
24027
+ const defIndent = m[1].length;
24028
+ const wrapperName = m[2];
24029
+ const argName = m[3];
24030
+ let foundGuard = false;
24031
+ let returnsArg = false;
24032
+ let blockEnd = -1;
24033
+ const maxScan = Math.min(lines.length, i2 + 40);
24034
+ for (let j = i2 + 1;j < maxScan; j++) {
24035
+ const line = lines[j];
24036
+ if (line.trim() === "")
24037
+ continue;
24038
+ const indent = line.length - line.trimStart().length;
24039
+ if (indent <= defIndent) {
24040
+ blockEnd = j - 1;
24041
+ break;
24042
+ }
24043
+ if (!foundGuard) {
24044
+ const guard = new RegExp(`if\\s+not\\s+re\\.(?:fullmatch|match)\\s*\\(\\s*(${tightRegex.source})\\s*,\\s*${argName}\\s*\\)\\s*:`);
24045
+ if (guard.test(line)) {
24046
+ for (let k = j + 1;k < maxScan; k++) {
24047
+ const klin = lines[k];
24048
+ if (klin.trim() === "")
24049
+ continue;
24050
+ const kindent = klin.length - klin.trimStart().length;
24051
+ if (kindent <= indent)
24052
+ break;
24053
+ if (terminator.test(klin))
24054
+ foundGuard = true;
24055
+ break;
24056
+ }
24057
+ }
24058
+ }
24059
+ if (new RegExp(`^\\s*return\\s+${argName}\\s*$`).test(line)) {
24060
+ returnsArg = true;
24061
+ }
24062
+ }
24063
+ if (blockEnd === -1)
24064
+ blockEnd = Math.min(lines.length - 1, i2 + 39);
24065
+ if (!foundGuard || !returnsArg)
24066
+ continue;
24067
+ wrappers.push({ name: wrapperName, defLine: i2, defIndent });
24068
+ }
24069
+ if (wrappers.length === 0)
24070
+ return sanitizers;
24071
+ const wrapperKinds = [
24072
+ "ldap_injection",
24073
+ "xpath_injection",
24074
+ "sql_injection",
24075
+ "command_injection",
24076
+ "path_traversal",
24077
+ "xss",
24078
+ "external_taint_escape"
24079
+ ];
24080
+ for (const w of wrappers) {
24081
+ const assignedCallRe = new RegExp(`^(\\s*)([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*${w.name}\\s*\\(`);
24082
+ const bareCallRe = new RegExp(`\\b${w.name}\\s*\\(`);
24083
+ for (let i2 = 0;i2 < lines.length; i2++) {
24084
+ if (i2 === w.defLine)
24085
+ continue;
24086
+ const line = lines[i2];
24087
+ if (!bareCallRe.test(line))
24088
+ continue;
24089
+ if (i2 > w.defLine) {
24090
+ const lineIndent = line.length - line.trimStart().length;
24091
+ if (lineIndent > w.defIndent) {
24092
+ let stillInside = true;
24093
+ for (let k = w.defLine + 1;k < i2; k++) {
24094
+ const kline = lines[k];
24095
+ if (kline.trim() === "")
24096
+ continue;
24097
+ const kindent = kline.length - kline.trimStart().length;
24098
+ if (kindent <= w.defIndent) {
24099
+ stillInside = false;
24100
+ break;
24101
+ }
24102
+ }
24103
+ if (stillInside)
24104
+ continue;
24105
+ }
24106
+ }
24107
+ sanitizers.push({
24108
+ type: "python_regex_allowlist_wrapper",
24109
+ method: w.name,
24110
+ line: i2 + 1,
24111
+ sanitizes: [...wrapperKinds]
24112
+ });
24113
+ const am = assignedCallRe.exec(line);
24114
+ if (!am)
24115
+ continue;
24116
+ const callIndent = am[1].length;
24117
+ const validated = am[2];
24118
+ const validatedRe = new RegExp(`\\b${validated}\\b`);
24119
+ for (let j = i2 + 1;j < lines.length; j++) {
24120
+ const jline = lines[j];
24121
+ if (jline.trim() === "")
24122
+ continue;
24123
+ const jindent = jline.length - jline.trimStart().length;
24124
+ if (jindent < callIndent)
24125
+ break;
24126
+ if (validatedRe.test(jline)) {
24127
+ sanitizers.push({
24128
+ type: "python_regex_allowlist_wrapper",
24129
+ method: w.name,
24130
+ line: j + 1,
24131
+ sanitizes: [...wrapperKinds]
24132
+ });
24133
+ }
24134
+ }
24135
+ }
24136
+ }
24137
+ return sanitizers;
24138
+ }
24139
+ function findPythonSetMembershipXssGuardSanitizers(code) {
24140
+ const sanitizers = [];
24141
+ const lines = code.split(`
24142
+ `);
24143
+ const guardOpen = /^(\s*)if\s+([A-Za-z_][A-Za-z0-9_]*)\s+not\s+in\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*$/;
24144
+ const allowlistName = /^(?:[A-Z][A-Z0-9_]+|.*?(allowed|accepted|whitelist|permitted|valid|approved).*)$/i;
24145
+ const terminator = /\b(return|raise|abort\s*\(|sys\.exit\s*\()/;
24146
+ for (let i2 = 0;i2 < lines.length; i2++) {
24147
+ const m = guardOpen.exec(lines[i2]);
24148
+ if (!m)
24149
+ continue;
24150
+ const guardIndent = m[1].length;
24151
+ const guardedVar = m[2];
24152
+ const allowName = m[3];
24153
+ if (!allowlistName.test(allowName))
24154
+ continue;
24155
+ let bodyHasTerminator = false;
24156
+ let blockEnd = -1;
24157
+ const maxScan = Math.min(lines.length, i2 + 26);
24158
+ for (let j = i2 + 1;j < maxScan; j++) {
24159
+ const line = lines[j];
24160
+ if (line.trim() === "")
24161
+ continue;
24162
+ const indent = line.length - line.trimStart().length;
24163
+ if (indent <= guardIndent) {
24164
+ blockEnd = j - 1;
24165
+ break;
24166
+ }
24167
+ if (terminator.test(line))
24168
+ bodyHasTerminator = true;
24169
+ }
24170
+ if (blockEnd === -1)
24171
+ blockEnd = Math.min(lines.length - 1, i2 + 25);
24172
+ if (!bodyHasTerminator)
24173
+ continue;
24174
+ const varRe = new RegExp(`\\b${guardedVar}\\b`);
24175
+ for (let l = blockEnd + 2;l <= lines.length; l++) {
24176
+ const lineText = lines[l - 1];
24177
+ if (!varRe.test(lineText))
24178
+ continue;
24179
+ sanitizers.push({
24180
+ type: "python_set_membership_xss_guard",
24181
+ method: "if",
24182
+ line: l,
24183
+ sanitizes: ["xss", "external_taint_escape"]
24184
+ });
24185
+ }
24186
+ }
24187
+ return sanitizers;
24188
+ }
24189
+ function findPythonDefusedXmlSanitizers(code) {
24190
+ const sanitizers = [];
24191
+ const lines = code.split(`
24192
+ `);
24193
+ const moduleAliases = new Set;
24194
+ const fromNames = new Set;
24195
+ const importAs = /^\s*import\s+defusedxml(?:\.[A-Za-z_][A-Za-z0-9_.]*)?\s+as\s+([A-Za-z_][A-Za-z0-9_]*)\s*$/;
24196
+ const importBare = /^\s*import\s+defusedxml(?:\.([A-Za-z_][A-Za-z0-9_.]*))?\s*$/;
24197
+ const fromImport = /^\s*from\s+defusedxml(?:\.[A-Za-z_][A-Za-z0-9_.]*)?\s+import\s+(.+)$/;
24198
+ for (const raw of lines) {
24199
+ const line = raw.replace(/#.*$/, "");
24200
+ let m;
24201
+ if (m = importAs.exec(line)) {
24202
+ moduleAliases.add(m[1]);
24203
+ continue;
24204
+ }
24205
+ if (m = importBare.exec(line)) {
24206
+ if (m[1]) {
24207
+ moduleAliases.add(m[1].split(".").pop());
24208
+ }
24209
+ moduleAliases.add("defusedxml");
24210
+ continue;
24211
+ }
24212
+ if (m = fromImport.exec(line)) {
24213
+ const items = m[1].split(",");
24214
+ for (const item of items) {
24215
+ const trimmed = item.trim().replace(/[()\\]/g, "");
24216
+ if (!trimmed)
24217
+ continue;
24218
+ const asMatch = /^([A-Za-z_][A-Za-z0-9_]*)\s+as\s+([A-Za-z_][A-Za-z0-9_]*)$/.exec(trimmed);
24219
+ if (asMatch) {
24220
+ fromNames.add(asMatch[2]);
24221
+ } else if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(trimmed)) {
24222
+ fromNames.add(trimmed);
24223
+ }
24224
+ }
24225
+ }
24226
+ }
24227
+ if (moduleAliases.size === 0 && fromNames.size === 0)
24228
+ return sanitizers;
24229
+ for (let i2 = 0;i2 < lines.length; i2++) {
24230
+ const line = lines[i2];
24231
+ if (/^\s*(?:import|from)\s+/.test(line))
24232
+ continue;
24233
+ let matched = false;
24234
+ for (const alias of moduleAliases) {
24235
+ const re = new RegExp(`\\b${alias}\\s*\\.\\s*[A-Za-z_][A-Za-z0-9_]*\\s*\\(`);
24236
+ if (re.test(line)) {
24237
+ matched = true;
24238
+ break;
24239
+ }
24240
+ }
24241
+ if (!matched) {
24242
+ for (const name2 of fromNames) {
24243
+ const re = new RegExp(`\\b${name2}\\s*\\(`);
24244
+ if (re.test(line)) {
24245
+ matched = true;
24246
+ break;
24247
+ }
24248
+ }
24249
+ }
24250
+ if (!matched)
24251
+ continue;
24252
+ sanitizers.push({
24253
+ type: "python_defusedxml_import",
24254
+ method: "defusedxml",
24255
+ line: i2 + 1,
24256
+ sanitizes: ["xxe", "external_taint_escape"]
24257
+ });
24258
+ }
24259
+ return sanitizers;
24260
+ }
24011
24261
  function findRustSetAllowlistGuardSanitizers(code) {
24012
24262
  const sanitizers = [];
24013
24263
  const lines = code.split(`
@@ -24238,6 +24488,98 @@ function findJsWrapperFunctionSanitizers(code) {
24238
24488
  }
24239
24489
  return sanitizers;
24240
24490
  }
24491
+ function findJsSsrfAllowlistGuardSanitizers(code) {
24492
+ const sanitizers = [];
24493
+ const lines = code.split(`
24494
+ `);
24495
+ const urlAliasToHostAlias = new Map;
24496
+ const urlAliasDecl = /\b(?:const|let|var)\s+([A-Za-z_]\w*)\s*=\s*new\s+URL\s*\(\s*([A-Za-z_][\w.[\]'"`]*)\s*\)/;
24497
+ const hostAliasDecl = /\b(?:const|let|var)\s+([A-Za-z_]\w*)\s*=\s*([A-Za-z_]\w*)\.(?:hostname|host)\b/;
24498
+ const hostFromUrl = new Map;
24499
+ for (const line of lines) {
24500
+ const ua = urlAliasDecl.exec(line);
24501
+ if (ua)
24502
+ urlAliasToHostAlias.set(ua[1], ua[2]);
24503
+ const ha = hostAliasDecl.exec(line);
24504
+ if (ha)
24505
+ hostFromUrl.set(ha[1], ha[2]);
24506
+ }
24507
+ const allowlistName = /^(?:[A-Z][A-Z0-9_]+|.*?(allowed|accepted|whitelist|permitted|valid|approved).*)$/i;
24508
+ 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*\)/;
24509
+ 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*\)/;
24510
+ const terminator = /\b(return|throw|res\s*\.\s*status\s*\([^)]*\)\s*\.\s*(?:send|end|json)\s*\()/;
24511
+ for (let i2 = 0;i2 < lines.length; i2++) {
24512
+ const line = lines[i2];
24513
+ let m = guardHas.exec(line);
24514
+ let allow = null;
24515
+ let guardedVar = null;
24516
+ if (m) {
24517
+ allow = m[1];
24518
+ guardedVar = m[2];
24519
+ } else {
24520
+ m = guardIndexOf.exec(line);
24521
+ if (m) {
24522
+ allow = m[1];
24523
+ guardedVar = m[2];
24524
+ }
24525
+ }
24526
+ if (!allow || !guardedVar)
24527
+ continue;
24528
+ if (!allowlistName.test(allow))
24529
+ continue;
24530
+ let bodyHasTerminator = false;
24531
+ let blockEnd = -1;
24532
+ const maxScan = Math.min(lines.length, i2 + 26);
24533
+ let braceDepth = 0;
24534
+ let started = false;
24535
+ for (let j = i2;j < maxScan; j++) {
24536
+ const ln = lines[j];
24537
+ for (const ch of ln) {
24538
+ if (ch === "{") {
24539
+ braceDepth++;
24540
+ started = true;
24541
+ } else if (ch === "}") {
24542
+ braceDepth--;
24543
+ if (started && braceDepth === 0) {
24544
+ blockEnd = j;
24545
+ break;
24546
+ }
24547
+ }
24548
+ }
24549
+ if (started && j > i2 && terminator.test(ln))
24550
+ bodyHasTerminator = true;
24551
+ if (blockEnd !== -1)
24552
+ break;
24553
+ }
24554
+ if (!started)
24555
+ continue;
24556
+ if (blockEnd === -1)
24557
+ continue;
24558
+ if (!bodyHasTerminator)
24559
+ continue;
24560
+ const safeNames = new Set([guardedVar]);
24561
+ if (hostFromUrl.has(guardedVar)) {
24562
+ safeNames.add(hostFromUrl.get(guardedVar));
24563
+ }
24564
+ for (const [hostName, urlName] of hostFromUrl) {
24565
+ if (urlName === guardedVar)
24566
+ safeNames.add(hostName);
24567
+ }
24568
+ const refRes = [...safeNames].map((n) => new RegExp(`\\b${n}\\b`));
24569
+ for (let l = blockEnd + 2;l <= lines.length; l++) {
24570
+ const lineText = lines[l - 1];
24571
+ if (!refRes.some((re) => re.test(lineText)))
24572
+ continue;
24573
+ sanitizers.push({
24574
+ type: "js_ssrf_allowlist_guard",
24575
+ method: "if",
24576
+ line: l,
24577
+ sanitizes: ["ssrf", "external_taint_escape"]
24578
+ });
24579
+ }
24580
+ }
24581
+ return sanitizers;
24582
+ }
24241
24583
  function collectEnvSecretVars(lines, language) {
24242
24584
  const out2 = new Map;
24243
24585
  for (let i2 = 0;i2 < lines.length; i2++) {
@@ -37779,7 +38121,7 @@ var colors = {
37779
38121
  };
37780
38122
 
37781
38123
  // src/version.ts
37782
- var version = "3.124.0";
38124
+ var version = "3.126.0";
37783
38125
 
37784
38126
  // src/formatters.ts
37785
38127
  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.124.0",
3
+ "version": "3.126.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.124.0"
69
+ "circle-ir": "^3.126.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",