Executive Summary
A critical deserialization vulnerability (CVE-2026-59940) has been disclosed in seroval, a popular JavaScript library for stringifying complex JS values — including Promises, Sets, Maps, and circular references — beyond what JSON.stringify supports. Versions prior to 1.5.3 are vulnerable.
CVSS Score: 9.8 (Critical)
The flaw exists in seroval.fromJSON(). Attacker-controlled JSON containing Promise control nodes can operate on values from the general deserialization reference table without verifying genuine intent, allowing an attacker to forge object references, trigger unintended code paths, or achieve object injection in the deserializing application.
Vulnerability Overview
| Attribute | Value |
|---|---|
| CVE ID | CVE-2026-59940 |
| CVSS Score | 9.8 (Critical) |
| Type | Deserialization / Object Forgery |
| Attack Vector | Network (attacker-controlled JSON input) |
| Privileges Required | None |
| User Interaction | None |
| Affected Function | seroval.fromJSON() |
Affected Versions
| Package | Affected Versions | Fixed Version |
|---|---|---|
| seroval | < 1.5.3 | 1.5.3+ |
Technical Analysis
Background on Seroval
Seroval is designed to serialize JavaScript values that JSON cannot handle: Promises, undefined, BigInt, circular references, typed arrays, Maps, Sets, and more. It generates a structured JSON representation — including special "control nodes" — that fromJSON() can reconstruct back into live JS values.
The Vulnerability
The fromJSON() function processes a stream of serialized nodes, some of which are Promise control nodes that resolve or reject deferred values. These nodes reference other entries in the deserialization reference table — a lookup structure tracking all deserialized objects.
Prior to 1.5.3, fromJSON() did not validate that a Promise control node's target reference was actually a Promise before operating on it. An attacker who controls the JSON input can craft a payload where a Promise control node targets an arbitrary reference table entry — a plain object, function, or sensitive value — and triggers behaviors on it that were never intended.
Attack Scenario
// Vulnerable application accepting user-controlled serialized data
import { fromJSON } from 'seroval'; // version < 1.5.3
const userInput = JSON.parse(req.body.data); // attacker-controlled
const result = fromJSON(userInput); // triggers the vulnerability// Malicious payload: Promise control node targeting a non-Promise reference
{
"t": { "0": <legitimate_object>, "1": <sensitive_target> },
"r": 0,
"v": [
{ "f": 1, "s": 1, "v": "<injected_value>" }
]
}Potential Impact
| Impact | Description |
|---|---|
| Object Forgery | Force arbitrary reference table entries into unintended states |
| Prototype Pollution | Depending on application code paths triggered post-deserialization |
| Application Logic Bypass | Manipulate internal state to bypass authorization or business logic |
| Denial of Service | Cause unhandled rejections or state corruption that crashes the process |
| RCE (context-dependent) | In environments where deserialized objects trigger code execution |
Immediate Remediation
Step 1: Upgrade Seroval
# npm
npm update seroval
# yarn
yarn upgrade seroval
# pnpm
pnpm update seroval
# Verify installed version
node -e "const s = require('seroval'); console.log(require('./node_modules/seroval/package.json').version)"Step 2: Audit Usage of fromJSON()
Search your codebase for any use of fromJSON with user-controlled input:
# Find all uses of fromJSON in the codebase
grep -rn "fromJSON" src/ --include="*.ts" --include="*.js" --include="*.mjs"
# Identify where input originates
grep -rn "fromJSON" src/ -A3 --include="*.ts"Step 3: Validate Input Before Deserialization
If you cannot upgrade immediately, validate that fromJSON() input comes from trusted sources only:
import { fromJSON } from 'seroval';
function safeFromJSON(data: unknown, trustedSources: Set<string>) {
if (!isTrustedSource(trustedSources)) {
throw new Error('Untrusted deserialization source rejected');
}
return fromJSON(data as SerovalJSON);
}Step 4: Monitor for Anomalous Promise Rejections
Until patched, enable unhandled rejection monitoring:
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled rejection — possible CVE-2026-59940 exploitation attempt:', reason);
// Alert your security monitoring system
});Ecosystem Impact
Seroval is used as a dependency in several popular frameworks and libraries that handle server-side serialization and hydration. Projects using solid-js, react-query integrations, or custom SSR hydration pipelines that pass user-influenced data through seroval's serialization format are potentially affected.
# Check if seroval appears in your dependency tree
npm ls seroval
# or
pnpm why serovalDetection Indicators
| Indicator | Description |
|---|---|
| Unhandled Promise rejections in logs | Deserialization state corruption from exploit attempts |
| Malformed JSON with nested numeric keys | Characteristic of crafted seroval payloads |
| Unexpected object mutations in application state | Successful object forgery |
| Process crashes in Node.js services | DoS from corrupted deserialization state |
Post-Remediation Steps
- Upgrade seroval to 1.5.3+ across all services
- Audit all transitive dependencies using
npm ls seroval - Never pass user-controlled data directly to
fromJSON()— deserialize from trusted serializers only - Add input schema validation before any deserialization step
- Enable unhandled rejection logging as an ongoing monitoring measure
- Review your SSR hydration pipeline for attacker-influenced data paths