CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-115897

CVE-2026-115897: Cross-Site Scripting via Function Body Boundary Bypass in serialize-javascript

Amit Schendel
Amit Schendel
Senior Security Researcher

Oct 24, 2026·13 min read·3 visits

Executive Summary (TL;DR)

Versions of the serialize-javascript npm package prior to 7.1.3 fail to neutralize the bare string '</script' when it appears at the exact end of a serialized function body. If an application serializes attacker-influenced function representations with formatting options enabled, the surrounding serialization structure can supply the necessary HTML tag delimiter and closing angle bracket, resulting in Cross-Site Scripting (XSS) when the output is embedded in an HTML <script> element.

CVE-2026-115897 (also tracked as GHSA-m5mm-p8h7-m29v and aliased as CVE-2026-115754) is a Cross-Site Scripting (XSS) vulnerability in the Yahoo serialize-javascript npm package prior to version 7.1.3. The flaw exists in the regular expression used by escapeFunctionBody() to neutralize HTML script closing tags within serialized JavaScript functions. Because function bodies are sanitized in isolation before structural formatting is applied, a function whose string representation terminates directly with the sequence '</script' bypasses the positive lookahead check. When combined with serialization formatting options that introduce whitespace delimiters and a subsequent property that supplies a closing bracket, an attacker can prematurely terminate an enclosing HTML <script> block and inject arbitrary HTML or JavaScript into the rendered document.

Vulnerability Overview

The serialize-javascript package, maintained by Yahoo, is widely used in Node.js server-side rendering (SSR) frameworks and web applications to serialize JavaScript objects into a superset of JSON. Unlike standard JSON.stringify(), which strips functions, regular expressions, dates, and undefined values, serialize-javascript preserves these rich data types as executable JavaScript source code. A primary use case for this library is serializing application state on the server and embedding the resulting string directly inside an HTML <script> element for hydration on the client.

Because the serialized output is designed to be placed directly inside <script>...</script> blocks, a core security requirement of serialize-javascript is neutralizing sequences that an HTML parser would interpret as script termination tags. Under the WHATWG HTML specification, a browser tokenizer operating in the script data state will exit the script block if it encounters the case-insensitive sequence </script followed immediately by an HTML end-tag delimiter (whitespace, /, or >). If an attacker can smuggle an unescaped closing script tag into the serialized output, they can break out of the JavaScript execution context and inject arbitrary HTML markup into the page.

In CVE-2026-115897 (tracked internally by the vendor as PSECBUGS-118415 and aliased as GHSA-m5mm-p8h7-m29v and CVE-2026-115754), this context-escaping mechanism fails under specific boundary conditions involving serialized functions. When a function's source representation ends with the bare prefix </script, the library's sanitization logic fails to identify it as a dangerous sequence because no trailing delimiter is present within the isolated function string itself.

When the sanitized function string is subsequently reassembled into the parent object structure, formatting options (such as indentation spaces or newlines passed via the space parameter) can immediately follow the function body. This formatting supplies the missing HTML end-tag delimiter right after the unescaped </script prefix. If a subsequent property in the serialized object supplies a closing > character and malicious markup, the browser prematurely closes the <script> tag and executes the injected payload, resulting in Cross-Site Scripting (CWE-79).

Root Cause Analysis

To understand the root cause of CVE-2026-115897, it is necessary to examine how serialize-javascript processes different data types during serialization. For standard string values and regular expressions, the library replaces all occurrences of <, >, /, and Unicode line terminators (\u2028, \u2029) with their corresponding Unicode escape sequences (e.g., \u003C). However, this blanket character replacement cannot be applied to serialized functions. Escaping all <, >, or / characters inside a function body would corrupt valid JavaScript syntax, such as comparison operators (a < b), arrow functions (=>), division operators, or regular expression literals.

To preserve valid JavaScript syntax while preventing HTML script breakout, serialize-javascript processes functions through a dedicated pipeline. During the initial pass over the input object, JSON.stringify() is called with a custom replacer function that replaces any encountered function with a unique string placeholder (@__F-<UID>-<index>__@). In a post-processing step, these placeholders are replaced with the actual function source code obtained via fn.toString() and sanitized by the internal helper escapeFunctionBody().

Prior to version 7.1.3, escapeFunctionBody() relied on the regular expression SCRIPT_CLOSE_REGEXP to locate dangerous closing script sequences within the function source code:

var SCRIPT_CLOSE_REGEXP = /<\/script[^<>]*>|<\/script(?=[\t\n\f\r \/>])/gi;

This regular expression matches two distinct patterns: a complete closing tag (<\/script[^<>]*>) or a bare prefix (<\/script) followed by a positive lookahead (?=[\t\n\f\r \/>]). The lookahead was designed to match the exact set of characters that the HTML5 specification defines as valid end-tag delimiters in the script data state: tab (\t), line feed (\n), form feed (\f), carriage return (\r), space ( ), solidus (/), or greater-than (>). The library matched the bare prefix without requiring a closing > inside the same function because a matching > could be supplied by a separate serialized value later in the output.

The vulnerability arises because escapeFunctionBody() executes on each function's string representation in complete isolation before placeholder substitution occurs. If a function's string representation ends immediately with the characters </script (for example, inside a trailing single-line comment function f(){}//</script), there are no characters following </script during the execution of escapeFunctionBody(). Consequently, the positive lookahead (?=[\t\n\f\r \/>]) evaluates against the end of the string, fails to find a matching delimiter character, and leaves the trailing </script sequence unmodified.

Once escapeFunctionBody() returns the unmodified string, serialize() substitutes it back into the JSON string in place of its @__F-...__@ placeholder. If the caller invoked serialize(obj, { space: 2 }) or similar formatting options, JSON.stringify() will have placed a newline character (\n) immediately after the placeholder. In the final concatenated output, the trailing </script from the function body is now directly adjacent to the \n from the JSON formatting, creating a valid HTML end-tag prefix (</script\n) that only requires a subsequent > anywhere later in the output to complete the tag and break out of the script context.

Code Analysis

The vulnerability resides in index.js within the yahoo/serialize-javascript repository. When serializing functions, the library invokes serializeFunc(), which extracts the function source via .toString() and passes it to escapeFunctionBody() unless options.unsafe is explicitly set to true.

function serializeFunc(fn, options) {
  var serializedFn = fn.toString();
  if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) {
    throw new TypeError('Serializing native function: ' + fn.name);
  }
 
  // pure functions, example: {key: function() {}}
  if (IS_PURE_FUNCTION.test(serializedFn)) {
    return !(options && options.unsafe) ? escapeFunctionBody(serializedFn) : serializedFn;
  }
  ...

In Commit 2864b3c6260a3bfd52d2db3e70d1a079de108a61 (Pull Request #235), the maintainers updated SCRIPT_CLOSE_REGEXP in index.js to account for the end-of-string boundary within isolated function bodies:

diff --git a/index.js b/index.js
--- a/index.js
+++ b/index.js
@@ -26,7 +26,11 @@ var UNSAFE_CHARS_REGEXP   = /[<>\/\u2028\u2029]/g;
 // stays in script data, so only these need neutralizing. The bare-prefix form
 // matters because the matching `>` could be supplied by a different
 // serialized value later in the output, so escaping stops there without
-// waiting for a closing `>`. A trailing backslash is not a delimiter here
+// waiting for a closing `>`. For the same reason the bare form also matches
+// at the very end of the string: each function body is escaped on its own, so
+// a body ending in `</script` can't see the delimiter (e.g. the newline added
+// by the `space` option) that the surrounding output supplies next
+// (PSECBUGS-118415). A trailing backslash is not a delimiter here
 // (that's a JS-level concern, not an HTML one), so this doesn't affect tagged
 // template literals like `String.raw`.
 //
@@ -38,7 +42,7 @@ var UNSAFE_CHARS_REGEXP   = /[<>\/\u2028\u2029]/g;
 // longer contain a second `<`, every `</script` in the source is either the
 // start of its own match or is followed by a non-delimiter — and the latter
 // the tokenizer never treats as an end tag.
-var SCRIPT_CLOSE_REGEXP = /<\/script[^<>]*>|<\/script(?=[\t\n\f\r \/>])/gi;
+var SCRIPT_CLOSE_REGEXP = /<\/script[^<>]*>|<\/script(?=$|[\t\n\f\r \/>])/gi;
 
 var RESERVED_SYMBOLS = ['*', 'async'];

The patch modifies the positive lookahead in the second branch of SCRIPT_CLOSE_REGEXP from (?=[\t\n\f\r \/>]) to (?=$|[\t\n\f\r \/>]). By adding the end-of-input anchor $ as an alternative inside the lookahead assertion, any occurrence of </script at the exact end of a function string is now matched during escapeFunctionBody() execution.

When escapeFunctionBody() finds a match via SCRIPT_CLOSE_REGEXP, it determines whether the match lies within a JavaScript string/template/comment span or within code tokens. If the trailing </script is at the end of a single-line comment (//</script), it is replaced with the Unicode-escaped sequence \u003C\u002Fscript. If it occurs in a code position (such as division following a block closure }</script), it is rewritten as < /script, inserting a space between < and / to prevent the HTML tokenizer from entering the end-tag open state while preserving valid JavaScript syntax.

Fix Completeness Assessment

The fix directly addresses the boundary discrepancy between isolated function sanitization and final document assembly. Because escapeFunctionBody() is the sole sanitization path that permits raw < and > characters in the output (all other serialized types undergo unconditional replacement of [<>\/\u2028\u2029]), anchoring the bare </script prefix check to both the HTML delimiter character class and the end-of-string anchor $ closes the gap where external formatting tokens could complete the tag delimiter requirement.

Exploitation

Exploiting CVE-2026-115897 requires specific application conditions, which is reflected in its CVSS v4.0 Attack Complexity (AC:H) and Attack Requirements (AT:P) ratings. First, the target application must pass an object containing functions (or objects with a custom/spoofed .toString() method that is treated as a function) into serialize(). Second, the attacker must be able to control or influence the source representation of at least one function so that it ends with </script, as well as a subsequent serialized function or unescaped code structure that contains a closing > character and the desired HTML payload.

Third, the serialization output must supply a valid HTML end-tag delimiter immediately following the first function body. In typical JSON structures without formatting, a function value inside an object or array is immediately followed by ,, }, or ]. Because none of ,, }, or ] are HTML end-tag delimiters (\t, \n, \f, \r, space, /, >), </script, or </script} will not close the <script> tag in a standard browser parser. However, if the space option in serialize(obj, { space: 2 }) is used, JSON.stringify outputs a newline (\n) immediately after nested object properties before the closing brace, supplying the required delimiter.

The following proof-of-concept, derived from the unit test suite added in Pull Request #235 (test/unit/serialize.js), demonstrates how two serialized functions interact with the space option to achieve script breakout:

const serialize = require('serialize-javascript');
 
// 1. First function overrides toString() to end strictly with '</script'
var f = function () {};
f.toString = function () {
    return 'function f(){ return 1 }//</script';
};
 
// 2. Second function supplies the closing '>' and the injected HTML payload
var g = function () {};
g.toString = function () {
    return 'function g(x){ return x } > <img src=x onerror=alert(1)>';
};
 
// 3. Serialize with formatting enabled (space: 2)
var payload = serialize({ a: { f: f }, g: g }, { space: 2 });
console.log('<script>var state = ' + payload + ';</script>');

When executed on serialize-javascript version 7.1.2 or earlier, the snippet produces the following HTML structure:

<script>var state = {
  "a": {
    "f": function f(){ return 1 }//</script
  },
  "g": function g(x){ return x } > <img src=x onerror=alert(1)>
};</script>

When a browser parses this HTML:

  1. The tokenizer enters the <script> block and parses var state = ... // as script data.
  2. It encounters </script followed immediately by \n (the newline inserted after "f" before }).
  3. Because \n is a valid whitespace delimiter in the HTML script data end tag open state, the tokenizer transitions to the attribute state and consumes }, "g": function g(x){ return x } as malformed attributes on the closing </script> tag.
  4. Upon encountering the > inside function g, the tokenizer closes the <script> element.
  5. The remaining string <img src=x onerror=alert(1)> is parsed in the standard HTML data state, rendering the image element and executing the onerror JavaScript handler in the victim's browser.

Impact Assessment

The primary impact of CVE-2026-115897 is Cross-Site Scripting (XSS) within the client browser rendering the serialized output. If an attacker successfully exploits this flaw, they can execute arbitrary JavaScript within the origin of the vulnerable web application. This allows the attacker to read or modify sensitive data accessible to the DOM, steal session tokens or authentication cookies (if not marked HttpOnly), perform unauthorized actions on behalf of the authenticated user, or alter the visual presentation of the page.

Despite the severity of XSS once triggered, the vulnerability carries a CVSS v4.0 base score of 2.1 (Low) with the vector CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N. The score is constrained by the stringent prerequisites required for exploitation (AC:H and AT:P). Standard user input is rarely converted into JavaScript Function objects prior to serialization, and standard strings are unaffected because their <, >, and / characters are unconditionally escaped via UNSAFE_CHARS_REGEXP.

For an application to be vulnerable in practice, it must serialize objects containing functions where the function body or its .toString() method is influenced by untrusted input, and it must either use the space formatting option or serialize a structure where a whitespace delimiter naturally follows the function placeholder. Furthermore, a second unescaped > must appear later in the serialized stream (such as inside another serialized function body) because > characters in ordinary string properties are converted to \u003E.

At the time of publication, there is no evidence of active exploitation in the wild, and the vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog. However, because serialize-javascript is a foundational dependency in many server-side rendering toolchains and state hydration libraries, downstream applications that dynamically construct or transpile functions from user-supplied templates or configuration files should treat this flaw as a defense-in-depth priority.

Remediation

The recommended remediation is to upgrade serialize-javascript to version 7.1.3 or later. Version 7.1.3 updates SCRIPT_CLOSE_REGEXP to detect and neutralize </script sequences occurring at the exact end of serialized function bodies.

Administrators and developers can update the package using their standard Node.js package manager:

# Using npm
npm install serialize-javascript@^7.1.3
 
# Using Yarn
yarn add serialize-javascript@^7.1.3
 
# Using pnpm
pnpm add serialize-javascript@^7.1.3

> [!NOTE] > Because serialize-javascript is frequently included as a transitive dependency by SSR frameworks, bundlers, and state management libraries (such as Redux or Webpack plugins), developers should inspect their lockfiles (package-lock.json, yarn.lock, or pnpm-lock.yaml) using npm ls serialize-javascript and apply package overrides or resolutions if a parent dependency pins an older version.

For environments where an immediate upgrade is not possible, several configuration and architectural mitigations can eliminate the attack surface:

  1. Disable Function Serialization: If the application only needs to serialize data primitives, dates, regular expressions, or collections (Maps/Sets) and does not require serializing executable functions, pass { ignoreFunction: true } in the options object when calling serialize(obj, { ignoreFunction: true }). This instructs the library to skip function properties entirely.
  2. Omit Formatting in Production: Avoid passing the space option (e.g., { space: 2 }) when serializing state for embedding into HTML <script> blocks. Without formatting whitespace inserted after function bodies, the comma or closing brace immediately following a function value prevents the HTML tokenizer from recognizing </script as a valid closing tag.
  3. Enforce Content Security Policy (CSP): Implement a strict Content Security Policy using cryptographic nonces or hashes (script-src 'nonce-...' 'strict-dynamic') to restrict the execution of unauthorized inline scripts and event handlers (onerror, onload) injected via HTML breakout.

Official Patches

YahooCommit fixing PSECBUGS-118415 by anchoring SCRIPT_CLOSE_REGEXP lookahead to end-of-string
Yahooserialize-javascript v7.1.3 official release tag
GitHubGitHub Security Advisory GHSA-m5mm-p8h7-m29v

Fix Analysis (1)

Technical Appendix

CVSS Score
2.1/ 10
CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N

Affected Systems

serialize-javascript (npm package)

Affected Versions Detail

Product
Affected Versions
Fixed Version
serialize-javascript
Yahoo
< 7.1.37.1.3
AttributeDetail
Vulnerability TypeCross-Site Scripting (CWE-79)
Affected Componentserialize-javascript (< 7.1.3) escapeFunctionBody()
CVSS v4.0 Score2.1 Low (CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N)
Attack VectorNetwork (HTML <script> breakout via serialized function boundary)
Exploit StatusProof-of-Concept (Unit test fixtures available)
CISA KEV StatusNot Listed
Patched Version7.1.3

MITRE ATT&CK Mapping

T1189Drive-by Compromise
Initial Access
T1185Browser Session Hijacking
Collection
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.

Vulnerability Timeline

Fix committed to yahoo/serialize-javascript repository (commit 2864b3c6260a3bfd52d2db3e70d1a079de108a61, PR #235).
2026-10-22
Version 7.1.3 tagged and released to the npm registry.
2026-10-22
GitHub Security Advisory GHSA-m5mm-p8h7-m29v and CVE-2026-115897 published.
2026-10-23
OSV and NVD records updated with commit range metadata and CWE-79 classification.
2026-10-24

References & Sources

  • [1]GitHub Security Advisory GHSA-m5mm-p8h7-m29v
  • [2]Pull Request #235: fix: escape '</script' at the end of a serialized function body
  • [3]Patch Commit 2864b3c6260a3bfd52d2db3e70d1a079de108a61
  • [4]serialize-javascript v7.1.3 Release Notes
  • [5]NVD - CVE-2026-115897
  • [6]CVE.org Record - CVE-2026-115897
  • [7]CVE List V5 JSON Record - CVE-2026-115897
Related Vulnerabilities
CVE-2026-115754

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 1 hour ago•CVE-2026-113872
7.5

CVE-2026-113872: Unauthenticated Process Crash in Express Multer via Late Busboy Stream Resumption

CVE-2026-113872 (GHSA-q7qp-rjg7-q26m) is a high-severity denial-of-service vulnerability affecting the multer multipart/form-data upload middleware for Node.js and Express in versions 2.0.0 through 2.4.0. When an endpoint is configured to accept multiple files using an asynchronous fileFilter callback, a crafted, truncated multipart HTTP request can trigger an unhandled error event on the underlying busboy stream parser after request teardown has already completed. Because all error listeners are stripped during teardown, Node.js treats the resulting error event as an uncaught exception and immediately terminates the server process.

Amit Schendel
Amit Schendel
3 views•12 min read
•about 3 hours ago•CVE-2026-109841
5.9

CVE-2026-109841: Subresource Integrity Verification Bypass in Node.js Undici

CVE-2026-109841 is a Subresource Integrity (SRI) verification bypass vulnerability in undici, the official HTTP/1.1 and HTTP/2 client library for Node.js that powers the global fetch() implementation. The flaw stems from improper handling of case sensitivity (CWE-178) when validating cryptographic hash algorithm tokens within SRI metadata. Because undici compared incoming algorithm identifiers against a strictly lowercase set without prior ASCII-lowercase normalization, valid uppercase or mixed-case tokens such as SHA256- or Sha384- were treated as unrecognized algorithms and discarded. Under SRI forward-compatibility rules, discarding all provided tokens results in an empty integrity list, causing undici to fail open and accept tampered HTTP response bodies without cryptographic verification.

Alon Barad
Alon Barad
2 views•9 min read
•about 4 hours ago•CVE-2026-109924
5.9

CVE-2026-109924: Unbounded Memory Allocation in Node.js Undici EventSource Stream Parser

A resource allocation vulnerability (CWE-770) exists in the Server-Sent Events (SSE) parser within the Node.js undici HTTP client library. In affected versions, the EventSourceStream implementation fails to restrict the count of internal Buffer chunk references and accumulated string nodes prior to event dispatch boundaries. A malicious or compromised SSE endpoint can exploit this behavior by streaming fragmented, unterminated lines or continuous sequences of short data fields without empty separator lines, bypassing configured byte-size limits and causing a fatal Out-of-Memory (OOM) crash in the client Node.js process.

Amit Schendel
Amit Schendel
5 views•11 min read
•about 4 hours ago•CVE-2026-109869
7.5

CVE-2026-109869: Denial of Service via Orphaned Stream in Node.js Undici Fetch Implementation

CVE-2026-109869 is a high-severity denial-of-service vulnerability in undici, the default HTTP/1.1 client and Fetch implementation for Node.js. When a server responds with more than five Content-Encoding directives and prematurely closes the connection before transmitting the body, undici allocates an internal Readable stream before enforcing the content-encoding limit. Upon rejecting the fetch promise, the stream is orphaned without an error listener or abort signal, causing the premature socket closure to emit an unhandled stream error that terminates the Node.js process.

Alon Barad
Alon Barad
7 views•10 min read
•about 4 hours ago•CVE-2026-114612
9.9

CVE-2026-114612: Privilege Escalation to Proxy Admin via Cryptographic Context Reuse in LiteLLM

LiteLLM versions 1.87.0 through 1.104.0-rc.1 contain a cryptographic domain separation vulnerability (CWE-269, CWE-345, CWE-441) that allows an authenticated low-privilege user to escalate privileges to full proxy administrator. The application reuses a single symmetric cryptographic context and key derivation path for both at-rest secret encryption and ephemeral UI/CLI session authentication tokens without providing Associated Authenticated Data (AAD). By submitting crafted JSON session claims into user-accessible encrypted metadata fields, an attacker can use the API-key generation flow as an encryption oracle, extract the resulting ciphertext, and replay it as a bearer token to achieve administrative control and subsequent remote command execution via the Model Context Protocol (MCP) stdio interface.

Alon Barad
Alon Barad
8 views•9 min read
•about 5 hours ago•CVE-2026-109461
5.9

CVE-2026-109461: Origin Validation Error in @azure/msal-node Leads to Information Disclosure

An origin validation vulnerability in @azure/msal-node allows attackers to process unchecked authorization code response payloads or request query parameters in ConfidentialClientApplication, potentially directing sensitive client credentials to external endpoints.

Amit Schendel
Amit Schendel
11 views•4 min read