From 4d3baba924a48719ee7fb37264c66411955b17fd Mon Sep 17 00:00:00 2001 From: redonkulus Date: Wed, 25 Feb 2026 13:38:53 -0800 Subject: [PATCH 1/2] fix(CVE-2020-7660): fix for RegExp.flags and Date.prototype.toISOString --- index.js | 11 +++++++++-- test/unit/serialize.js | 27 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 8970953..8794ef7 100644 --- a/index.js +++ b/index.js @@ -248,11 +248,18 @@ module.exports = function serialize(obj, options) { } if (type === 'D') { - return "new Date(\"" + dates[valueIndex].toISOString() + "\")"; + // Validate ISO string format to prevent code injection via spoofed toISOString() + var isoStr = String(dates[valueIndex].toISOString()); + if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/.test(isoStr)) { + throw new TypeError('Invalid Date ISO string'); + } + return "new Date(\"" + isoStr + "\")"; } if (type === 'R') { - return "new RegExp(" + serialize(regexps[valueIndex].source) + ", \"" + regexps[valueIndex].flags + "\")"; + // Sanitize flags to prevent code injection (only allow valid RegExp flag characters) + var flags = String(regexps[valueIndex].flags).replace(/[^gimsuyd]/g, ''); + return "new RegExp(" + serialize(regexps[valueIndex].source) + ", \"" + flags + "\")"; } if (type === 'M') { diff --git a/test/unit/serialize.js b/test/unit/serialize.js index f850d6e..db1f9a0 100644 --- a/test/unit/serialize.js +++ b/test/unit/serialize.js @@ -315,6 +315,23 @@ describe('serialize( obj )', function () { strictEqual(typeof serialize(re), 'string'); strictEqual(serialize(re), 'new RegExp("[\\u003C\\\\\\u002Fscript\\u003E\\u003Cscript\\u003Ealert(\'xss\')\\\\\\u002F\\\\\\u002F]", "")'); }); + + it('should sanitize RegExp.flags to prevent code injection', function () { + // Object that passes instanceof RegExp with attacker-controlled .flags + var fakeRegex = Object.create(RegExp.prototype); + Object.defineProperty(fakeRegex, 'source', { get: function () { return 'x'; } }); + Object.defineProperty(fakeRegex, 'flags', { + get: function () { return '"+(global.__INJECTED_FLAGS="pwned")+"'; } + }); + fakeRegex.toJSON = function () { return '@placeholder'; }; + var output = serialize({ re: fakeRegex }); + // Malicious flags must be stripped; only valid flag chars allowed + strictEqual(output.includes('__INJECTED_FLAGS'), false); + strictEqual(output.includes('pwned'), false); + var obj = eval('obj = ' + output); + strictEqual(global.__INJECTED_FLAGS, undefined); + delete global.__INJECTED_FLAGS; + }); }); describe('dates', function () { @@ -345,6 +362,16 @@ describe('serialize( obj )', function () { strictEqual(typeof serialize({t: [d]}), 'string'); strictEqual(serialize({t: [d]}), '{"t":[{"foo":new Date("2016-04-28T22:02:17.156Z")}]}'); }); + + it('should reject invalid Date ISO string to prevent code injection', function () { + var fakeDate = Object.create(Date.prototype); + fakeDate.toISOString = function () { return '"+(global.__INJECTED_DATE="pwned")+"'; }; + fakeDate.toJSON = function () { return '2024-01-01'; }; + throws(function () { + serialize({ d: fakeDate }); + }, TypeError); + strictEqual(global.__INJECTED_DATE, undefined); + }); }); describe('maps', function () { From b8b4a6f5850bb8dc596cfcaeb3b81b015c27db8d Mon Sep 17 00:00:00 2001 From: redonkulus Date: Thu, 26 Feb 2026 09:09:46 -0800 Subject: [PATCH 2/2] fix: add v --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 8794ef7..e298ab4 100644 --- a/index.js +++ b/index.js @@ -258,7 +258,7 @@ module.exports = function serialize(obj, options) { if (type === 'R') { // Sanitize flags to prevent code injection (only allow valid RegExp flag characters) - var flags = String(regexps[valueIndex].flags).replace(/[^gimsuyd]/g, ''); + var flags = String(regexps[valueIndex].flags).replace(/[^gimsuydv]/g, ''); return "new RegExp(" + serialize(regexps[valueIndex].source) + ", \"" + flags + "\")"; }