From a00312147f287a1703bf6b7e104d27c8a0caacee Mon Sep 17 00:00:00 2001 From: Alex Kocharin Date: Fri, 11 Dec 2020 20:59:11 +0300 Subject: [PATCH] Use defineProperty if user data happens to have `__proto__` fix https://github.com/nodeca/js-yaml/issues/164 --- CHANGELOG.md | 1 + lib/loader.js | 21 ++++++++++++++++----- test/issues/0164.js | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 test/issues/0164.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 8041ff8f..83d6be5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - "duplicate mapping key" exception now points at the correct column, #452. - Extra commas in flow collections (e.g. `[foo,,bar]`) now throw an exception instead of producing null, #321. +- `__proto__` key no longer overrides object prototype, #164. - Removed `bower.json`. diff --git a/lib/loader.js b/lib/loader.js index 2717ee71..f99e56b9 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -339,7 +339,18 @@ function storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valu state.position = startPos || state.position; throwError(state, 'duplicated mapping key'); } - _result[keyNode] = valueNode; + + // used for this specific key only because Object.defineProperty is slow + if (keyNode === '__proto__') { + Object.defineProperty(_result, keyNode, { + configurable: true, + enumerable: true, + writable: true, + value: valueNode + }); + } else { + _result[keyNode] = valueNode; + } delete overridableKeys[keyNode]; } @@ -683,7 +694,7 @@ function readFlowCollection(state, nodeIndent) { isPair, isExplicitPair, isMapping, - overridableKeys = {}, + overridableKeys = Object.create(null), keyNode, keyTag, valueNode, @@ -997,7 +1008,7 @@ function readBlockMapping(state, nodeIndent, flowIndent) { _tag = state.tag, _anchor = state.anchor, _result = {}, - overridableKeys = {}, + overridableKeys = Object.create(null), keyTag = null, keyNode = null, valueNode = null, @@ -1476,8 +1487,8 @@ function readDocument(state) { state.version = null; state.checkLineBreaks = state.legacy; - state.tagMap = {}; - state.anchorMap = {}; + state.tagMap = Object.create(null); + state.anchorMap = Object.create(null); while ((ch = state.input.charCodeAt(state.position)) !== 0) { skipSeparationSpace(state, true, -1); diff --git a/test/issues/0164.js b/test/issues/0164.js new file mode 100644 index 00000000..963087f1 --- /dev/null +++ b/test/issues/0164.js @@ -0,0 +1,14 @@ +'use strict'; + + +const assert = require('assert'); +const yaml = require('../../'); + + +it('should define __proto__ as a value (not invoke setter)', function () { + let object = yaml.load('{ __proto__: {foo: bar} }'); + + assert.strictEqual(({}).hasOwnProperty.call(yaml.load('{}'), '__proto__'), false); + assert.strictEqual(({}).hasOwnProperty.call(object, '__proto__'), true); + assert(!object.foo); +});