Jackson deserialization validation
[COLM'26] SkillLearnBench is the first benchmark for evaluating continual learning methods that automatically generate agent skills.
npx -y skills add cxcscmu/SkillLearnBench --skill jackson-deserialization-validationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
Securing Jackson JSON deserialization by validating input before processing and preventing unknown properties.
SKILL.md
4.3 KB, 902 tokens by cl100k_base, as published. Nobody here has run it
Jackson Deserialization Validation
Vulnerability Pattern
When Jackson deserializes JSON with unknown properties, if not handled properly, malicious extra properties can bypass validation logic:
{
"validProperty": "legitimate value",
"": {
"enabled": true,
"other": "malicious"
}
}
The empty key "" can be overlooked by simple property checks.
Jackson Configuration
1. Fail on Unknown Properties
@JsonIgnoreProperties(ignoreUnknown = false)
public class MyFilter {
@JsonProperty
private String function;
// This will throw exception on unknown properties
}
2. Custom Deserialization
@JsonDeserialize(using = CustomDeserializer.class)
public class MyFilter {
// Custom logic during deserialization
}
3. @JsonAnySetter for Validation
public class MyFilter {
@JsonProperty
private String function;
@JsonAnySetter
public void handleUnknown(String key, Object value) {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("Empty property keys not allowed");
}
// Validate other unknown properties
}
}
Validation Strategies
Strategy 1: Whitelist Allowed Keys
private static final Set<String> ALLOWED_KEYS =
Set.of("function", "type", "name");
@JsonAnySetter
public void handleProperty(String key, Object value) {
if (!ALLOWED_KEYS.contains(key)) {
throw new IllegalArgumentException(
"Unknown property: " + key);
}
}
Strategy 2: Reject Empty/Null Keys
@JsonAnySetter
public void validateKey(String key, Object value) {
if (key == null || key.trim().isEmpty()) {
throw new IllegalArgumentException(
"Property keys cannot be empty or null");
}
}
Strategy 3: Post-Deserialization Validation
@JsonProperty
private Map<String, Object> properties;
@PostConstruct
public void validate() {
for (String key : properties.keySet()) {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException(
"Empty keys not allowed");
}
}
}
Implementation in Druid
Finding Vulnerable Classes
- Search for
@JsonTypeName("javascript") - Look for JavaScript filter/transform specs
- Check deserialization of filter objects
Key Locations
JavaScriptFilter.java- Main JavaScript filter class- Filter spec handlers in indexing-service
- Transform spec deserialization
Fix Pattern
@JsonIgnoreProperties(ignoreUnknown = false)
public class JavaScriptFilter {
@JsonProperty
private String function;
@JsonAnySetter
public void handleUnknown(String key, Object value) {
throw new IllegalArgumentException(
"Unknown property '" + key +
"' in JavaScript filter specification");
}
}
Testing the Fix
Test Case 1: Valid Request
String json = "{\"type\":\"javascript\",\"function\":\"function(){return true;}\"}";
JavaScriptFilter filter = mapper.readValue(json, JavaScriptFilter.class);
// Should succeed
Test Case 2: Empty Key (Should Fail)
String json = "{\"type\":\"javascript\",\"function\":\"...\",\"\":{\"enabled\":true}}";
assertThrows(JsonMappingException.class, () ->
mapper.readValue(json, JavaScriptFilter.class));
// Should throw exception
Test Case 3: Null Key (Should Fail)
String json = "{\"type\":\"javascript\",null:{\"value\":\"bad\"}}";
assertThrows(JsonMappingException.class, () ->
mapper.readValue(json, JavaScriptFilter.class));
Druid-Specific Considerations
- Custom ObjectMapper: Druid may use custom ObjectMapper configuration
- Multiple Filter Types: Fix may need to apply to multiple filter classes
- Backward Compatibility: Ensure legitimate requests still work
- Nested Specs: Check if transformSpec or other nested objects also need fixes
Recommended Approach
- Use
@JsonIgnoreProperties(ignoreUnknown = false)to detect unknown properties - Add
@JsonAnySetterto validate property names - Reject any empty string keys explicitly
- Test both via HTTP API and direct deserialization