{
  "root_cause_equivalence": "partial_same_sink_different_mitigation_aspect",
  "parent_root_cause": "SvmDoccatModel.deserialize(InputStream) calls ObjectInputStream.readObject() on an attacker-controlled serialised stream. readObject() materialises the entire object graph (and runs every class's readObject/readResolve) BEFORE the result is cast to SvmDoccatModel, so attacker-controlled work executes during deserialisation.",
  "parent_sink": "opennlp.tools.ml.libsvm.doccat.SvmDoccatModel.deserialize -> new ObjectInputStream(in) -> readObject()",
  "variant_sink": "opennlp.tools.ml.libsvm.doccat.SvmDoccatModel.deserialize -> new ObjectInputStream(in) -> setObjectInputFilter(buildFilter) -> readObject() -> HashMap.readObject() -> readArray -> Array.newInstance",
  "same_sink": true,
  "same_entry_point": true,
  "same_trust_boundary": true,
  "trust_boundary": "An attacker- or semi-trusted-source serialised byte stream supplied to the public static SvmDoccatModel.deserialize(InputStream) by a consumer JVM (e.g. a downstream service ingesting model files from users/network). Identical to the parent CVE.",
  "mitigation_aspect_bypassed": "The 3.0.0-M4 fix adds TWO mitigations of the unsafe-deserialisation root cause: (A) a class allow-list that rejects foreign gadget classes (closes the RCE vector), and (B) numeric resource limits (depth/refs/per-array-length) intended to 'bound pathological streams'. This variant bypasses mitigation (B) only: it uses solely allow-listed classes (HashMap, String, primitive double[]) within all numeric limits, so mitigation (A) does not engage (no foreign class to reject) and mitigation (B) fails to bound total allocated memory. The OOM is delivered during readObject(), before the cast — the same 'work-before-cast' property as the parent CVE.",
  "mitigation_aspect_still_holding": "Mitigation (A) holds: the class allow-list still rejects foreign gadget classes. Verified by the RCE control run (InvalidClassException: filter status: REJECTED for MaliciousGadget on the fixed build; no gadget execution). No allow-listed class provides an exploitable readObject/readResolve/writeReplace, so no RCE bypass of (A) exists.",
  "impact_class_parent": "code_execution",
  "impact_class_variant": "denial_of_service",
  "impact_class_same": false,
  "impact_relationship": "The variant reaches the same unsafe-deserialisation sink and defeats the same fix's resource-limit mitigation, but yields a lesser impact (memory-exhaustion DoS) than the parent's RCE. It is a bypass of the fix's stated 'bound pathological streams' goal, not a bypass of its RCE-prevention goal.",
  "why_same_root_cause": "Both parent and variant exploit that SvmDoccatModel.deserialize() performs untrusted ObjectInputStream.readObject() whose attacker-controlled graph is fully materialised before any caller-level type check. The fix reduces but does not eliminate this exposure: the allow-list removes foreign classes, but the unbounded-memory deserialisation of allow-listed primitive arrays is the same underlying 'readObject materialises attacker data' behaviour, now expressed as a DoS.",
  "confidence": "high",
  "evidence_refs": {
    "fixed_oom_small": "bundle/logs/vuln_variant/fixed_oom_small.log",
    "fixed_oom_large": "bundle/logs/vuln_variant/fixed_oom_large.log",
    "fixed_rce": "bundle/logs/vuln_variant/fixed_rce.log",
    "vuln_rce": "bundle/logs/vuln_variant/vuln_rce.log"
  }
}
