Skip to content

Commit

Permalink
Use Object.create(null) to avoid default object property hazards
Browse files Browse the repository at this point in the history
  • Loading branch information
isaacs committed Dec 9, 2020
1 parent 2da9039 commit 032fbaf
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 19 deletions.
11 changes: 6 additions & 5 deletions ini.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function encode (obj, opt) {
whitespace: false
}
} else {
opt = opt || {}
opt = opt || Object.create(null)
opt.whitespace = opt.whitespace === true
}

Expand Down Expand Up @@ -67,7 +67,7 @@ function dotSplit (str) {
}

function decode (str) {
var out = {}
var out = Object.create(null)
var p = out
var section = null
// section |key = value
Expand All @@ -83,13 +83,14 @@ function decode (str) {
if (section === '__proto__') {
// not allowed
// keep parsing the section, but don't attach it.
p = {}
p = Object.create(null)
return
}
p = out[section] = out[section] || {}
p = out[section] = out[section] || Object.create(null)
return
}
var key = unsafe(match[2])
if (key === '__proto__') return
var value = match[3] ? unsafe(match[4]) : true
switch (value) {
case 'true':
Expand Down Expand Up @@ -133,7 +134,7 @@ function decode (str) {
var nl = l.replace(/\\\./g, '.')
parts.forEach(function (part, _, __) {
if (part === '__proto__') return
if (!p[part] || typeof p[part] !== 'object') p[part] = {}
if (!p[part] || typeof p[part] !== 'object') p[part] = Object.create(null)
p = p[part]
})
if (p === out && nl === l) {
Expand Down
41 changes: 27 additions & 14 deletions test/proto.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var t = require('tap')

var data = `
__proto__ = quux
constructor.prototype.foo = asdfasdf
foo = baz
[__proto__]
foo = bar
Expand All @@ -15,27 +16,39 @@ hello = snyk
__proto__[] = you did a good job
__proto__[] = so you deserve arrays
thanks = true
[ctor.constructor.prototype]
foo = asdfasdf
`

var res = ini.parse(data)
t.deepEqual(res, {

t.deepEqual(res, Object.assign(Object.create(null), {
'constructor.prototype.foo': 'asdfasdf',
foo: 'baz',
other: {
other: Object.assign(Object.create(null), {
foo: 'asdf',
},
kid: {
foo: {
}),
kid: Object.assign(Object.create(null), {
foo: Object.assign(Object.create(null), {
foo: 'kid',
},
},
arrproto: {
}),
}),
arrproto: Object.assign(Object.create(null), {
hello: 'snyk',
thanks: true,
},
})
t.equal(res.__proto__, Object.prototype)
t.equal(res.kid.__proto__, Object.prototype)
t.equal(res.kid.foo.__proto__, Object.prototype)
t.equal(res.arrproto.__proto__, Object.prototype)
}),
ctor: Object.assign(Object.create(null), {
constructor: Object.assign(Object.create(null), {
prototype: Object.assign(Object.create(null), {
foo: 'asdfasdf',
}),
}),
}),
}))
t.equal(res.__proto__, undefined)
t.equal(res.kid.__proto__, undefined)
t.equal(res.kid.foo.__proto__, undefined)
t.equal(res.arrproto.__proto__, undefined)
t.equal(Object.prototype.foo, undefined)
t.equal(Object.prototype[0], undefined)
t.equal(Object.prototype['0'], undefined)
Expand Down

1 comment on commit 032fbaf

@rstacruz
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty interesting, thanks for introducing us to Object.create(null). What are the advantages of using Object.create(null) over {}?

Please sign in to comment.