From 130bdc2744895b1d65f1f6be61688b01087575f2 Mon Sep 17 00:00:00 2001 From: Nathan LaFreniere Date: Thu, 28 Aug 2014 13:08:16 -0700 Subject: [PATCH 1/2] account for circular references properly, closes #31 --- lib/utils.js | 59 ++++++++++++++++++++++++++++++++++----------------- test/parse.js | 11 ++++++++++ 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 8274601c..31760fb6 100755 --- a/lib/utils.js +++ b/lib/utils.js @@ -20,7 +20,7 @@ exports.arrayToObject = function (source) { }; -exports.clone = function (source) { +exports.clone = function (source, refs) { if (typeof source !== 'object' || source === null) { @@ -32,14 +32,24 @@ exports.clone = function (source) { return source.toString(); } - var obj = Array.isArray(source) ? [] : {}; + refs = refs || []; + + var lookup = refs.indexOf(source); + if (lookup !== -1) { + return refs[lookup]; + } + + var copy = Array.isArray(source) ? [] : source; + + refs.push(source); + for (var i in source) { if (source.hasOwnProperty(i)) { - obj[i] = exports.clone(source[i]); + copy[i] = exports.clone(source[i], refs); } } - return obj; + return copy; }; @@ -108,32 +118,41 @@ exports.decode = function (str) { }; -exports.compact = function (obj) { +exports.compact = function (obj, refs) { + + if (typeof obj !== 'object' || + obj === null) { - if (typeof obj !== 'object' || obj === null) { return obj; } - var compacted = {}; + refs = refs || []; + var lookup = refs.indexOf(obj); + if (lookup !== -1) { + return refs[lookup]; + } - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - if (Array.isArray(obj[key])) { - compacted[key] = []; + refs.push(obj); - for (var i = 0, l = obj[key].length; i < l; i++) { - if (typeof obj[key][i] !== 'undefined') { - compacted[key].push(obj[key][i]); - } - } - } - else { - compacted[key] = exports.compact(obj[key]); + if (Array.isArray(obj)) { + var compacted = []; + + for (var i = 0, l = obj.length; i < l; ++i) { + if (typeof obj[i] !== 'undefined') { + compacted.push(obj[i]); } } + + return compacted; + } + + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + obj[key] = exports.compact(obj[key], refs); + } } - return compacted; + return obj; }; diff --git a/test/parse.js b/test/parse.js index 04f11cdd..e8e1c184 100755 --- a/test/parse.js +++ b/test/parse.js @@ -337,4 +337,15 @@ describe('#parse', function () { expect(Qs.parse('roomInfoList[0].childrenAges[0]=15&roomInfoList[0].numberOfAdults=2')).to.deep.equal({ roomInfoList: [['15', '2']] }); done(); }); + + it('does not crash when parsing circular references', function (done) { + + var a = {}; + a.b = a; + expect(function () { + + Qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a }); + }).to.not.throw(Error); + done(); + }); }); From c0d73434ec39c0d6c2b6c774ebac130d6e3dacd8 Mon Sep 17 00:00:00 2001 From: Nathan LaFreniere Date: Thu, 28 Aug 2014 13:28:24 -0700 Subject: [PATCH 2/2] more assertions for circular reference test --- test/parse.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/parse.js b/test/parse.js index e8e1c184..694adb54 100755 --- a/test/parse.js +++ b/test/parse.js @@ -342,10 +342,18 @@ describe('#parse', function () { var a = {}; a.b = a; + + var parsed; + expect(function () { - Qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a }); + parsed = Qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a }); }).to.not.throw(Error); + + expect(parsed).to.have.key('foo'); + expect(parsed.foo).to.have.keys('bar', 'baz'); + expect(parsed.foo.bar).to.equal('baz'); + expect(parsed.foo.baz).to.deep.equal(a); done(); }); });