cognium-dev 3.145.0 → 3.147.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 +183 -2
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -12318,6 +12318,38 @@ function isSafeGoExecCommandCall(call, pattern, language) {
12318
12318
  ]);
12319
12319
  if (SHELL_PROGRAMS.has(program))
12320
12320
  return false;
12321
+ const PACKAGE_MANAGER_EXEC_SUBCOMMANDS = {
12322
+ go: new Set(["install", "run", "get"]),
12323
+ npm: new Set(["install", "i", "exec"]),
12324
+ npx: new Set(["*"]),
12325
+ pip: new Set(["install"]),
12326
+ pip3: new Set(["install"]),
12327
+ gem: new Set(["install"]),
12328
+ cargo: new Set(["install", "run"]),
12329
+ yarn: new Set(["add", "install", "exec", "dlx"]),
12330
+ pnpm: new Set(["add", "install", "exec", "dlx"])
12331
+ };
12332
+ const pmSubcommands = PACKAGE_MANAGER_EXEC_SUBCOMMANDS[program];
12333
+ if (pmSubcommands !== undefined) {
12334
+ const subcommandArgPos = pattern.method === "CommandContext" ? 2 : 1;
12335
+ const subcommandArg = call.arguments.find((a) => a.position === subcommandArgPos);
12336
+ if (!subcommandArg)
12337
+ return false;
12338
+ let subcommand;
12339
+ if (subcommandArg.literal !== null && subcommandArg.literal !== undefined) {
12340
+ subcommand = String(subcommandArg.literal);
12341
+ } else {
12342
+ const expr = (subcommandArg.expression ?? "").trim();
12343
+ if (expr.startsWith('"') || expr.startsWith("`") || expr.startsWith("'")) {
12344
+ subcommand = expr.slice(1, -1);
12345
+ }
12346
+ }
12347
+ if (subcommand === undefined)
12348
+ return false;
12349
+ if (pmSubcommands.has("*") || pmSubcommands.has(subcommand)) {
12350
+ return false;
12351
+ }
12352
+ }
12321
12353
  return true;
12322
12354
  }
12323
12355
  function isSafeJSChildProcessCall(call, pattern, language) {
@@ -22868,6 +22900,8 @@ class LanguageSourcesPass {
22868
22900
  additionalSanitizers.push(...findJsSsrfAllowlistGuardSanitizers(code));
22869
22901
  additionalSanitizers.push(...findJsArgvFormExecSanitizers(code));
22870
22902
  additionalSanitizers.push(...findJsParameterizedSqlSanitizers(code));
22903
+ additionalSanitizers.push(...findJsPathResolveStartsWithGuardSanitizers(code));
22904
+ additionalSanitizers.push(...findJsCommandAllowlistGuardSanitizers(code));
22871
22905
  for (const finding of findJsPatternFindings(code, graph.ir.meta.file)) {
22872
22906
  ctx.addFinding(finding);
22873
22907
  }
@@ -24987,6 +25021,126 @@ function findJsParameterizedSqlSanitizers(code) {
24987
25021
  }
24988
25022
  return sanitizers;
24989
25023
  }
25024
+ function findJsPathResolveStartsWithGuardSanitizers(code) {
25025
+ const sanitizers = [];
25026
+ const lines = code.split(`
25027
+ `);
25028
+ 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*,/;
25029
+ const startsWithGuardRe = (varName, rootName) => new RegExp(`if\\s*\\(\\s*!\\s*${varName}\\s*\\.\\s*startsWith\\s*\\(\\s*${rootName}\\b`);
25030
+ const terminatorRe = /\b(?:throw|return|res\s*\.\s*status\s*\([^)]*\)\s*\.\s*(?:send|end|json))/;
25031
+ const candidates = [];
25032
+ for (let i2 = 0;i2 < lines.length; i2++) {
25033
+ const m = resolveDeclRe.exec(lines[i2]);
25034
+ if (!m)
25035
+ continue;
25036
+ candidates.push({ line: i2 + 1, fullVar: m[1], rootVar: m[2] });
25037
+ }
25038
+ if (candidates.length === 0)
25039
+ return sanitizers;
25040
+ for (const c of candidates) {
25041
+ const guardRe = startsWithGuardRe(c.fullVar, c.rootVar);
25042
+ let guardLine = -1;
25043
+ for (let l = c.line;l < Math.min(lines.length, c.line + 6); l++) {
25044
+ if (!guardRe.test(lines[l]))
25045
+ continue;
25046
+ if (terminatorRe.test(lines[l]) || l + 1 < lines.length && terminatorRe.test(lines[l + 1])) {
25047
+ guardLine = l + 1;
25048
+ break;
25049
+ }
25050
+ }
25051
+ if (guardLine < 0)
25052
+ continue;
25053
+ const varRefRe = new RegExp(`\\b${c.fullVar}\\b`);
25054
+ sanitizers.push({
25055
+ type: "js_path_resolve_startswith_guard",
25056
+ method: "startsWith",
25057
+ line: c.line,
25058
+ sanitizes: ["path_traversal", "external_taint_escape"]
25059
+ });
25060
+ for (let l = c.line;l < lines.length; l++) {
25061
+ if (!varRefRe.test(lines[l]))
25062
+ continue;
25063
+ sanitizers.push({
25064
+ type: "js_path_resolve_startswith_guard",
25065
+ method: "startsWith",
25066
+ line: l + 1,
25067
+ sanitizes: ["path_traversal", "external_taint_escape"]
25068
+ });
25069
+ }
25070
+ }
25071
+ return sanitizers;
25072
+ }
25073
+ function findJsCommandAllowlistGuardSanitizers(code) {
25074
+ const sanitizers = [];
25075
+ const lines = code.split(`
25076
+ `);
25077
+ const shoutyCase = /^[A-Z][A-Z0-9_]+$/;
25078
+ const allowlistTokens = /(?:allowed|accepted|whitelist|permitted|valid|approved|cmds|commands|tools)/i;
25079
+ const isAllowlistName = (n) => shoutyCase.test(n) || allowlistTokens.test(n);
25080
+ const guardHas = /\bif\s*\(\s*!\s*([A-Za-z_]\w*)\s*\.\s*(?:has|includes)\s*\(\s*([A-Za-z_]\w*)\s*\)\s*\)/;
25081
+ 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*\)/;
25082
+ const terminator = /\b(?:return|throw|res\s*\.\s*status\s*\([^)]*\)\s*\.\s*(?:send|end|json))/;
25083
+ for (let i2 = 0;i2 < lines.length; i2++) {
25084
+ const line = lines[i2];
25085
+ let m = guardHas.exec(line);
25086
+ let allow = null;
25087
+ let guardedVar = null;
25088
+ if (m) {
25089
+ allow = m[1];
25090
+ guardedVar = m[2];
25091
+ } else {
25092
+ m = guardIndexOf.exec(line);
25093
+ if (m) {
25094
+ allow = m[1];
25095
+ guardedVar = m[2];
25096
+ }
25097
+ }
25098
+ if (!allow || !guardedVar)
25099
+ continue;
25100
+ if (!isAllowlistName(allow))
25101
+ continue;
25102
+ let bodyHasTerminator = terminator.test(line);
25103
+ let blockEnd = i2;
25104
+ if (!bodyHasTerminator) {
25105
+ let braceDepth = 0;
25106
+ let started = false;
25107
+ const maxScan = Math.min(lines.length, i2 + 26);
25108
+ for (let j = i2;j < maxScan; j++) {
25109
+ const ln = lines[j];
25110
+ for (const ch of ln) {
25111
+ if (ch === "{") {
25112
+ braceDepth++;
25113
+ started = true;
25114
+ } else if (ch === "}") {
25115
+ braceDepth--;
25116
+ if (started && braceDepth === 0) {
25117
+ blockEnd = j;
25118
+ break;
25119
+ }
25120
+ }
25121
+ }
25122
+ if (started && j > i2 && terminator.test(ln))
25123
+ bodyHasTerminator = true;
25124
+ if (started && braceDepth === 0 && blockEnd !== i2)
25125
+ break;
25126
+ }
25127
+ if (!started || !bodyHasTerminator)
25128
+ continue;
25129
+ }
25130
+ const varRefRe = new RegExp(`\\b${guardedVar}\\b`);
25131
+ for (let l = blockEnd + 1;l < lines.length; l++) {
25132
+ if (!varRefRe.test(lines[l]))
25133
+ continue;
25134
+ sanitizers.push({
25135
+ type: "js_command_allowlist_guard",
25136
+ method: "if",
25137
+ line: l + 1,
25138
+ sanitizes: ["command_injection", "external_taint_escape"]
25139
+ });
25140
+ }
25141
+ }
25142
+ return sanitizers;
25143
+ }
24990
25144
  function findJavaPathNormalizeStartsWithGuardSanitizers(code) {
24991
25145
  const sanitizers = [];
24992
25146
  const lines = code.split(`
@@ -28756,6 +28910,16 @@ var COMPILED_TEMPLATE_TYPES = new Set([
28756
28910
  "VelocityTemplate",
28757
28911
  "BeetlTemplate"
28758
28912
  ]);
28913
+ var TEMPLATE_ENGINE_LITERAL_TARGETS = new Set([
28914
+ "VelocityEngine",
28915
+ "Velocity",
28916
+ "TemplateEngine",
28917
+ "SpringTemplateEngine",
28918
+ "Configuration",
28919
+ "PebbleEngine",
28920
+ "Pebble",
28921
+ "Handlebars"
28922
+ ]);
28759
28923
  var REFLECTION_LITERAL_METHODS = new Set([
28760
28924
  "forName",
28761
28925
  "loadClass",
@@ -28788,7 +28952,7 @@ var PROTOCOL_CLIENT_PACKAGES = [
28788
28952
  ];
28789
28953
  var OS_EXEC_RECEIVER_RE = /\b(?:Runtime|ProcessBuilder|DefaultExecutor|Executor|Exec|Launcher|ProcStarter|ProcessExecutor|RuntimeUtil)\s*[.(]/;
28790
28954
  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/;
28955
+ var JAVA_THROW_STATEMENT_RE = /^\s*throw\s+new\s+\w+(?:Exception|Error)\b[^;]*;\s*(?:\/\/.*|\/\*.*)?$/;
28792
28956
  var JEXL_ENGINE_TYPES = new Set(["JexlEngine", "Jexl", "JxltEngine"]);
28793
28957
  var JEXL_EXPRESSION_TYPE_RE = /(?:Jexl)?(?:Expression|Script|Template)(?:Script)?$/;
28794
28958
  var TEMPLATE_COMPILE_RECEIVER_TYPES = new Set([
@@ -29214,6 +29378,23 @@ class SinkFilterPass {
29214
29378
  const recvType = resolveJavaReceiverType(receiver, sink.line, sourceLines);
29215
29379
  if (recvType && COMPILED_TEMPLATE_TYPES.has(recvType))
29216
29380
  return false;
29381
+ if (recvType && TEMPLATE_ENGINE_LITERAL_TARGETS.has(recvType)) {
29382
+ const args2 = extractJavaCallArgs(method, sinkLineText);
29383
+ if (args2 !== null && args2.length >= 1 && isJavaLiteralOrAnnotationAccessor(args2[0])) {
29384
+ return false;
29385
+ }
29386
+ }
29387
+ }
29388
+ if (method === "evaluate" && receiver) {
29389
+ const recvType = resolveJavaReceiverType(receiver, sink.line, sourceLines);
29390
+ if (recvType && TEMPLATE_ENGINE_LITERAL_TARGETS.has(recvType)) {
29391
+ const args2 = extractJavaCallArgs(method, sinkLineText);
29392
+ if (args2 !== null && args2.length >= 1) {
29393
+ const last = args2[args2.length - 1] ?? "";
29394
+ if (isJavaLiteralOrAnnotationAccessor(last))
29395
+ return false;
29396
+ }
29397
+ }
29217
29398
  }
29218
29399
  if (method && REFLECTION_LITERAL_METHODS.has(method)) {
29219
29400
  const args2 = extractJavaCallArgs(method, sinkLineText);
@@ -42081,7 +42262,7 @@ var colors = {
42081
42262
  };
42082
42263
 
42083
42264
  // src/version.ts
42084
- var version = "3.145.0";
42265
+ var version = "3.147.0";
42085
42266
 
42086
42267
  // src/formatters.ts
42087
42268
  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.147.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.147.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",