Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: Jackson Mixin Allowlist Keys in Kotlin are Lowercase Primitive Names Instead of Fully Qualified Class Names #16684

Open
CHOICORE opened this issue Mar 1, 2025 · 0 comments
Labels
status: waiting-for-triage An issue we've not yet triaged type: bug A general bug

Comments

@CHOICORE
Copy link

CHOICORE commented Mar 1, 2025

Describe the bug
When using Spring Security's Jackson deserialization with Kotlin, there's a bug in how Mixin classes are applied to primitive wrapper classes. In Java, when applying a Mixin to Long.class, the key is correctly set to "java.lang.Long". However, in Kotlin, the equivalent code results in the key being set to lowercase "long", causing allowlist configurations to fail.

Caused by: java.lang.IllegalArgumentException: The class with java.lang.Long and name of java.lang.Long is not in the allowlist. If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. If the serialization is only done by a trusted source, you can also enable default typing. See https://github.com/spring-projects/spring-security/issues/4370 for details
	at org.springframework.security.jackson2.SecurityJackson2Modules$AllowlistTypeIdResolver.typeFromId(SecurityJackson2Modules.java:293) ~[spring-security-core-6.4.3.jar:6.4.3]
	at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:159) ~[jackson-databind-2.18.2.jar:2.18.2]
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:100) ~[jackson-databind-2.18.2.jar:2.18.2]
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromArray(AsArrayTypeDeserializer.java:56) ~[jackson-databind-2.18.2.jar:2.18.2]
	at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromAny(AsPropertyTypeDeserializer.java:238) ~[jackson-databind-2.18.2.jar:2.18.2]
	at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializerNR.deserializeWithType(UntypedObjectDeserializerNR.java:112) ~[jackson-databind-2.18.2.jar:2.18.2]
	at com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringKeyMap(MapDeserializer.java:625) ~[jackson-databind-2.18.2.jar:2.18.2]
	... 16 common frames omitted

To Reproduce

  1. Create a Spring Boot application using Kotlin
  2. Configure Jackson ObjectMapper with Security modules
  3. Try to add a Mixin for Long.class using addMixIn(Long::class.java, LongMixin::class.java)
  4. Observe that the Mixin is registered with key "long" instead of "java.lang.Long"

Expected behavior
When adding a Mixin in Kotlin using addMixIn(Long::class.java, LongMixin::class.java), the key should be set to "java.lang.Long" just like in Java, not lowercase "long".

Sample
Java code (working correctly):

@JsonMixin(Long.class)
abstract class LongMixin {}

public void customObjectMapper(ObjectMapper objectMapper) {
    ObjectMapper copied = objectMapper.copy();
    copied.registerModules(SecurityJackson2Modules.getModules(this.classLoader));
    copied.addMixIn(Long.class, LongMixin.class);
    // Mixin key is set to "java.lang.Long"
}

Kotlin code (exhibiting the bug):

@JsonMixin(Long::class)
abstract class LongMixin {}

fun customObjectMapper(objectMapper: ObjectMapper) {
    val copied: ObjectMapper = objectMapper.copy()
    copied.registerModules(SecurityJackson2Modules.getModules(this.classLoader))
    copied.addMixIn(Long::class.java, LongMixin::class.java)
    // Mixin key is incorrectly set to "long" instead of "java.lang.Long"
}

I'm not entirely sure if this is the right place to report this issue, so I apologize if I've posted it in the wrong location.

@CHOICORE CHOICORE added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Mar 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-triage An issue we've not yet triaged type: bug A general bug
Projects
None yet
Development

No branches or pull requests

1 participant