Security Vulnerability Report
Type: Cross-Site Scripting (XSS) — </script> Injection via Unsanitized Function Body Serialization
Package: serialize-javascript
Version: 6.0.2 (latest)
CVSS 4.0 Score: 7.5 (High)
Summary
serialize-javascript escapes <, >, /, and other unsafe HTML characters to make serialized output safe for embedding inside <script> tags. However, this HTML escaping is applied before function placeholders are replaced with actual function source code.
As a result, if a function body contains </script>, the closing tag is inserted into the final output without any escaping, allowing an attacker to break out of the <script> context and execute arbitrary JavaScript.
Root Cause
The processing order in index.js:
JSON.stringify() replaces functions with "@__F-<uid>-0__@" placeholders
- HTML escaping is applied — but only to the placeholder string, not to actual function source
- Placeholder is replaced with raw
serializeFunc(fn) output — no escaping applied here
The escaping in step 2 is therefore ineffective for function values.
Impact
Applications that:
- Serialize objects containing functions into
<script> tags (SSR pattern)
- Use dynamic functions where the body is influenced by user input (e.g.,
new Function('v', userInput))
...are vulnerable to XSS that executes on page load without any user interaction (no click required).
Affected Code
index.js — placeholder replacement without re-escaping:
return str.replace(PLACE_HOLDER_REGEXP, function(match, backSlash, type, valueIndex) {
// ...
var fn = functions[valueIndex];
return serializeFunc(fn); // raw source, no HTML escaping
});
Suggested Fix
Apply HTML escaping to the serialized function source at substitution time:
if (type === 'F') {
var fn = functions[valueIndex];
var serialized = serializeFunc(fn);
if (options.unsafe !== true) {
serialized = serialized.replace(UNSAFE_CHARS_REGEXP, escapeUnsafeChars);
}
return serialized;
}
Workaround (for users)
Until a fix is released:
// Option 1: disable function serialization entirely
serialize(obj, { ignoreFunction: true });
// Option 2: validate output after serialization
function safeSerialize(obj) {
const result = serialize(obj);
if (result.includes('</script>') || result.includes('</SCRIPT>')) {
throw new Error('Dangerous content detected in serialized function body');
}
return result;
}
Disclosure
This issue is being reported following responsible disclosure principles. I am providing 90 days from today (2026-05-28) for a fix before public disclosure.
Private vulnerability reporting does not appear to be enabled for this repository. If there is a preferred private channel (e.g., security email, security.txt), please let me know and I will re-submit through that channel.
Reported by: natekimyoungee9@gmail.com
Security Vulnerability Report
Type: Cross-Site Scripting (XSS) —
</script>Injection via Unsanitized Function Body SerializationPackage: serialize-javascript
Version: 6.0.2 (latest)
CVSS 4.0 Score: 7.5 (High)
Summary
serialize-javascriptescapes<,>,/, and other unsafe HTML characters to make serialized output safe for embedding inside<script>tags. However, this HTML escaping is applied before function placeholders are replaced with actual function source code.As a result, if a function body contains
</script>, the closing tag is inserted into the final output without any escaping, allowing an attacker to break out of the<script>context and execute arbitrary JavaScript.Root Cause
The processing order in
index.js:JSON.stringify()replaces functions with"@__F-<uid>-0__@"placeholdersserializeFunc(fn)output — no escaping applied hereThe escaping in step 2 is therefore ineffective for function values.
Impact
Applications that:
<script>tags (SSR pattern)new Function('v', userInput))...are vulnerable to XSS that executes on page load without any user interaction (no click required).
Affected Code
index.js— placeholder replacement without re-escaping:Suggested Fix
Apply HTML escaping to the serialized function source at substitution time:
Workaround (for users)
Until a fix is released:
Disclosure
This issue is being reported following responsible disclosure principles. I am providing 90 days from today (2026-05-28) for a fix before public disclosure.
Private vulnerability reporting does not appear to be enabled for this repository. If there is a preferred private channel (e.g., security email,
security.txt), please let me know and I will re-submit through that channel.Reported by: natekimyoungee9@gmail.com