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.
2.5 KiB
S05 — Default typing
Guides: https://ankurm.com/jackson-3-migration-guide/ (Step 4) and https://ankurm.com/jackson-security-best-practices/
before/S05DefaultTyping.java ·
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:
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.