cognium-dev 4.11.0 → 4.12.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 +136 -12
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -9403,6 +9403,31 @@ function processStatement2(stmt, startDefId, startUseId, scopeStack, isJavaScrip
9403
9403
  }
9404
9404
  break;
9405
9405
  }
9406
+ case "switch_expression":
9407
+ case "switch_statement": {
9408
+ const value = stmt.childForFieldName("condition") ?? stmt.childForFieldName("value");
9409
+ if (value) {
9410
+ const valueUses = extractUses(value, useId, scopeStack, isJavaScript);
9411
+ uses.push(...valueUses.uses);
9412
+ useId = valueUses.nextId;
9413
+ }
9414
+ const body2 = stmt.childForFieldName("body");
9415
+ if (body2) {
9416
+ scopeStack.push(new Map);
9417
+ for (let i2 = 0;i2 < body2.childCount; i2++) {
9418
+ const group = body2.child(i2);
9419
+ if (!group)
9420
+ continue;
9421
+ const result = processBlock(group, defId, useId, scopeStack, isJavaScript);
9422
+ defs.push(...result.defs);
9423
+ uses.push(...result.uses);
9424
+ defId = result.nextDefId;
9425
+ useId = result.nextUseId;
9426
+ }
9427
+ scopeStack.pop();
9428
+ }
9429
+ break;
9430
+ }
9406
9431
  case "block":
9407
9432
  case "statement_block": {
9408
9433
  scopeStack.push(new Map);
@@ -11404,6 +11429,7 @@ var DEFAULT_SOURCES = [
11404
11429
  { method: "get", class: "ConcurrentHashMap", type: "plugin_param", severity: "high", return_tainted: true },
11405
11430
  { method: "get", class: "Hashtable", type: "plugin_param", severity: "high", return_tainted: true },
11406
11431
  { method: "get", class: "Properties", type: "config_param", severity: "high", return_tainted: true },
11432
+ { method: "getProperty", class: "Properties", type: "config_param", severity: "high", return_tainted: true },
11407
11433
  { method: "getText", class: "Message", type: "io_input", severity: "high", return_tainted: true },
11408
11434
  { method: "getPayload", type: "io_input", severity: "high", return_tainted: true },
11409
11435
  { method: "getData", type: "io_input", severity: "high", return_tainted: true },
@@ -11986,8 +12012,8 @@ var DEFAULT_SINKS = [
11986
12012
  { method: "write", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
11987
12013
  { method: "println", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
11988
12014
  { method: "print", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
11989
- { method: "format", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1] },
11990
- { method: "printf", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1] },
12015
+ { method: "format", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1, 2, 3, 4, 5, 6, 7] },
12016
+ { method: "printf", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1, 2, 3, 4, 5, 6, 7] },
11991
12017
  { method: "write", class: "ServletOutputStream", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
11992
12018
  { method: "print", class: "ServletOutputStream", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
11993
12019
  { method: "println", class: "ServletOutputStream", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
@@ -20183,9 +20209,18 @@ class ConstantPropagator {
20183
20209
  }
20184
20210
  }
20185
20211
  } else {
20212
+ const taintedBefore = new Set(this.tainted);
20213
+ const taintedAfter = new Set(taintedBefore);
20214
+ const wasInConditional = this.inConditionalBranch;
20215
+ this.inConditionalBranch = true;
20186
20216
  for (const caseGroup of caseGroups) {
20217
+ this.tainted = new Set(taintedBefore);
20187
20218
  this.visit(caseGroup);
20219
+ for (const v of this.tainted)
20220
+ taintedAfter.add(v);
20188
20221
  }
20222
+ this.inConditionalBranch = wasInConditional;
20223
+ this.tainted = taintedAfter;
20189
20224
  }
20190
20225
  }
20191
20226
  handleTernary(node) {
@@ -21260,7 +21295,11 @@ function annotationsInclude(annotations, targets) {
21260
21295
  return false;
21261
21296
  }
21262
21297
  function simpleTypeName(ref) {
21263
- return ref.replace(/<.*$/, "").trim();
21298
+ const name2 = ref.replace(/<.*$/, "").trim();
21299
+ return name2.substring(name2.lastIndexOf(".") + 1);
21300
+ }
21301
+ function isSupertypeLifecycleMethod(supertype, methodName) {
21302
+ return TIER_1_BY_SUPERTYPE.get(simpleTypeName(supertype))?.has(methodName) ?? false;
21264
21303
  }
21265
21304
  function looksLikeMainMethod(method) {
21266
21305
  if (method.name !== "main")
@@ -21822,7 +21861,7 @@ function applyRequireEntryPath(fileAnalyses, options = {}) {
21822
21861
  if (graph.methodsByKey.size === 0)
21823
21862
  return;
21824
21863
  const fileContext = buildFileContext(fileAnalyses);
21825
- const entryPointKeys = collectEntryPointKeys(graph, fileContext);
21864
+ const entryPointKeys = collectEntryPointKeys(graph, fileContext, indexTypesByName(fileAnalyses));
21826
21865
  const entryPointKeysByLanguage = countEntryPointKeysByLanguage(graph, entryPointKeys);
21827
21866
  const profileResolver = makeProfileResolver(options.projectProfile);
21828
21867
  for (const fa of fileAnalyses) {
@@ -21984,7 +22023,7 @@ function resolveCalleeKeys(call, methodsByKey, methodsByName) {
21984
22023
  }
21985
22024
  return candidates;
21986
22025
  }
21987
- function collectEntryPointKeys(graph, fileContext) {
22026
+ function collectEntryPointKeys(graph, fileContext, typesByName) {
21988
22027
  const entryPoints = new Set;
21989
22028
  for (const rec of graph.methodsByKey.values()) {
21990
22029
  const fileCtx = fileContext.get(rec.file);
@@ -21995,11 +22034,48 @@ function collectEntryPointKeys(graph, fileContext) {
21995
22034
  calls: fileCtx?.calls ?? null,
21996
22035
  runtimeRegistrations: fileCtx?.runtimeRegistrations ?? null
21997
22036
  });
21998
- if (tier === "TIER_1_ENTRY_POINT")
22037
+ if (tier === "TIER_1_ENTRY_POINT" || inheritsLifecycleEntry(rec.method.name, rec.enclosingType, typesByName)) {
21999
22038
  entryPoints.add(rec.key);
22039
+ }
22000
22040
  }
22001
22041
  return entryPoints;
22002
22042
  }
22043
+ function indexTypesByName(fileAnalyses) {
22044
+ const byName = new Map;
22045
+ for (const fa of fileAnalyses) {
22046
+ for (const t of fa.analysis.types ?? []) {
22047
+ const list = byName.get(t.name) ?? [];
22048
+ list.push(t);
22049
+ byName.set(t.name, list);
22050
+ }
22051
+ }
22052
+ return byName;
22053
+ }
22054
+ var simpleName = (ref) => {
22055
+ const name2 = ref.replace(/<.*$/, "").trim();
22056
+ return name2.substring(name2.lastIndexOf(".") + 1);
22057
+ };
22058
+ function inheritsLifecycleEntry(methodName, enclosingType, typesByName) {
22059
+ if (!methodName || !enclosingType?.extends)
22060
+ return false;
22061
+ const seen = new Set([enclosingType]);
22062
+ let frontier = typesByName.get(simpleName(enclosingType.extends)) ?? [];
22063
+ for (let depth = 0;depth < 8 && frontier.length > 0; depth++) {
22064
+ const next = [];
22065
+ for (const ancestor of frontier) {
22066
+ if (seen.has(ancestor))
22067
+ continue;
22068
+ seen.add(ancestor);
22069
+ const supers = [ancestor.extends, ...ancestor.implements ?? []].filter((x) => !!x);
22070
+ if (supers.some((sup) => isSupertypeLifecycleMethod(sup, methodName)))
22071
+ return true;
22072
+ if (ancestor.extends)
22073
+ next.push(...typesByName.get(simpleName(ancestor.extends)) ?? []);
22074
+ }
22075
+ frontier = next;
22076
+ }
22077
+ return false;
22078
+ }
22003
22079
  function buildFileContext(fileAnalyses) {
22004
22080
  const out2 = new Map;
22005
22081
  for (const fa of fileAnalyses) {
@@ -26307,7 +26383,8 @@ class LanguageSourcesPass {
26307
26383
  for (const finding of findJavaPatternFindings(code, graph.ir.meta.file)) {
26308
26384
  ctx.addFinding(finding);
26309
26385
  }
26310
- for (const finding of findJavaResponseWriterXssFindings(code, graph.ir.meta.file)) {
26386
+ const xssSinkLines = new Set(ctx.getResult("taint-matcher").sinks.filter((s) => s.type === "xss").map((s) => s.line));
26387
+ for (const finding of findJavaResponseWriterXssFindings(code, graph.ir.meta.file, xssSinkLines)) {
26311
26388
  ctx.addFinding(finding);
26312
26389
  }
26313
26390
  for (const finding of findJavaMongoNosqlInjectionFindings(code, graph.ir.meta.file)) {
@@ -30861,7 +30938,7 @@ function findGoXssFindings(code, file) {
30861
30938
  }
30862
30939
  return out2;
30863
30940
  }
30864
- function findJavaResponseWriterXssFindings(code, file) {
30941
+ function findJavaResponseWriterXssFindings(code, file, taintSinkLines = new Set) {
30865
30942
  const out2 = [];
30866
30943
  const lines = code.split(`
30867
30944
  `);
@@ -30897,6 +30974,8 @@ function findJavaResponseWriterXssFindings(code, file) {
30897
30974
  const m = trimmed.match(chainRe);
30898
30975
  if (!m)
30899
30976
  continue;
30977
+ if (taintSinkLines.has(i2 + 1))
30978
+ continue;
30900
30979
  const recv = m[1];
30901
30980
  if (!respNames.has(recv))
30902
30981
  continue;
@@ -37919,8 +37998,53 @@ function isReassignedToLiteralBetween(code, variable, srcLine, sinkLine) {
37919
37998
  const line = lines[i2];
37920
37999
  if (!line)
37921
38000
  continue;
37922
- if (reNaked.test(line) || reGuarded.test(line) || reSwitchCase.test(line))
38001
+ if (reGuarded.test(line))
37923
38002
  return true;
38003
+ if (!reNaked.test(line) && !reSwitchCase.test(line))
38004
+ continue;
38005
+ if (switchAlsoAssignsNonLiteral(lines, i2, variable, strLit))
38006
+ continue;
38007
+ return true;
38008
+ }
38009
+ return false;
38010
+ }
38011
+ function switchAlsoAssignsNonLiteral(lines, idx, variable, strLit) {
38012
+ let depth = 0;
38013
+ let start2 = -1;
38014
+ for (let i2 = idx;i2 >= 0; i2--) {
38015
+ const line = lines[i2];
38016
+ for (let c = line.length - 1;c >= 0; c--) {
38017
+ if (line[c] === "}")
38018
+ depth++;
38019
+ else if (line[c] === "{")
38020
+ depth--;
38021
+ }
38022
+ if (depth < 0) {
38023
+ if (/\bswitch\b/.test(line))
38024
+ start2 = i2;
38025
+ break;
38026
+ }
38027
+ }
38028
+ if (start2 < 0)
38029
+ return false;
38030
+ const assign = new RegExp(`(?:^|[^\\w.$])${variable}\\s*(?::?=)(?!=)\\s*(.*?)\\s*;?\\s*(?:break\\s*;?)?\\s*$`);
38031
+ const literalOnly = new RegExp(`^${strLit}$`);
38032
+ depth = 0;
38033
+ for (let i2 = start2;i2 < lines.length; i2++) {
38034
+ const line = lines[i2];
38035
+ if (i2 > start2 || line.includes("{")) {
38036
+ const m = assign.exec(line);
38037
+ if (m && m[1] && !literalOnly.test(m[1]))
38038
+ return true;
38039
+ }
38040
+ for (const ch of line) {
38041
+ if (ch === "{")
38042
+ depth++;
38043
+ else if (ch === "}")
38044
+ depth--;
38045
+ }
38046
+ if (i2 > start2 && depth <= 0)
38047
+ break;
37924
38048
  }
37925
38049
  return false;
37926
38050
  }
@@ -51011,7 +51135,7 @@ var colors = {
51011
51135
  };
51012
51136
 
51013
51137
  // src/version.ts
51014
- var version = "4.11.0";
51138
+ var version = "4.12.0";
51015
51139
 
51016
51140
  // src/formatters.ts
51017
51141
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -51563,8 +51687,8 @@ function generateSarifResults(results, crossFileData) {
51563
51687
 
51564
51688
  // src/build-info.ts
51565
51689
  var buildInfo = {
51566
- gitSha: "165e82d",
51567
- builtAt: "2026-10-07T23:49:13.604Z"
51690
+ gitSha: "8c284bc",
51691
+ builtAt: "2026-10-08T00:42:12.776Z"
51568
51692
  };
51569
51693
 
51570
51694
  // src/utils/args.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.11.0",
3
+ "version": "4.12.0",
4
4
  "description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
5
5
  "bin": {
6
6
  "cognium-dev": "dist/cli.js"
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "dependencies": {
67
67
  "@cognium/project-profile-detect": "1.1.2",
68
- "circle-ir": "4.11.0"
68
+ "circle-ir": "4.12.0"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^26.6.4",