diff --git a/applicators.js b/applicators.js index 23a3755..877a71c 100644 --- a/applicators.js +++ b/applicators.js @@ -13,7 +13,7 @@ function _perform(select, fn, obj, options) { const mode = { 'strict': 1, 'lenient': 2 }[options?.mode?.toLowerCase()]; - let resolution = select(obj, options?.references, mode); + let resolution = select(obj, options?.references, mode, !options?.allowUnsafe); if (options?.unique) { console.warn('Using options.unique is deprecated. Use the :unique meta property instead.'); @@ -42,10 +42,9 @@ function _perform(select, fn, obj, options) { resolution[k].selection = resolution[k].selection.filter(sel => sel !== undefined); } } - for (let item of resolution) for (let property of item.selection) { - const value = fn(item.target[property], property, item.target); + const value = fn(item.target[property], property, item.target, resolution.root); // Only assign new value if it is different // This is important so that read operations will work even on read-only (e.g. frozen) objects // In read-only mode, NEVER attempt to set a new value. This is important so that dynamically generated properties @@ -137,7 +136,9 @@ export function compile(selector) { * mimics the ordinary rules of selecting object properties in Javascript (where `{}['a'] === undefined`). * In `strict` mode, any attempt to select a non-existent property immediately results in an error. * In `lenient` mode, non-existent properties are silently dropped. - * The default mode is `normal`. + * @param {boolean} [options.allowUnsafe=false] Set this to `true` to allow accessing unsafe properties on the target. Unsafe properties are + * `constructor, prototype, __proto__, __defineGetter__, __lookupGetter__, __defineSetter__, __lookupSetter__`. Otherwise, trying to access these will result in an + * error being thrown. See the section on [security considerations](#security-considerations) for why these are considered unsafe. * @param {Object} [options.references] The values for any references used in the selector. * @return {*} The results of applying `fn` to all selected properties. */ diff --git a/applicators.spec.js b/applicators.spec.js index 0e23a6f..7fff4d8 100644 --- a/applicators.spec.js +++ b/applicators.spec.js @@ -23,13 +23,13 @@ describe('perform', function() { expect(fn).to.have.been.calledWith(obj.a.b2.c); }); - it('should pass value, property, context to the function', function() { + it('should pass value, property, context and root to the function', function() { const fn = sinon.spy(); const c = obj.a.b1.c; perform('a.b1.c', fn, obj); - expect(fn).to.have.been.calledWith(c, 'c', sinon.match.same(obj.a.b1)); + expect(fn).to.have.been.calledWith(c, 'c', sinon.match.same(obj.a.b1), sinon.match.same(obj)); }); it('should set matching properties to the result value', function() { @@ -146,6 +146,25 @@ describe('perform', function() { expect(spy).to.have.been.calledOnce.and.calledWith(1); }); }); + + describe('Selection safety', function() { + const fn = x => x; + const UNSAFE_KEYS = [ 'constructor', 'prototype', '__proto__', '__defineGetter__', '__lookupGetter__', '__defineSetter__', '__lookupSetter__' ]; + + it('should throw when trying to access a forbidden key', function() { + UNSAFE_KEYS.forEach(evil => expect(perform.bind(null, evil, fn, obj), evil).to.throw(/unsafe/i)); + }); + + it('should allow to access a forbidden key when explicitly opting into unsafe selection', function() { + UNSAFE_KEYS.forEach(evil => { + const target = function() {}; // prototype property exists only on functions, and for the other ones it makes no difference + const op = perform.bind(null, evil, fn, target, { allowUnsafe: true }); + + expect(op, evil).to.not.throw(); + expect(op(), evil).to.equal(target[evil]); + }); + }); + }); }); describe('get', function() { diff --git a/selector.js b/selector.js index 512015d..8ba1d87 100644 --- a/selector.js +++ b/selector.js @@ -261,11 +261,12 @@ function peg$parse(input, options) { var peg$e41 = peg$classExpectation([" ", "\t", "\n"], false, false); var peg$f0 = function(selectors) { - return Object.assign(function union(obj, references, mode) { + return Object.assign(function union(obj, references, mode, safe) { + // For some reason, this is WAY faster than doing selectors.flatMap(): - let result = selectors[0](obj, references, mode); // This is safe, because the grammar guarantees there is at least one selector - for (let i = 1; i < selectors.length; i++) - result = result.concat(selectors[i](obj, references, mode)); + let result = selectors[0](obj, references, mode, safe); // This is safe, because the grammar guarantees there is at least one selector + for (let i = 1; i < selectors.length; i++) + result = result.concat(selectors[i](obj, references, mode, safe)); return result; }, { // The selector is ambiguous if it is a union of more than one selectors, @@ -283,12 +284,12 @@ function peg$parse(input, options) { // Return value is the function select, but we will set some meta-properties of the selector on it: // - Whether or not the selector is ambiguous // - The raw source text of the selector - return Object.assign(function select(obj, references, mode) { + return Object.assign(function select(obj, references, mode, safe) { const resolution = [ { target: obj, selection: [] } ] resolution.root = obj; - selector?.forEach(element => element(resolution, references, mode)); + selector?.forEach(element => element(resolution, references, mode, safe)); return resolution; }, { // The selector as a whole is ambiguous if at least one of its constituents is ambiguous (multi-valued) @@ -371,7 +372,7 @@ function peg$parse(input, options) { }; var peg$f8 = function(regex) { // Wildcard selectors are always ambiguous - return Object.assign(function selectByRegex(resolution, references, mode) { + return Object.assign(function selectByRegex(resolution, references, mode, safe) { for (let item of resolution) { if (mode === MODE_LENIENT && item.target == null) item.selection = []; @@ -386,7 +387,9 @@ function peg$parse(input, options) { }, { ambiguous: true }); }; var peg$f9 = function(name) { - return function selectByName(resolution) { + return function selectByName(resolution, references, mode, safe) { + if (safe && PROHIBITED_KEYS.includes(name)) + throw new Error(`Unsafe access on ${resolution.root}: tried to access prohibited key ${name}`); resolution.forEach(item => item.selection = [ name ]); } }; @@ -1744,6 +1747,8 @@ function peg$parse(input, options) { const MODE_STRICT = 1; const MODE_LENIENT = 2; + const PROHIBITED_KEYS = [ 'constructor', 'prototype', '__proto__', '__defineGetter__', '__lookupGetter__', '__defineSetter__', '__lookupSetter__' ] + peg$result = peg$startRuleFunction(); if (peg$result !== peg$FAILED && peg$currPos === input.length) { @@ -1768,4 +1773,4 @@ export { peg$parse as parse }; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNlbGVjdG9yLnBlZyJdLCJuYW1lcyI6WyJVbmlvbl9TZWxlY3RvciIsIlVuaW9uX09wZXJhdG9yIiwiU2VsZWN0b3IiLCJFbXB0eV9zZWxlY3RvciIsIkFjY2Vzc29yX3NlbGVjdG9yIiwiQWNjZXNzb3IiLCJQcm9wZXJ0eV9kZXNjcmlwdG9yIiwiUHJvcGVydHlfbmFtZSIsIk1ldGFfcHJvcGVydHkiLCJDb25kaXRpb24iLCJVbmFyeUNvbmRpdGlvbiIsIkJpbmFyeUNvbmRpdGlvbiIsIk9wZXJhdG9yIiwiVmFsdWUiLCJSZWZlcmVuY2VWYWx1ZSIsIlJlZmVyZW5jZV9jaGFyYWN0ZXIiLCJMaXRlcmFsVmFsdWUiLCJJZGVudGlmaWVyIiwiUHJvcGVydHlfbmFtZV93aXRoX3dpbGRjYXJkIiwiV2lsZGNhcmQiLCJQc2V1ZG9fUHJvcGVydHkiLCJQZXJtaXR0ZWRfY2hhcmFjdGVyIiwiUmVzZXJ2ZWRfY2hhcmFjdGVyIiwiRXNjYXBlX2NoYXJhY3RlciIsIlN5bWJvbCIsIl8iLCIkaW5pdGlhbGl6ZXIiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7b0NBTWdFO0FBQ2hFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEU7cUNBUTZEO0FBQzdEO0FBQ0E7QUFDQTtBQUNBLENBQUMsRTttQ0FBSTtBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB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Ozs7Ozs7Ozs7OztXQXdCUkMsdUJBQWM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O1dBV2RDLDBCQUFpQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztXQU1qQkMsaUJBQVE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7V0FxQlJDLDRCQUFtQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztXQWlEbkJDLHNCQUFhOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztXQXdFYkMsc0JBQWE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7V0FnRWJDLGtCQUFTOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O1dBb0JUQyx1QkFBYzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztXQVNkQyx3QkFBZTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztXQVFmQyxpQkFBUTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7V0FnQlJDLGNBQUs7Ozs7Ozs7Ozs7O1dBRUxDLHVCQUFjOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O1dBS2RDLDRCQUFtQjs7Ozs7Ozs7Ozs7Ozs7V0FDbkJDLHFCQUFZOzs7Ozs7Ozs7Ozs7OztXQUtaQyxtQkFBVTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7V0FRVkMsb0NBQTJCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7V0FJM0JDLGlCQUFROzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O1dBU1JDLHdCQUFlOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztXQUVmQyw0QkFBbUI7Ozs7Ozs7Ozs7Ozs7O1dBRW5CQywyQkFBa0I7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O1dBRWxCQyx5QkFBZ0I7Ozs7Ozs7Ozs7Ozs7O1dBR2hCQyxlQUFNOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O1dBR05DLFVBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBblhBQztBQUNEQTtBQUNBQTtBQUNBQSJ9 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNlbGVjdG9yLnBlZyJdLCJuYW1lcyI6WyJVbmlvbl9TZWxlY3RvciIsIlVuaW9uX09wZXJhdG9yIiwiU2VsZWN0b3IiLCJFbXB0eV9zZWxlY3RvciIsIkFjY2Vzc29yX3NlbGVjdG9yIiwiQWNjZXNzb3IiLCJQcm9wZXJ0eV9kZXNjcmlwdG9yIiwiUHJvcGVydHlfbmFtZSIsIk1ldGFfcHJvcGVydHkiLCJDb25kaXRpb24iLCJVbmFyeUNvbmRpdGlvbiIsIkJpbmFyeUNvbmRpdGlvbiIsIk9wZXJhdG9yIiwiVmFsdWUiLCJSZWZlcmVuY2VWYWx1ZSIsIlJlZmVyZW5jZV9jaGFyYWN0ZXIiLCJMaXRlcmFsVmFsdWUiLCJJZGVudGlmaWVyIiwiUHJvcGVydHlfbmFtZV93aXRoX3dpbGRjYXJkIiwiV2lsZGNhcmQiLCJQc2V1ZG9fUHJvcGVydHkiLCJQZXJtaXR0ZWRfY2hhcmFjdGVyIiwiUmVzZXJ2ZWRfY2hhcmFjdGVyIiwiRXNjYXBlX2NoYXJhY3RlciIsIlN5bWJvbCIsIl8iLCIkaW5pdGlhbGl6ZXIiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7b0NBUWdFO0FBQ2hFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRTtxQ0FRNkQ7QUFDN0Q7QUFDQTtBQUNBO0FBQ0EsQ0FBQyxFO21DQUFJO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQyxFOzJCQUdJLHFCQUFxQixFOzJCQUFHO0FBQzdCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQyxFOzZDQUdrRDtBQUNuRDtBQUNBO0FBQ0EsQ0FBQyxFOzJCQUdhO0FBQ2Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDLEU7b0RBT3lFO0FBQzFFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUMsRTtnQ0FNcUM7QUFDdEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUMsRTsrQkFDbUI7QUFDcEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUMsRTtrQ0FDMEI7QUFDM0I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUMsRTtnQ0FTa0w7QUFDbkw7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB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 diff --git a/selector.peg b/selector.peg index fe27bb9..7214a2c 100644 --- a/selector.peg +++ b/selector.peg @@ -2,14 +2,17 @@ const MODE_NORMAL = 0; const MODE_STRICT = 1; const MODE_LENIENT = 2; + + const PROHIBITED_KEYS = [ 'constructor', 'prototype', '__proto__', '__defineGetter__', '__lookupGetter__', '__defineSetter__', '__lookupSetter__' ] } Union_Selector = selectors:Selector|1.., _* Union_Operator _*| { - return Object.assign(function union(obj, references, mode) { + return Object.assign(function union(obj, references, mode, safe) { + // For some reason, this is WAY faster than doing selectors.flatMap(): - let result = selectors[0](obj, references, mode); // This is safe, because the grammar guarantees there is at least one selector - for (let i = 1; i < selectors.length; i++) - result = result.concat(selectors[i](obj, references, mode)); + let result = selectors[0](obj, references, mode, safe); // This is safe, because the grammar guarantees there is at least one selector + for (let i = 1; i < selectors.length; i++) + result = result.concat(selectors[i](obj, references, mode, safe)); return result; }, { // The selector is ambiguous if it is a union of more than one selectors, @@ -33,12 +36,12 @@ Selector // Return value is the function select, but we will set some meta-properties of the selector on it: // - Whether or not the selector is ambiguous // - The raw source text of the selector - return Object.assign(function select(obj, references, mode) { + return Object.assign(function select(obj, references, mode, safe) { const resolution = [ { target: obj, selection: [] } ] resolution.root = obj; - selector?.forEach(element => element(resolution, references, mode)); + selector?.forEach(element => element(resolution, references, mode, safe)); return resolution; }, { // The selector as a whole is ambiguous if at least one of its constituents is ambiguous (multi-valued) @@ -137,7 +140,7 @@ Property_descriptor Property_name = regex:Property_name_with_wildcard { // Wildcard selectors are always ambiguous - return Object.assign(function selectByRegex(resolution, references, mode) { + return Object.assign(function selectByRegex(resolution, references, mode, safe) { for (let item of resolution) { if (mode === MODE_LENIENT && item.target == null) item.selection = []; @@ -152,7 +155,9 @@ Property_name }, { ambiguous: true }); } / name:Identifier { - return function selectByName(resolution) { + return function selectByName(resolution, references, mode, safe) { + if (safe && PROHIBITED_KEYS.includes(name)) + throw new Error(`Unsafe access on ${resolution.root}: tried to access prohibited key ${name}`); resolution.forEach(item => item.selection = [ name ]); } }