GHSA-jggr-w7fw-pc2jMedium▾ Sunlitfast-copy: Stack exhaustion in fast-copy when copying deeply-nested values
▾ Sunlit zone — Low / medium · no exploitation signal
impact 27.5 · likelihood 0 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
fast-copy traverses values recursively with no bound on depth. Copying a sufficiently deeply-nested value exhausts the JavaScript call stack and throws a native RangeError: Maximum call stack size exceeded from inside the library.
Both copy and copyStrict recurse once per level of nesting. The value does not need to be circular (circular references were already handled correctly via an internal cache) and it does not need to be large. A plain object nested a few thousand levels deep is only a few
kilobytes of equivalent JSON.
Measured depths at which copying begins to fail (Node.js, V8; the exact ceiling varies with JIT state and the stack available to the environment):
| Call | Last depth that copies successfully |
|---|---|
copy(object) | 2811 |
copy(array) | 3983 |
copyStrict(object) | 1874 |
copyStrict(array) | 1874 |
JSON.parse is iterative and parses deeply-nested input without difficulty, so a payload that deserializes cleanly can fail in a subsequent copy call.
The failure is a synchronous, catchable RangeError confined to the copy call that received the value. There are no memory safety concerns, no data exposure, and no effect on state outside that call. In a typical server the result is a failed request rather than a failed process.
Applications that call copy on externally-supplied data, such as request bodies, cached payloads, merged configuration, etc., do not expect a clone helper to throw may surface this as an unhandled error.
Fixed in 4.1.0, and backported to 3.1.0 and 2.2.0 so that every major line has a patch available without requiring a breaking upgrade.
Traversal is now bounded by a maxDepth option, defaulting to 1000, which sits below the native limit in standard environments. Exceeding it throws MaxDepthExceededError, which carries the limit that was exceeded and extends RangeError so existing handling continues to match.
The limit is configurable for consumers with legitimately deep data. On 3.x and 4.x it is set when creating a copier:
import { createCopier } from 'fast-copy';
export const copy = createCopier({ maxDepth: 5000 });
On 2.x it is passed per call, and the error is a property of the exported function rather than a named export:
import copy from 'fast-copy';
copy(value, { maxDepth: 5000 });
// detection: error instanceof copy.MaxDepthExceededError
In legacy environments without Object.setPrototypeOf, instanceof MaxDepthExceededError cannot be supported; check error.name === 'MaxDepthExceededError' or error instanceof RangeError instead.
fast-copy >= 4.0.0, < 4.1.0fast-copy >= 3.0.0, < 3.1.0fast-copy < 2.2.0Upgrade to a patched release:
fast-copy 4.1.0fast-copy 3.1.0fast-copy 2.2.0Connected by shared product, vendor, weakness, or advisory.
CVE-2021-45105Medium· 5.9Apache Log4j2 versions 2.0-alpha1 through 2.16.0 (excluding 2.12.3 and 2.3.1) did not protect from uncontrolled recursion from self-referential lookups
CVE-2022-50407Medium· 5.5In the Linux kernel, the following vulnerability has been resolved: crypto: hisilicon/qm - increase the memory of local variables Increase the buffer to prevent stack overflow by fuzz test
GHSA-8vvx-rff5-p5rqMedium· 5.9Nodemailer: Nested structured recipient arrays bypass the parser depth limit and cause stack exhaustion DoS
CVE-2026-102281High· 7.5Nest is a framework for building scalable Node.js server-side applications
CVE-2026-102278High· 7.5The brace-expansion library generates arbitrary strings containing a common prefix and suffix
CVE-2026-102276High· 7.5The brace-expansion library generates arbitrary strings containing a common prefix and suffix