cognium-dev 3.177.0 → 3.178.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 +160 -2
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -19183,6 +19183,50 @@ function applyLibraryApiSurfaceDowngrade(findings) {
19183
19183
  });
19184
19184
  }
19185
19185
 
19186
+ // ../circle-ir/dist/analysis/note-coalescer.js
19187
+ function coalesceNoteLevelFindings(findings) {
19188
+ if (findings.length < 2)
19189
+ return [...findings];
19190
+ const groups = new Map;
19191
+ const order = [];
19192
+ for (const f of findings) {
19193
+ const key = `${f.file}\x00${f.line}`;
19194
+ const bucket = groups.get(key);
19195
+ if (bucket) {
19196
+ bucket.push(f);
19197
+ } else {
19198
+ groups.set(key, [f]);
19199
+ order.push(key);
19200
+ }
19201
+ }
19202
+ const out2 = [];
19203
+ for (const key of order) {
19204
+ const bucket = groups.get(key);
19205
+ if (bucket.length === 1) {
19206
+ out2.push(bucket[0]);
19207
+ continue;
19208
+ }
19209
+ const allNote = bucket.every((f) => f.level === "note");
19210
+ if (!allNote) {
19211
+ for (const f of bucket)
19212
+ out2.push(f);
19213
+ continue;
19214
+ }
19215
+ const uniqueRuleIds = new Set(bucket.map((f) => f.rule_id));
19216
+ if (uniqueRuleIds.size < 2) {
19217
+ for (const f of bucket)
19218
+ out2.push(f);
19219
+ continue;
19220
+ }
19221
+ const sorted = [...bucket].sort((a, b) => a.rule_id.localeCompare(b.rule_id));
19222
+ const primary = sorted[0];
19223
+ const additional = sorted.slice(1).map((f) => f.rule_id).concat(...sorted.map((f) => f.labels ?? []));
19224
+ const uniqueLabels = Array.from(new Set(additional)).filter((l) => l !== primary.rule_id);
19225
+ out2.push({ ...primary, labels: uniqueLabels });
19226
+ }
19227
+ return out2;
19228
+ }
19229
+
19186
19230
  // ../circle-ir/dist/analysis/entry-point-detection.js
19187
19231
  var TIER_1_METHOD_ANNOTATIONS = new Set([
19188
19232
  "RequestMapping",
@@ -32583,6 +32627,117 @@ class SinkSemanticsPass {
32583
32627
  }
32584
32628
  }
32585
32629
 
32630
+ // ../circle-ir/dist/analysis/dependency-versions.js
32631
+ function resolveFastjsonFromPom(pomXml) {
32632
+ if (!pomXml)
32633
+ return null;
32634
+ const propMatch = pomXml.match(/<fastjson\.version>\s*([^<\s]+)\s*<\/fastjson\.version>/);
32635
+ if (propMatch) {
32636
+ const version = propMatch[1];
32637
+ return { version, noneAutotype: /_noneautotype/i.test(version) };
32638
+ }
32639
+ const depRe = /<dependency>[\s\S]*?<\/dependency>/g;
32640
+ let m;
32641
+ while ((m = depRe.exec(pomXml)) !== null) {
32642
+ const block = m[0];
32643
+ const gid = block.match(/<groupId>\s*([^<\s]+)\s*<\/groupId>/)?.[1];
32644
+ const aid = block.match(/<artifactId>\s*([^<\s]+)\s*<\/artifactId>/)?.[1];
32645
+ if (gid !== "com.alibaba")
32646
+ continue;
32647
+ if (aid !== "fastjson" && aid !== "fastjson2")
32648
+ continue;
32649
+ const ver = block.match(/<version>\s*([^<\s]+)\s*<\/version>/)?.[1];
32650
+ if (!ver)
32651
+ continue;
32652
+ if (/^\$\{/.test(ver))
32653
+ return null;
32654
+ return { version: ver, noneAutotype: /_noneautotype/i.test(ver) };
32655
+ }
32656
+ return null;
32657
+ }
32658
+ function fileReenablesFastjsonAutotype(source) {
32659
+ if (!source)
32660
+ return false;
32661
+ return /\bsetAutoTypeSupport\s*\(\s*true\b/.test(source);
32662
+ }
32663
+ function fileEnablesJacksonPolymorphism(source) {
32664
+ if (!source)
32665
+ return false;
32666
+ if (/\benableDefaultTyping\s*\(/.test(source))
32667
+ return true;
32668
+ if (/\bactivateDefaultTyping\s*\(/.test(source))
32669
+ return true;
32670
+ if (/@JsonTypeInfo\b/.test(source))
32671
+ return true;
32672
+ return false;
32673
+ }
32674
+ function fileConfiguresSnakeYamlSafely(source) {
32675
+ if (!source)
32676
+ return false;
32677
+ if (/\bnew\s+SafeConstructor\s*\(/.test(source))
32678
+ return true;
32679
+ if (/\bSafeConstructor\s+\w+\s*=/.test(source))
32680
+ return true;
32681
+ return false;
32682
+ }
32683
+
32684
+ // ../circle-ir/dist/analysis/passes/deserialization-safety-gate-pass.js
32685
+ var FASTJSON_METHODS = new Set(["parseObject", "parse"]);
32686
+ var FASTJSON_CLASSES = new Set(["JSON", "JSONObject"]);
32687
+ var JACKSON_METHODS = new Set(["readValue", "convertValue", "treeToValue"]);
32688
+ var JACKSON_CLASSES = new Set(["ObjectMapper", "ObjectReader"]);
32689
+ var SNAKEYAML_METHODS = new Set(["load", "loadAs", "loadAll"]);
32690
+ var SNAKEYAML_CLASSES = new Set(["Yaml"]);
32691
+
32692
+ class DeserializationSafetyGatePass {
32693
+ dependencyContext;
32694
+ name = "deserialization-safety-gate";
32695
+ category = "security";
32696
+ constructor(dependencyContext) {
32697
+ this.dependencyContext = dependencyContext;
32698
+ }
32699
+ run(ctx) {
32700
+ const { graph, language, code } = ctx;
32701
+ if (language !== "java") {
32702
+ return { droppedFastjson: 0, droppedJackson: 0, droppedSnakeYaml: 0 };
32703
+ }
32704
+ const sinks = ctx.hasResult("sink-filter") ? ctx.getResult("sink-filter").sinks : graph.ir.taint.sinks;
32705
+ const pomXml = this.dependencyContext?.java?.pomXml;
32706
+ const fastjson = pomXml ? resolveFastjsonFromPom(pomXml) : null;
32707
+ const fastjsonHardened = fastjson?.noneAutotype === true && !fileReenablesFastjsonAutotype(code);
32708
+ const jacksonSafe = !fileEnablesJacksonPolymorphism(code);
32709
+ const snakeYamlSafe = fileConfiguresSnakeYamlSafely(code);
32710
+ let droppedFastjson = 0;
32711
+ let droppedJackson = 0;
32712
+ let droppedSnakeYaml = 0;
32713
+ const kept = sinks.filter((sink) => {
32714
+ if (sink.type !== "deserialization")
32715
+ return true;
32716
+ if (!sink.method)
32717
+ return true;
32718
+ if (fastjsonHardened && FASTJSON_METHODS.has(sink.method) && (sink.class === undefined || FASTJSON_CLASSES.has(sink.class))) {
32719
+ droppedFastjson++;
32720
+ return false;
32721
+ }
32722
+ if (jacksonSafe && JACKSON_METHODS.has(sink.method) && sink.class !== undefined && JACKSON_CLASSES.has(sink.class)) {
32723
+ droppedJackson++;
32724
+ return false;
32725
+ }
32726
+ if (snakeYamlSafe && SNAKEYAML_METHODS.has(sink.method) && sink.class !== undefined && SNAKEYAML_CLASSES.has(sink.class)) {
32727
+ droppedSnakeYaml++;
32728
+ return false;
32729
+ }
32730
+ return true;
32731
+ });
32732
+ const totalDropped = droppedFastjson + droppedJackson + droppedSnakeYaml;
32733
+ if (totalDropped > 0) {
32734
+ sinks.length = 0;
32735
+ sinks.push(...kept);
32736
+ }
32737
+ return { droppedFastjson, droppedJackson, droppedSnakeYaml };
32738
+ }
32739
+ }
32740
+
32586
32741
  // ../circle-ir/dist/analysis/passes/cli-main-reflection-suppress-pass.js
32587
32742
  var REFLECTION_SINK_METHODS = new Set([
32588
32743
  "forName",
@@ -44124,6 +44279,8 @@ async function analyze(code, filePath, language, options = {}) {
44124
44279
  pipeline.add(new SinkFilterPass);
44125
44280
  if (!disabledPasses.has("sink-semantics"))
44126
44281
  pipeline.add(new SinkSemanticsPass);
44282
+ if (!disabledPasses.has("deserialization-safety-gate"))
44283
+ pipeline.add(new DeserializationSafetyGatePass(options.dependencyContext));
44127
44284
  if (!disabledPasses.has("cli-main-reflection-suppress"))
44128
44285
  pipeline.add(new CliMainReflectionSuppressPass);
44129
44286
  if (!disabledPasses.has("library-profile-sink-gate"))
@@ -44285,7 +44442,8 @@ async function analyze(code, filePath, language, options = {}) {
44285
44442
  const verifiedFindings = applyConfidenceFilter(findings, options.includeSpeculative === true);
44286
44443
  const downgradedFindings = applyLibraryApiSurfaceDowngrade(verifiedFindings);
44287
44444
  const profiledFindings = applyProjectProfileTransform(downgradedFindings, makeProfileResolver2(options.projectProfile));
44288
- const cappedFindings = applyPerFileFindingCap(filePath, profiledFindings, options.perFileFindingCap ?? DEFAULT_PER_FILE_FINDING_CAP);
44445
+ const coalescedFindings = coalesceNoteLevelFindings(profiledFindings);
44446
+ const cappedFindings = applyPerFileFindingCap(filePath, coalescedFindings, options.perFileFindingCap ?? DEFAULT_PER_FILE_FINDING_CAP);
44289
44447
  return {
44290
44448
  meta,
44291
44449
  types,
@@ -45066,7 +45224,7 @@ var colors = {
45066
45224
  };
45067
45225
 
45068
45226
  // src/version.ts
45069
- var version = "3.177.0";
45227
+ var version = "3.178.0";
45070
45228
 
45071
45229
  // src/formatters.ts
45072
45230
  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.177.0",
3
+ "version": "3.178.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.177.0"
69
+ "circle-ir": "^3.178.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",