TanStack Query — Unbounded Recursion Denial of Service in `replaceEqualDeep` (CVE-2026-26903)
by John-Jung (GitHub) · 2026-07-05
- Severity
- Medium
- CVE
- CVE-2026-26903
- Category
- web
- Affected product
- TanStack Query (@tanstack/query-core and framework bindings: react-query, vue-query, solid-query, svelte-query)
- Affected versions
- @tanstack/query-core <= 5.90.16 (fixed in 5.90.17; framework bindings fixed in 5.90.18+ / 6.1.7+ for svelte-query)
- Disclosed
- 2026-07-05
- Patch status
- patched
Tags
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-05 |
| Last Updated | 2026-04 |
| Author / Researcher | John-Jung (GitHub) |
| CVE / Advisory | CVE-2026-26903 |
| Category | web |
| Severity | Medium |
| CVSS Score | Not specified in source (upstream advisory rates it Medium) |
| Status | PoC |
| Tags | tanstack-query, javascript, denial-of-service, stack-overflow, recursion, react, frontend, client-side-dos |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | TanStack Query (@tanstack/query-core and framework bindings: react-query, vue-query, solid-query, svelte-query) |
| Versions Affected | @tanstack/query-core <= 5.90.16 (fixed in 5.90.17; framework bindings fixed in 5.90.18+ / 6.1.7+ for svelte-query) |
| Language / Platform | JavaScript/TypeScript, runs in Node.js and browsers |
| Authentication Required | No |
| Network Access Required | No — purely client-side |
Summary
TanStack Query’s internal replaceEqualDeep function recursively performs deep-equality comparisons between old and new query cache data so that unchanged object references can be preserved across re-renders. The recursive implementation has no depth limit or cycle detection, so processing a sufficiently deeply nested object (on the order of 5,000+ levels) exhausts the JavaScript call stack. Because this function runs on the main thread for any query cache update (useQuery, setQueryData, invalidateQueries, etc.), an attacker who can influence the shape of query response data can freeze the entire application UI until the page is reloaded. The included PoC provides both a Node.js script that demonstrates the stack overflow directly and an interactive HTML page showing the UI freeze in a browser.
Vulnerability Details
Root Cause
replaceEqualDeep() recurses into nested object/array properties without any maximum depth check, so an attacker-controlled deeply nested payload causes unbounded recursion and a stack overflow that blocks the JS event loop.
Attack Vector
- Attacker (or a compromised/malicious upstream API) crafts a JSON response whose data is nested thousands of levels deep (e.g.,
{nested: {nested: {...}}}). - The nested payload flows into TanStack Query’s cache via a normal query update path (
useQueryresponse,setQueryData,invalidateQueries). replaceEqualDeepis invoked to diff the old and new cache values and recurses once per nesting level.- The call stack is exhausted, freezing the JS thread and making the entire page unresponsive until manually reloaded.
Impact
A single malicious or malformed API response can render a TanStack Query-based web application completely unresponsive (client-side denial of service), requiring a full page reload to recover.
Environment / Lab Setup
Target: Any web app using @tanstack/query-core <= 5.90.16 (directly or via react-query/vue-query/solid-query/svelte-query)
Attacker: Node.js runtime (to run poc.js) or any modern browser (to open tanstack-query-poc.html)
Proof of Concept
PoC Script
See
poc.jsandtanstack-query-poc.htmlin this folder.
| |
poc.js generates deeply nested objects at increasing depths (100, 1000, 5000, 10000) and calls the vulnerable replaceEqualDeep logic directly, showing successful comparisons at shallow depths and a “Maximum call stack size exceeded” crash at 5,000+ levels. tanstack-query-poc.html provides an interactive demo with a live counter/timer/input to show a normally-responsive UI, then a button that triggers the DoS by feeding a 10,000-level-deep object into query cache diffing, freezing all UI interaction.
Detection & Indicators of Compromise
Signs of compromise:
- Reports of the web app “freezing” or becoming totally unresponsive after a specific action
- Abnormally deep JSON nesting present in captured network responses or request payloads
- Browser tab needing a hard reload to recover, with no JS console errors logged
Remediation
| Action | Detail |
|---|---|
| Primary fix | Upgrade to @tanstack/query-core 5.90.17+ (react-query/vue-query 5.90.18+, svelte-query 6.1.7+), which adds a depth limit (>500 returns early) to replaceEqualDeep |
| Interim mitigation | Flatten or cap the nesting depth of data returned by APIs before it reaches the query cache; implement a custom structuralSharing comparator with a depth guard |
References
Notes
Mirrored from https://github.com/John-Jung/CVE-2026-26903-PoC on 2026-07-05.
| |