diff --git a/bench.js b/bench.js index 18506e8..849b809 100644 --- a/bench.js +++ b/bench.js @@ -1,6 +1,6 @@ 'use strict'; /* globals bench */ -const m = require('./'); +const m = require('.'); bench('get', () => { const f1 = {foo: {bar: 1}}; diff --git a/index.js b/index.js index 15282bb..189831c 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,14 @@ 'use strict'; const isObj = require('is-obj'); +const disallowedKeys = [ + '__proto__', + 'prototype', + 'constructor' +]; + +const isValidPath = pathSegments => !pathSegments.some(segment => disallowedKeys.includes(segment)); + function getPathSegments(path) { const pathArr = path.split('.'); const parts = []; @@ -16,6 +24,10 @@ function getPathSegments(path) { parts.push(p); } + if (!isValidPath(parts)) { + return []; + } + return parts; } @@ -26,6 +38,9 @@ module.exports = { } const pathArr = getPathSegments(path); + if (pathArr.length === 0) { + return; + } for (let i = 0; i < pathArr.length; i++) { if (!Object.prototype.propertyIsEnumerable.call(obj, pathArr[i])) { @@ -58,6 +73,9 @@ module.exports = { const root = obj; const pathArr = getPathSegments(path); + if (pathArr.length === 0) { + return; + } for (let i = 0; i < pathArr.length; i++) { const p = pathArr[i]; diff --git a/package.json b/package.json index 384734a..c9b0f58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dot-prop", - "version": "4.2.0", + "version": "4.2.1", "description": "Get, set, or delete a property from a nested object using a dot path", "license": "MIT", "repository": "sindresorhus/dot-prop", @@ -38,9 +38,9 @@ "is-obj": "^1.0.0" }, "devDependencies": { - "ava": "*", + "ava": "1.4.1", "matcha": "^0.7.0", - "xo": "*" + "xo": "0.24.0" }, "xo": { "esnext": true diff --git a/readme.md b/readme.md index fab3b7a..0e18f78 100644 --- a/readme.md +++ b/readme.md @@ -85,6 +85,8 @@ Path of the property in the object, using `.` to separate each nested key. Use `\\.` if you have a `.` in the key. +The following path components are invalid and results in `undefined` being returned: `__proto__`, `prototype`, `constructor`. + #### value Type: `any` diff --git a/test.js b/test.js index 5fb3735..dbbc182 100644 --- a/test.js +++ b/test.js @@ -1,5 +1,5 @@ import test from 'ava'; -import m from './'; +import m from '.'; test('get', t => { const f1 = {foo: {bar: 1}}; @@ -199,3 +199,10 @@ test('has', t => { t.is(m.has({'foo.baz': {bar: true}}, 'foo\\.baz.bar'), true); t.is(m.has({'fo.ob.az': {bar: true}}, 'fo\\.ob\\.az.bar'), true); }); + +test('prevent setting/getting `__proto__`', t => { + m.set({}, '__proto__.unicorn', '🦄'); + t.not({}.unicorn, '🦄'); // eslint-disable-line no-use-extend-native/no-use-extend-native + + t.is(m.get({}, '__proto__'), undefined); +});