Jackson 2 to 3 migration companion code
Three Maven modules - jackson2-before (2.22.1), jackson3-after (3.2.1) and a coexistence module with BOTH majors on one classpath - so every claim in the two migration guides is executed rather than asserted. Paired class names make the before/after outputs directly diffable via run-all.sh. Confirms the guides on wire-format equivalence (10-case suite, zero mismatches), classpath coexistence and the collapse of four artifacts into one. Corrects nine points, including that enableDefaultTyping() is still present in Jackson 2.22.1 rather than removed in 2.16, and that the published "after" mapper snippet does not compile.
This commit is contained in:
56
docs/s05-default-typing.md
Normal file
56
docs/s05-default-typing.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# S05 — Default typing
|
||||
|
||||
Guides: <https://ankurm.com/jackson-3-migration-guide/> (Step 4) and
|
||||
<https://ankurm.com/jackson-security-best-practices/>
|
||||
|
||||
[`before/S05DefaultTyping.java`](../jackson2-before/src/main/java/com/ankurm/migration/before/S05DefaultTyping.java) ·
|
||||
[`after/S05DefaultTyping.java`](../jackson3-after/src/main/java/com/ankurm/migration/after/S05DefaultTyping.java)
|
||||
|
||||
## Two corrections
|
||||
|
||||
**`enableDefaultTyping()` was not removed in 2.16.** Both guides say it was. It is still
|
||||
on `ObjectMapper` in Jackson 2.22.1, deprecated. The "before" program prints the
|
||||
reflective check. This changes the migration story: the method survives every 2.x
|
||||
upgrade, so Jackson 3 is where a codebase using it finally fails to compile — which is
|
||||
also the guides' own argument for why the removal is a useful security forcing function.
|
||||
|
||||
**The remediation snippet is Jackson 2.** The security post shows:
|
||||
|
||||
```java
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.activateDefaultTyping(safeTypeValidator, ObjectMapper.DefaultTyping.NON_FINAL, ...);
|
||||
```
|
||||
|
||||
Neither half compiles against Jackson 3. `activateDefaultTyping` is on
|
||||
`JsonMapper.Builder` only, and `DefaultTyping` is now a top-level enum in
|
||||
`tools.jackson.databind` rather than nested in `ObjectMapper`.
|
||||
|
||||
## Output
|
||||
|
||||
**Jackson 2 — `jackson2-before`**
|
||||
|
||||
```
|
||||
enableDefaultTyping on Jackson 2.22 ObjectMapper : true
|
||||
written : {"@class":"com.ankurm.migration.before.S05DefaultTyping$Envelope","body":{"@class":"com.ankurm.migration.before.S05DefaultTyping$SafePayload","note":"ok"}}
|
||||
read : SafePayload[ok]
|
||||
```
|
||||
|
||||
**Jackson 3 — `jackson3-after`**
|
||||
|
||||
```
|
||||
enableDefaultTyping on Jackson 3 ObjectMapper : false
|
||||
activateDefaultTyping on Jackson 3 ObjectMapper : false
|
||||
activateDefaultTyping on JsonMapper.Builder : true
|
||||
|
||||
written : {"@class":"com.ankurm.migration.after.S05DefaultTyping$Envelope","body":{"@class":"com.ankurm.migration.after.S05DefaultTyping$SafePayload","note":"ok"}}
|
||||
read : SafePayload[ok]
|
||||
rogue : rejected with InvalidTypeIdException
|
||||
```
|
||||
|
||||
A third trap, in neither guide: with `DefaultTyping.NON_FINAL` Jackson writes a type id
|
||||
for the **root** object too. So the root class has to be in the allowlist as well as the
|
||||
payload hierarchy. Allowlist only the payload base type and the happy path fails, not
|
||||
just the attack path — which looks like a broken validator when it is working correctly.
|
||||
|
||||
The final `rogue` line is the negative test: a real class, present on the classpath,
|
||||
deserialisable in every other respect, refused at type resolution before instantiation.
|
||||
Reference in New Issue
Block a user