Dark Mode

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 2e609d0

Browse files
authored
fix(CVE-2020-7660): fix for RegExp.flags and Date.prototype.toISOString (#207)
* fix(CVE-2020-7660): fix for RegExp.flags and Date.prototype.toISOString * fix: add v
1 parent 42b7cdb commit 2e609d0

File tree

2 files changed

+36
-2
lines changed
  • index.js
  • test/unit
    • serialize.js

2 files changed

+36
-2
lines changed

index.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,18 @@ module.exports = function serialize(obj, options) {
248248
}
249249

250250
if (type === 'D') {
251-
return "new Date(\"" + dates[valueIndex].toISOString() + "\")";
251+
// Validate ISO string format to prevent code injection via spoofed toISOString()
252+
var isoStr = String(dates[valueIndex].toISOString());
253+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/.test(isoStr)) {
254+
throw new TypeError('Invalid Date ISO string');
255+
}
256+
return "new Date(\"" + isoStr + "\")";
252257
}
253258

254259
if (type === 'R') {
255-
return "new RegExp(" + serialize(regexps[valueIndex].source) + ", \"" + regexps[valueIndex].flags + "\")";
260+
// Sanitize flags to prevent code injection (only allow valid RegExp flag characters)
261+
var flags = String(regexps[valueIndex].flags).replace(/[^gimsuydv]/g, '');
262+
return "new RegExp(" + serialize(regexps[valueIndex].source) + ", \"" + flags + "\")";
256263
}
257264

258265
if (type === 'M') {

test/unit/serialize.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,23 @@ describe('serialize( obj )', function () {
315315
strictEqual(typeof serialize(re), 'string');
316316
strictEqual(serialize(re), 'new RegExp("[\\u003C\\\\\\u002Fscript\\u003E\\u003Cscript\\u003E alert(\'xss\')\\\\\\u002F\\\\\\u002F]", "")');
317317
});
318+
319+
it('should sanitize RegExp.flags to prevent code injection', function () {
320+
// Object that passes instanceof RegExp with attacker-controlled .flags
321+
var fakeRegex = Object.create(RegExp.prototype);
322+
Object.defineProperty(fakeRegex, 'source', { get: function () { return 'x'; } });
323+
Object.defineProperty(fakeRegex, 'flags', {
324+
get: function () { return '"+(global.__INJECTED_FLAGS="pwned")+"'; }
325+
});
326+
fakeRegex.toJSON = function () { return '@placeholder'; };
327+
var output = serialize({ re: fakeRegex });
328+
// Malicious flags must be stripped; only valid flag chars allowed
329+
strictEqual(output.includes('__INJECTED_FLAGS'), false);
330+
strictEqual(output.includes('pwned'), false);
331+
var obj = eval('obj = ' + output);
332+
strictEqual(global.__INJECTED_FLAGS, undefined);
333+
delete global.__INJECTED_FLAGS;
334+
});
318335
});
319336

320337
describe('dates', function () {
@@ -345,6 +362,16 @@ describe('serialize( obj )', function () {
345362
strictEqual(typeof serialize({t: [d]}), 'string');
346363
strictEqual(serialize({t: [d]}), '{"t":[{"foo":new Date("2016-04-28T22:02:17.156Z")}]}');
347364
});
365+
366+
it('should reject invalid Date ISO string to prevent code injection', function () {
367+
var fakeDate = Object.create(Date.prototype);
368+
fakeDate.toISOString = function () { return '"+(global.__INJECTED_DATE="pwned")+"'; };
369+
fakeDate.toJSON = function () { return '2024-01-01'; };
370+
throws(function () {
371+
serialize({ d: fakeDate });
372+
}, TypeError);
373+
strictEqual(global.__INJECTED_DATE, undefined);
374+
});
348375
});
349376

350377
describe('maps', function () {

0 commit comments

Comments
(0)