# Part 7 — Security Post: ## H01 — the safe pattern [`H01SafePolymorphismByAnnotation.java`](../src/main/java/com/ankurm/jackson3/part7security/H01SafePolymorphismByAnnotation.java) `@JsonTypeInfo(use = Id.NAME)` plus an explicit `@JsonSubTypes` registry. The permitted set is fixed at compile time, so no payload can introduce a class of its own. ``` email : EmailNotification[a@example.com] sms : SmsNotification[+441234567890] attack: rejected with InvalidTypeIdException ``` This is the recommendation, and it is correct. Reach for anything below only if this cannot express your model. ## H02 — what survived into Jackson 3 [`H02DefaultTypingRemoved.java`](../src/main/java/com/ankurm/jackson3/part7security/H02DefaultTypingRemoved.java) The post's remediation snippet is Jackson 2: ```java ObjectMapper mapper = new ObjectMapper(); mapper.activateDefaultTyping(safeTypeValidator, ObjectMapper.DefaultTyping.NON_FINAL, ...); ``` Neither line compiles against Jackson 3. The mapper has no mutators at all, and `DefaultTyping` is no longer nested inside `ObjectMapper`. ``` --- tools.jackson.databind.ObjectMapper --- enableDefaultTyping ABSENT activateDefaultTyping ABSENT setSerializationInclusion ABSENT registerModule ABSENT addMixIn ABSENT total set*() mutators: 0 --- tools.jackson.databind.json.JsonMapper.Builder --- activateDefaultTyping present deactivateDefaultTyping present polymorphicTypeValidator present changeDefaultPropertyInclusion present serializationInclusion ABSENT ``` A second correction, from the migration repo rather than this one: both guides state that `enableDefaultTyping()` was "removed in 2.16". It is still present on `ObjectMapper` in Jackson 2.22.1, deprecated. That changes the migration story — a Jackson 2 codebase can still be compiling against it today, so Jackson 3 is where it finally breaks, not 2.16. ## H03 — a provable allowlist [`H03PolymorphicTypeValidatorAllowlist.java`](../src/main/java/com/ankurm/jackson3/part7security/H03PolymorphicTypeValidatorAllowlist.java) The post's intent in working Jackson 3 form, with the negative test the post's own AI prompt asks for. ``` allowed written : {"@class":"com.ankurm.jackson3.part7security.H03PolymorphicTypeValidatorAllowlist$Envelope","body":{"@class":"com.ankurm.jackson3.part7security.H03PolymorphicTypeValidatorAllowlist$SafePayload","note":"ok"}} allowed read : SafePayload[ok] rogue : rejected with InvalidTypeIdException ``` One trap the post does not mention: with `DefaultTyping.NON_FINAL`, Jackson writes a type id for the **root** object too, so the root class must be allowlisted as well. Allowlisting only the payload base type makes the happy path fail, not just the attack path — which reads like a bug in your validator when it is actually correct behaviour. ## H04 — resource limits [`H04StreamReadConstraints.java`](../src/main/java/com/ankurm/jackson3/part7security/H04StreamReadConstraints.java) Beyond the post. Gadget attacks are not the only deserialisation risk: a few hundred bytes of nested brackets can exhaust the stack. Jackson 3 ships defaults for this and they are tunable. ``` --- Jackson 3 defaults --- max nesting depth : 500 max number length : 1000 max string length : 100000000 max name length : 50000 max doc length : -1 (-1 = unlimited) 1200-deep nesting, default limits -> rejected (StreamConstraintsException) 20-deep nesting, strict limits -> rejected (StreamConstraintsException) 5-deep nesting, strict limits -> accepted 3KB string, strict limits -> rejected (StreamConstraintsException) ``` The 100 MB default string limit and unlimited document length are generous for a public endpoint. Tighten both if you accept untrusted JSON. ## H05 — Object.class [`H05NeverDeserialiseIntoObject.java`](../src/main/java/com/ankurm/jackson3/part7security/H05NeverDeserialiseIntoObject.java) ``` as Object : {action=ship, quantity=3, extra={nested=[1, 2]}} runtime type: java.util.LinkedHashMap <- a plain Map, no arbitrary class was instantiated as DTO : MyRequestDto[action=ship, quantity=3] bad input : rejected with InvalidFormatException ``` Worth being precise here, because the post is slightly alarming about it: on a default Jackson 3 mapper, `readValue(json, Object.class)` yields a plain `LinkedHashMap`. No arbitrary class is instantiated. The danger returns only once default typing is active (H03). The rule still holds — target a specific type and you get input validation as a side effect, as the last line shows.