├── .gitignore ├── .jshintrc ├── README.md ├── LICENSE-MIT ├── .github └── workflows │ └── test.yml ├── package.json ├── Gruntfile.js ├── lib └── getobject.js └── test └── namespace_test.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "unused": true, 11 | "boss": true, 12 | "eqnull": true, 13 | "node": true, 14 | "es5": true 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # getobject [![Build Status](https://github.com/cowboy/node-getobject/workflows/Tests/badge.svg)](https://github.com/cowboy/node-getobject/actions?workflow=Tests) 2 | 3 | get.and.set.deep.objects.easily = true; 4 | 5 | ## Getting Started 6 | Install the module with: `npm install getobject` 7 | 8 | ```javascript 9 | var getobject = require('getobject'); 10 | ``` 11 | 12 | ## Contributing 13 | In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/). 14 | 15 | ## Release History 16 | 17 | * 11/03/2021 - 1.0.0 Release. Fixes CVE-2020-28282. 18 | * 21/11/2013 - 0.1.0 Release. 19 | 20 | ## License 21 | Copyright (c) 2013 "Cowboy" Ben Alman 22 | Licensed under the MIT license. 23 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 "Cowboy" Ben Alman 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | FORCE_COLOR: 2 7 | 8 | jobs: 9 | run: 10 | name: Node ${{ matrix.node }} on ${{ matrix.os }} 11 | runs-on: ${{ matrix.os }} 12 | 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | node: [10, 12, 14] 17 | os: [ubuntu-latest, windows-latest] 18 | 19 | steps: 20 | - name: Clone repository 21 | uses: actions/checkout@v2 22 | 23 | - name: Set up Node.js 24 | uses: actions/setup-node@v2 25 | with: 26 | node-version: ${{ matrix.node }} 27 | 28 | - name: Install npm dependencies 29 | run: npm ci 30 | 31 | - name: Run tests 32 | run: npm test 33 | 34 | # We test multiple Windows shells because of prior stdout buffering issues 35 | # filed against Grunt. https://github.com/joyent/node/issues/3584 36 | - name: Run PowerShell tests 37 | run: "npm test # PowerShell" # Pass comment to PS for easier debugging 38 | shell: powershell 39 | if: startsWith(matrix.os, 'windows') 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getobject", 3 | "description": "get.and.set.deep.objects.easily = true", 4 | "version": "1.1.1", 5 | "homepage": "https://github.com/cowboy/node-getobject", 6 | "author": { 7 | "name": "\"Cowboy\" Ben Alman", 8 | "url": "http://benalman.com/" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/cowboy/node-getobject.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/cowboy/node-getobject/issues" 16 | }, 17 | "licenses": [ 18 | { 19 | "type": "MIT", 20 | "url": "https://github.com/cowboy/node-getobject/blob/master/LICENSE-MIT" 21 | } 22 | ], 23 | "main": "lib/getobject", 24 | "engines": { 25 | "node": ">=10" 26 | }, 27 | "scripts": { 28 | "test": "grunt nodeunit" 29 | }, 30 | "devDependencies": { 31 | "grunt-contrib-jshint": "~3.2.0", 32 | "grunt-contrib-nodeunit": "~4.0.0", 33 | "grunt-contrib-watch": "~1.1.0", 34 | "grunt": "~1.5.2" 35 | }, 36 | "keywords": [ 37 | "dot notation", 38 | "properties", 39 | "get", 40 | "set", 41 | "object", 42 | "dot" 43 | ], 44 | "files": [ 45 | "lib" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(grunt) { 4 | 5 | // Project configuration. 6 | grunt.initConfig({ 7 | nodeunit: { 8 | files: ['test/**/*_test.js'], 9 | }, 10 | jshint: { 11 | options: { 12 | jshintrc: '.jshintrc' 13 | }, 14 | gruntfile: { 15 | src: 'Gruntfile.js' 16 | }, 17 | lib: { 18 | src: ['lib/**/*.js'] 19 | }, 20 | test: { 21 | src: ['test/*.js'] 22 | }, 23 | }, 24 | watch: { 25 | gruntfile: { 26 | files: '<%= jshint.gruntfile.src %>', 27 | tasks: ['jshint:gruntfile'] 28 | }, 29 | lib: { 30 | files: '<%= jshint.lib.src %>', 31 | tasks: ['jshint:lib', 'nodeunit'] 32 | }, 33 | test: { 34 | files: '<%= jshint.test.src %>', 35 | tasks: ['jshint:test', 'nodeunit'] 36 | }, 37 | }, 38 | }); 39 | 40 | // These plugins provide necessary tasks. 41 | grunt.loadNpmTasks('grunt-contrib-nodeunit'); 42 | grunt.loadNpmTasks('grunt-contrib-jshint'); 43 | grunt.loadNpmTasks('grunt-contrib-watch'); 44 | 45 | // Default task. 46 | grunt.registerTask('default', ['jshint', 'nodeunit']); 47 | 48 | }; 49 | -------------------------------------------------------------------------------- /lib/getobject.js: -------------------------------------------------------------------------------- 1 | /* 2 | * getobject 3 | * https://github.com/cowboy/node-getobject 4 | * 5 | * Copyright (c) 2013 "Cowboy" Ben Alman 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | var getobject = module.exports = {}; 12 | 13 | // Split strings on dot, but only if dot isn't preceded by a backslash. Since 14 | // JavaScript doesn't support lookbehinds, use a placeholder for "\.", split 15 | // on dot, then replace the placeholder character with a dot. 16 | function getParts(str) { 17 | return str.replace(/\\\./g, '\uffff').split('.').map(function(s) { 18 | return s.replace(/\uffff/g, '.'); 19 | }); 20 | } 21 | 22 | // Get the value of a deeply-nested property exist in an object. 23 | getobject.get = function(obj, parts, create) { 24 | if (typeof parts === 'string') { 25 | parts = getParts(parts); 26 | } 27 | 28 | var part; 29 | while (typeof obj === 'object' && obj && parts.length) { 30 | part = parts.shift(); 31 | if (!(part in obj) && create) { 32 | obj[part] = {}; 33 | } 34 | obj = obj[part]; 35 | } 36 | 37 | return obj; 38 | }; 39 | 40 | // Set a deeply-nested property in an object, creating intermediary objects 41 | // as we go. 42 | getobject.set = function(obj, parts, value) { 43 | parts = getParts(parts); 44 | if (parts.includes('__proto__')) { 45 | // do not allow setting of __proto__. See CVE-2020-28282. 46 | return; 47 | } 48 | 49 | var prop = parts.pop(); 50 | obj = getobject.get(obj, parts, true); 51 | if (obj && typeof obj === 'object') { 52 | return (obj[prop] = value); 53 | } 54 | }; 55 | 56 | // Does a deeply-nested property exist in an object? 57 | getobject.exists = function(obj, parts) { 58 | parts = getParts(parts); 59 | 60 | var prop = parts.pop(); 61 | obj = getobject.get(obj, parts); 62 | 63 | return typeof obj === 'object' && obj && prop in obj; 64 | }; 65 | -------------------------------------------------------------------------------- /test/namespace_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var getobject = require('../lib/getobject'); 4 | 5 | exports.get = { 6 | 'no create': function(test) { 7 | var obj = {a: {b: {c: 1, d: '', e: null, f: undefined, 'g.h.i': 2}}}; 8 | test.strictEqual(getobject.get(obj, 'a'), obj.a, 'should get immediate properties.'); 9 | test.strictEqual(getobject.get(obj, 'a.b'), obj.a.b, 'should get nested properties.'); 10 | test.strictEqual(getobject.get(obj, 'a.x'), undefined, 'should return undefined for nonexistent properties.'); 11 | test.strictEqual(getobject.get(obj, 'a.b.c'), 1, 'should return values.'); 12 | test.strictEqual(getobject.get(obj, 'a.b.d'), '', 'should return values.'); 13 | test.strictEqual(getobject.get(obj, 'a.b.e'), null, 'should return values.'); 14 | test.strictEqual(getobject.get(obj, 'a.b.f'), undefined, 'should return values.'); 15 | test.strictEqual(getobject.get(obj, 'a.b.g\\.h\\.i'), 2, 'literal backslash should escape period in property name.'); 16 | test.done(); 17 | }, 18 | 'create': function(test) { 19 | var obj = {a: 1}; 20 | test.strictEqual(getobject.get(obj, 'a', true), obj.a, 'should just return existing properties.'); 21 | test.strictEqual(getobject.get(obj, 'b', true), obj.b, 'should create immediate properties.'); 22 | test.strictEqual(getobject.get(obj, 'c.d.e', true), obj.c.d.e, 'should create nested properties.'); 23 | test.done(); 24 | } 25 | }; 26 | 27 | exports.set = function(test) { 28 | var obj = {}; 29 | test.strictEqual(getobject.set(obj, 'a', 1), 1, 'should return immediate property value.'); 30 | test.strictEqual(obj.a, 1, 'should set property value.'); 31 | test.strictEqual(getobject.set(obj, 'b.c.d', 1), 1, 'should return nested property value.'); 32 | test.strictEqual(obj.b.c.d, 1, 'should set property value.'); 33 | test.strictEqual(getobject.set(obj, 'e\\.f\\.g', 1), 1, 'literal backslash should escape period in property name.'); 34 | test.strictEqual(obj['e.f.g'], 1, 'should set property value.'); 35 | test.done(); 36 | }; 37 | 38 | exports.exists = function(test) { 39 | var obj = {a: {b: {c: 1, d: '', e: null, f: undefined, 'g.h.i': 2}}}; 40 | test.ok(getobject.exists(obj, 'a'), 'immediate property should exist.'); 41 | test.ok(getobject.exists(obj, 'a.b'), 'nested property should exist.'); 42 | test.ok(getobject.exists(obj, 'a.b.c'), 'nested property should exist.'); 43 | test.ok(getobject.exists(obj, 'a.b.d'), 'nested property should exist.'); 44 | test.ok(getobject.exists(obj, 'a.b.e'), 'nested property should exist.'); 45 | test.ok(getobject.exists(obj, 'a.b.f'), 'nested property should exist.'); 46 | test.ok(getobject.exists(obj, 'a.b.g\\.h\\.i'), 'literal backslash should escape period in property name.'); 47 | test.equal(getobject.exists(obj, 'x'), false, 'nonexistent property should not exist.'); 48 | test.equal(getobject.exists(obj, 'a.x'), false, 'nonexistent property should not exist.'); 49 | test.equal(getobject.exists(obj, 'a.b.x'), false, 'nonexistent property should not exist.'); 50 | test.done(); 51 | }; 52 | 53 | exports.proto = function(test) { 54 | var obj = {}; 55 | test.equal(getobject.exists(obj, 'isAdmin'), false); 56 | getobject.set(obj, '__proto__.isAdmin', true); 57 | test.equal(getobject.exists(obj, 'isAdmin'), false); 58 | test.done(); 59 | }; 60 | --------------------------------------------------------------------------------