diff --git a/README.md b/README.md index 1e0d9adc..7347a3c4 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ assert.deepEqual(qs.parse('foo[bar]=baz'), { }); ``` -When using the `plainObjects` option the parsed value is returned as a null object, created via `Object.create(null)` and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like: +When using the `plainObjects` option the parsed value is returned as a null object, created via `{ __proto__: null }` and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like: ```javascript var nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true }); diff --git a/lib/parse.js b/lib/parse.js index f7fadc07..f6dabf14 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -132,7 +132,7 @@ var parseObject = function (chain, val, options, valuesParsed) { ? [] : [].concat(leaf); } else { - obj = options.plainObjects ? Object.create(null) : {}; + obj = options.plainObjects ? { __proto__: null } : {}; var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root; var decodedRoot = options.decodeDotInKeys ? cleanRoot.replace(/%2E/g, '.') : cleanRoot; var index = parseInt(decodedRoot, 10); @@ -274,11 +274,11 @@ module.exports = function (str, opts) { var options = normalizeParseOptions(opts); if (str === '' || str === null || typeof str === 'undefined') { - return options.plainObjects ? Object.create(null) : {}; + return options.plainObjects ? { __proto__: null } : {}; } var tempObj = typeof str === 'string' ? parseValues(str, options) : str; - var obj = options.plainObjects ? Object.create(null) : {}; + var obj = options.plainObjects ? { __proto__: null } : {}; // Iterate over the keys and setup the new object diff --git a/lib/utils.js b/lib/utils.js index d325ffdb..2905ff01 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -34,7 +34,7 @@ var compactQueue = function compactQueue(queue) { }; var arrayToObject = function arrayToObject(source, options) { - var obj = options && options.plainObjects ? Object.create(null) : {}; + var obj = options && options.plainObjects ? { __proto__: null } : {}; for (var i = 0; i < source.length; ++i) { if (typeof source[i] !== 'undefined') { obj[i] = source[i]; diff --git a/package.json b/package.json index b7182e06..2b11951b 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,10 @@ "evalmd": "^0.0.19", "for-each": "^0.3.3", "glob": "=10.3.7", + "has-bigints": "^1.0.2", "has-override-mistake": "^1.0.1", "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", "iconv-lite": "^0.5.1", "in-publish": "^2.0.1", diff --git a/test/parse.js b/test/parse.js index 5d7b3e58..d678ac1d 100644 --- a/test/parse.js +++ b/test/parse.js @@ -9,6 +9,7 @@ var SaferBuffer = require('safer-buffer').Buffer; var v = require('es-value-fixtures'); var inspect = require('object-inspect'); var emptyTestCases = require('./empty-keys-cases').emptyTestCases; +var hasProto = require('has-proto')(); var qs = require('../'); var utils = require('../lib/utils'); @@ -691,9 +692,8 @@ test('parse()', function (t) { st.end(); }); - t.test('parses null objects correctly', { skip: !Object.create }, function (st) { - var a = Object.create(null); - a.b = 'c'; + t.test('parses null objects correctly', { skip: !hasProto }, function (st) { + var a = { __proto__: null, b: 'c' }; st.deepEqual(qs.parse(a), { b: 'c' }); var result = qs.parse({ a: a }); @@ -870,17 +870,25 @@ test('parse()', function (t) { st.end(); }); - t.test('can return null objects', { skip: !Object.create }, function (st) { - var expected = Object.create(null); - expected.a = Object.create(null); - expected.a.b = 'c'; - expected.a.hasOwnProperty = 'd'; + t.test('can return null objects', { skip: !hasProto }, function (st) { + var expected = { + __proto__: null, + a: { + __proto__: null, + b: 'c', + hasOwnProperty: 'd' + } + }; st.deepEqual(qs.parse('a[b]=c&a[hasOwnProperty]=d', { plainObjects: true }), expected); - st.deepEqual(qs.parse(null, { plainObjects: true }), Object.create(null)); - var expectedArray = Object.create(null); - expectedArray.a = Object.create(null); - expectedArray.a[0] = 'b'; - expectedArray.a.c = 'd'; + st.deepEqual(qs.parse(null, { plainObjects: true }), { __proto__: null }); + var expectedArray = { + __proto__: null, + a: { + __proto__: null, + 0: 'b', + c: 'd' + } + }; st.deepEqual(qs.parse('a[]=b&a[c]=d', { plainObjects: true }), expectedArray); st.end(); }); diff --git a/test/stringify.js b/test/stringify.js index 75eaf738..72531441 100644 --- a/test/stringify.js +++ b/test/stringify.js @@ -8,7 +8,8 @@ var SaferBuffer = require('safer-buffer').Buffer; var hasSymbols = require('has-symbols'); var mockProperty = require('mock-property'); var emptyTestCases = require('./empty-keys-cases').emptyTestCases; -var hasBigInt = typeof BigInt === 'function'; +var hasProto = require('has-proto')(); +var hasBigInt = require('has-bigints')(); test('stringify()', function (t) { t.test('stringifies a querystring object', function (st) { @@ -650,10 +651,8 @@ test('stringify()', function (t) { st.end(); }); - t.test('stringifies a null object', { skip: !Object.create }, function (st) { - var obj = Object.create(null); - obj.a = 'b'; - st.equal(qs.stringify(obj), 'a=b'); + t.test('stringifies a null object', { skip: !hasProto }, function (st) { + st.equal(qs.stringify({ __proto__: null, a: 'b' }), 'a=b'); st.end(); }); @@ -665,11 +664,8 @@ test('stringify()', function (t) { st.end(); }); - t.test('stringifies an object with a null object as a child', { skip: !Object.create }, function (st) { - var obj = { a: Object.create(null) }; - - obj.a.b = 'c'; - st.equal(qs.stringify(obj), 'a%5Bb%5D=c'); + t.test('stringifies an object with a null object as a child', { skip: !hasProto }, function (st) { + st.equal(qs.stringify({ a: { __proto__: null, b: 'c' } }), 'a%5Bb%5D=c'); st.end(); });