From 86da421f253cbc369c08c36f3d3cf0aa12b61624 Mon Sep 17 00:00:00 2001 From: Franziska Hinkelmann Date: Wed, 1 Feb 2017 19:57:22 +0100 Subject: [PATCH] src: don't overwrite non-writable vm globals Check that the property doesn't have the read-only flag set before overwriting it. This is Ben Noordhuis previous commit, but keeping is_contextual_store. is_contextual_store describes whether this.foo = 42 or foo = 42 was called. The second is contextual and will fail in strict mode if foo is used without declaration. Therefore only do an early return if it is a contextual store. In particular, don't do an early return for Object.defineProperty(this, ...). Fixes: https://github.com/nodejs/node/issues/10223 Refs: https://github.com/nodejs/node/pull/10227 PR-URL: https://github.com/nodejs/node/pull/11109 Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell Reviewed-By: Sakthipriyan Vairamani --- src/node_contextify.cc | 27 ++++++++++++++++++--------- test/parallel/test-vm-context.js | 13 +++++++++++++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/node_contextify.cc b/src/node_contextify.cc index d74b01ea0da371..5ed7d5faf0932e 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -383,19 +383,28 @@ class ContextifyContext { if (ctx->context_.IsEmpty()) return; + auto attributes = PropertyAttribute::None; bool is_declared = - ctx->global_proxy()->HasRealNamedProperty(ctx->context(), - property).FromJust(); + ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(), + property) + .To(&attributes); + bool read_only = + static_cast(attributes) & + static_cast(PropertyAttribute::ReadOnly); + + if (is_declared && read_only) + return; + + // true for x = 5 + // false for this.x = 5 + // false for Object.defineProperty(this, 'foo', ...) + // false for vmResult.x = 5 where vmResult = vm.runInContext(); bool is_contextual_store = ctx->global_proxy() != args.This(); - bool set_property_will_throw = - args.ShouldThrowOnError() && - !is_declared && - is_contextual_store; + if (!is_declared && args.ShouldThrowOnError() && is_contextual_store) + return; - if (!set_property_will_throw) { - ctx->sandbox()->Set(property, value); - } + ctx->sandbox()->Set(property, value); } diff --git a/test/parallel/test-vm-context.js b/test/parallel/test-vm-context.js index 75d91ce8129dcb..c58bdc29daba66 100644 --- a/test/parallel/test-vm-context.js +++ b/test/parallel/test-vm-context.js @@ -75,3 +75,16 @@ assert.throws(function() { // https://github.com/nodejs/node/issues/6158 ctx = new Proxy({}, {}); assert.strictEqual(typeof vm.runInNewContext('String', ctx), 'function'); + +// https://github.com/nodejs/node/issues/10223 +ctx = vm.createContext(); +vm.runInContext('Object.defineProperty(this, "x", { value: 42 })', ctx); +assert.strictEqual(ctx.x, 42); +assert.strictEqual(vm.runInContext('x', ctx), 42); + +vm.runInContext('x = 0', ctx); // Does not throw but x... +assert.strictEqual(vm.runInContext('x', ctx), 42); // ...should be unaltered. + +assert.throws(() => vm.runInContext('"use strict"; x = 0', ctx), + /Cannot assign to read only property 'x'/); +assert.strictEqual(vm.runInContext('x', ctx), 42);