cognium-dev 3.145.0 → 3.146.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 +151 -2
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -22868,6 +22868,8 @@ class LanguageSourcesPass {
22868
22868
  additionalSanitizers.push(...findJsSsrfAllowlistGuardSanitizers(code));
22869
22869
  additionalSanitizers.push(...findJsArgvFormExecSanitizers(code));
22870
22870
  additionalSanitizers.push(...findJsParameterizedSqlSanitizers(code));
22871
+ additionalSanitizers.push(...findJsPathResolveStartsWithGuardSanitizers(code));
22872
+ additionalSanitizers.push(...findJsCommandAllowlistGuardSanitizers(code));
22871
22873
  for (const finding of findJsPatternFindings(code, graph.ir.meta.file)) {
22872
22874
  ctx.addFinding(finding);
22873
22875
  }
@@ -24987,6 +24989,126 @@ function findJsParameterizedSqlSanitizers(code) {
24987
24989
  }
24988
24990
  return sanitizers;
24989
24991
  }
24992
+ function findJsPathResolveStartsWithGuardSanitizers(code) {
24993
+ const sanitizers = [];
24994
+ const lines = code.split(`
24995
+ `);
24996
+ const resolveDeclRe = /\b(?:const|let|var)\s+([A-Za-z_]\w*)\s*=\s*(?:(?:node:)?path|nodePath|Path)\s*\.\s*(?:resolve|join)\s*\(\s*([A-Za-z_]\w*(?:\.\w+)*)\s*,/;
24997
+ const startsWithGuardRe = (varName, rootName) => new RegExp(`if\\s*\\(\\s*!\\s*${varName}\\s*\\.\\s*startsWith\\s*\\(\\s*${rootName}\\b`);
24998
+ const terminatorRe = /\b(?:throw|return|res\s*\.\s*status\s*\([^)]*\)\s*\.\s*(?:send|end|json))/;
24999
+ const candidates = [];
25000
+ for (let i2 = 0;i2 < lines.length; i2++) {
25001
+ const m = resolveDeclRe.exec(lines[i2]);
25002
+ if (!m)
25003
+ continue;
25004
+ candidates.push({ line: i2 + 1, fullVar: m[1], rootVar: m[2] });
25005
+ }
25006
+ if (candidates.length === 0)
25007
+ return sanitizers;
25008
+ for (const c of candidates) {
25009
+ const guardRe = startsWithGuardRe(c.fullVar, c.rootVar);
25010
+ let guardLine = -1;
25011
+ for (let l = c.line;l < Math.min(lines.length, c.line + 6); l++) {
25012
+ if (!guardRe.test(lines[l]))
25013
+ continue;
25014
+ if (terminatorRe.test(lines[l]) || l + 1 < lines.length && terminatorRe.test(lines[l + 1])) {
25015
+ guardLine = l + 1;
25016
+ break;
25017
+ }
25018
+ }
25019
+ if (guardLine < 0)
25020
+ continue;
25021
+ const varRefRe = new RegExp(`\\b${c.fullVar}\\b`);
25022
+ sanitizers.push({
25023
+ type: "js_path_resolve_startswith_guard",
25024
+ method: "startsWith",
25025
+ line: c.line,
25026
+ sanitizes: ["path_traversal", "external_taint_escape"]
25027
+ });
25028
+ for (let l = c.line;l < lines.length; l++) {
25029
+ if (!varRefRe.test(lines[l]))
25030
+ continue;
25031
+ sanitizers.push({
25032
+ type: "js_path_resolve_startswith_guard",
25033
+ method: "startsWith",
25034
+ line: l + 1,
25035
+ sanitizes: ["path_traversal", "external_taint_escape"]
25036
+ });
25037
+ }
25038
+ }
25039
+ return sanitizers;
25040
+ }
25041
+ function findJsCommandAllowlistGuardSanitizers(code) {
25042
+ const sanitizers = [];
25043
+ const lines = code.split(`
25044
+ `);
25045
+ const shoutyCase = /^[A-Z][A-Z0-9_]+$/;
25046
+ const allowlistTokens = /(?:allowed|accepted|whitelist|permitted|valid|approved|cmds|commands|tools)/i;
25047
+ const isAllowlistName = (n) => shoutyCase.test(n) || allowlistTokens.test(n);
25048
+ const guardHas = /\bif\s*\(\s*!\s*([A-Za-z_]\w*)\s*\.\s*(?:has|includes)\s*\(\s*([A-Za-z_]\w*)\s*\)\s*\)/;
25049
+ const guardIndexOf = /\bif\s*\(\s*([A-Za-z_]\w*)\s*\.\s*indexOf\s*\(\s*([A-Za-z_]\w*)\s*\)\s*(?:<\s*0|===\s*-1|==\s*-1)\s*\)/;
25050
+ const terminator = /\b(?:return|throw|res\s*\.\s*status\s*\([^)]*\)\s*\.\s*(?:send|end|json))/;
25051
+ for (let i2 = 0;i2 < lines.length; i2++) {
25052
+ const line = lines[i2];
25053
+ let m = guardHas.exec(line);
25054
+ let allow = null;
25055
+ let guardedVar = null;
25056
+ if (m) {
25057
+ allow = m[1];
25058
+ guardedVar = m[2];
25059
+ } else {
25060
+ m = guardIndexOf.exec(line);
25061
+ if (m) {
25062
+ allow = m[1];
25063
+ guardedVar = m[2];
25064
+ }
25065
+ }
25066
+ if (!allow || !guardedVar)
25067
+ continue;
25068
+ if (!isAllowlistName(allow))
25069
+ continue;
25070
+ let bodyHasTerminator = terminator.test(line);
25071
+ let blockEnd = i2;
25072
+ if (!bodyHasTerminator) {
25073
+ let braceDepth = 0;
25074
+ let started = false;
25075
+ const maxScan = Math.min(lines.length, i2 + 26);
25076
+ for (let j = i2;j < maxScan; j++) {
25077
+ const ln = lines[j];
25078
+ for (const ch of ln) {
25079
+ if (ch === "{") {
25080
+ braceDepth++;
25081
+ started = true;
25082
+ } else if (ch === "}") {
25083
+ braceDepth--;
25084
+ if (started && braceDepth === 0) {
25085
+ blockEnd = j;
25086
+ break;
25087
+ }
25088
+ }
25089
+ }
25090
+ if (started && j > i2 && terminator.test(ln))
25091
+ bodyHasTerminator = true;
25092
+ if (started && braceDepth === 0 && blockEnd !== i2)
25093
+ break;
25094
+ }
25095
+ if (!started || !bodyHasTerminator)
25096
+ continue;
25097
+ }
25098
+ const varRefRe = new RegExp(`\\b${guardedVar}\\b`);
25099
+ for (let l = blockEnd + 1;l < lines.length; l++) {
25100
+ if (!varRefRe.test(lines[l]))
25101
+ continue;
25102
+ sanitizers.push({
25103
+ type: "js_command_allowlist_guard",
25104
+ method: "if",
25105
+ line: l + 1,
25106
+ sanitizes: ["command_injection", "external_taint_escape"]
25107
+ });
25108
+ }
25109
+ }
25110
+ return sanitizers;
25111
+ }
24990
25112
  function findJavaPathNormalizeStartsWithGuardSanitizers(code) {
24991
25113
  const sanitizers = [];
24992
25114
  const lines = code.split(`
@@ -28756,6 +28878,16 @@ var COMPILED_TEMPLATE_TYPES = new Set([
28756
28878
  "VelocityTemplate",
28757
28879
  "BeetlTemplate"
28758
28880
  ]);
28881
+ var TEMPLATE_ENGINE_LITERAL_TARGETS = new Set([
28882
+ "VelocityEngine",
28883
+ "Velocity",
28884
+ "TemplateEngine",
28885
+ "SpringTemplateEngine",
28886
+ "Configuration",
28887
+ "PebbleEngine",
28888
+ "Pebble",
28889
+ "Handlebars"
28890
+ ]);
28759
28891
  var REFLECTION_LITERAL_METHODS = new Set([
28760
28892
  "forName",
28761
28893
  "loadClass",
@@ -28788,7 +28920,7 @@ var PROTOCOL_CLIENT_PACKAGES = [
28788
28920
  ];
28789
28921
  var OS_EXEC_RECEIVER_RE = /\b(?:Runtime|ProcessBuilder|DefaultExecutor|Executor|Exec|Launcher|ProcStarter|ProcessExecutor|RuntimeUtil)\s*[.(]/;
28790
28922
  var PROCESS_BUILDER_ARGV_FORM_RE = /\bnew\s+ProcessBuilder\s*\(\s*(?:Arrays\.asList\b|List\.of\b|Collections\.singletonList\b|new\s+ArrayList\b|new\s+String\s*\[\s*\]\s*\{|"[^"]*"\s*,)/;
28791
- var JAVA_THROW_STATEMENT_RE = /^\s*throw\s+new\s+\w+(?:Exception|Error)\b/;
28923
+ var JAVA_THROW_STATEMENT_RE = /^\s*throw\s+new\s+\w+(?:Exception|Error)\b[^;]*;\s*(?:\/\/.*|\/\*.*)?$/;
28792
28924
  var JEXL_ENGINE_TYPES = new Set(["JexlEngine", "Jexl", "JxltEngine"]);
28793
28925
  var JEXL_EXPRESSION_TYPE_RE = /(?:Jexl)?(?:Expression|Script|Template)(?:Script)?$/;
28794
28926
  var TEMPLATE_COMPILE_RECEIVER_TYPES = new Set([
@@ -29214,6 +29346,23 @@ class SinkFilterPass {
29214
29346
  const recvType = resolveJavaReceiverType(receiver, sink.line, sourceLines);
29215
29347
  if (recvType && COMPILED_TEMPLATE_TYPES.has(recvType))
29216
29348
  return false;
29349
+ if (recvType && TEMPLATE_ENGINE_LITERAL_TARGETS.has(recvType)) {
29350
+ const args2 = extractJavaCallArgs(method, sinkLineText);
29351
+ if (args2 !== null && args2.length >= 1 && isJavaLiteralOrAnnotationAccessor(args2[0])) {
29352
+ return false;
29353
+ }
29354
+ }
29355
+ }
29356
+ if (method === "evaluate" && receiver) {
29357
+ const recvType = resolveJavaReceiverType(receiver, sink.line, sourceLines);
29358
+ if (recvType && TEMPLATE_ENGINE_LITERAL_TARGETS.has(recvType)) {
29359
+ const args2 = extractJavaCallArgs(method, sinkLineText);
29360
+ if (args2 !== null && args2.length >= 1) {
29361
+ const last = args2[args2.length - 1] ?? "";
29362
+ if (isJavaLiteralOrAnnotationAccessor(last))
29363
+ return false;
29364
+ }
29365
+ }
29217
29366
  }
29218
29367
  if (method && REFLECTION_LITERAL_METHODS.has(method)) {
29219
29368
  const args2 = extractJavaCallArgs(method, sinkLineText);
@@ -42081,7 +42230,7 @@ var colors = {
42081
42230
  };
42082
42231
 
42083
42232
  // src/version.ts
42084
- var version = "3.145.0";
42233
+ var version = "3.146.0";
42085
42234
 
42086
42235
  // src/formatters.ts
42087
42236
  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.145.0",
3
+ "version": "3.146.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.145.0"
69
+ "circle-ir": "^3.146.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",