From 12a45939e9b047b6d3663fe55f2eb383ec63c4e1 Mon Sep 17 00:00:00 2001 From: Stuart Dotson Date: Fri, 24 Dec 2021 02:43:20 -0500 Subject: [PATCH] Fix 2377: Throw error when trying to stub non-configurable or non-writable properties (#2417) Fixes issue #2377 by throwing an error when trying to stub non-configurable or non-writable properties --- lib/sinon/stub.js | 22 +++++++++++ test/shared-spy-stub-everything-tests.js | 47 ++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/lib/sinon/stub.js b/lib/sinon/stub.js index 8ce86a0ea..8712f9d8b 100644 --- a/lib/sinon/stub.js +++ b/lib/sinon/stub.js @@ -81,6 +81,9 @@ function stub(object, property) { } var actualDescriptor = getPropertyDescriptor(object, property); + + assertValidPropertyDescriptor(actualDescriptor, property); + var isObjectOrFunction = typeof object === "object" || typeof object === "function"; var isStubbingEntireObject = @@ -147,6 +150,25 @@ stub.createStubInstance = function (constructor, overrides) { return stubbedObject; }; +function assertValidPropertyDescriptor(descriptor, property) { + if (!descriptor || !property) { + return; + } + if (!descriptor.configurable && !descriptor.writable) { + throw new TypeError(`Descriptor for property ${property} is non-configurable and non-writable`); + } + if ((descriptor.get || descriptor.set) && !descriptor.configurable) { + throw new TypeError(`Descriptor for accessor property ${property} is non-configurable`); + } + if (isDataDescriptor(descriptor) && !descriptor.writable) { + throw new TypeError(`Descriptor for data property ${property} is non-writable`); + } +} + +function isDataDescriptor(descriptor) { + return !descriptor.value && !descriptor.writable && !descriptor.set && !descriptor.get; +} + /*eslint-disable no-use-before-define*/ function getParentBehaviour(stubInstance) { return stubInstance.parent && getCurrentBehavior(stubInstance.parent); diff --git a/test/shared-spy-stub-everything-tests.js b/test/shared-spy-stub-everything-tests.js index 860ddf78d..1d560c802 100644 --- a/test/shared-spy-stub-everything-tests.js +++ b/test/shared-spy-stub-everything-tests.js @@ -151,4 +151,51 @@ module.exports = function shared(createSpyOrStub) { assert.isUndefined(myObj.ouch); }); + + it("throws on data property descriptors that are not writable or configurable", function () { + var myObj = {}; + Object.defineProperty(myObj, 'ignoreme', { + writable: false, + configurable: false + }); + + assert.exception(function () { + createSpyOrStub(myObj, "ignoreme"); + }, + new TypeError('Descriptor for property ignoreme is non-configurable and non-writable') + ); + }); + + it("throws on accessor property descriptors that are not configurable", function () { + var myObj = {}; + Object.defineProperty(myObj, 'ignoreme', { + get: function(key) { + return this[key]; + }, + set: function(key, val) { + this[key] = val; + }, + configurable: false + }); + + assert.exception(function () { + createSpyOrStub(myObj, "ignoreme"); + }, + new TypeError('Descriptor for accessor property ignoreme is non-configurable') + ); + }); + + it("throws on data descriptors that are not stubbable", function () { + var myObj = {}; + Object.defineProperty(myObj, 'ignoreme', { + writable: false, + configurable: false + }); + + assert.exception(function () { + createSpyOrStub(myObj, "ignoreme"); + }, + new TypeError('Descriptor for data property ignoreme is non-writable') + ); + }); };