From dcab88ad38500283c6494b6cdec5cdd38a060c7c Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 13 Aug 2021 15:12:07 -0700 Subject: [PATCH] worker: add brand checks for detached properties/methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add proper brand-checking for detached property and method accesses. Also adds a note about non-standard APIs and makes the standard accessors enumerable. Signed-off-by: James M Snell PR-URL: https://github.com/nodejs/node/pull/39763 Reviewed-By: Antoine du Hamel Reviewed-By: Anna Henningsen Reviewed-By: Tobias Nießen --- lib/internal/worker/io.js | 40 ++++++++++++++++++- test/parallel/test-worker-broadcastchannel.js | 17 ++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/lib/internal/worker/io.js b/lib/internal/worker/io.js index adacdd913c6693..95c5515d1414eb 100644 --- a/lib/internal/worker/io.js +++ b/lib/internal/worker/io.js @@ -53,6 +53,7 @@ const { inspect } = require('internal/util/inspect'); const { codes: { ERR_INVALID_ARG_TYPE, + ERR_INVALID_THIS, ERR_MISSING_ARGS, } } = require('internal/errors'); @@ -74,6 +75,7 @@ const kStartedReading = Symbol('kStartedReading'); const kStdioWantsMoreDataCallback = Symbol('kStdioWantsMoreDataCallback'); const kCurrentlyReceivingPorts = SymbolFor('nodejs.internal.kCurrentlyReceivingPorts'); +const kType = Symbol('kType'); const messageTypes = { UP_AND_RUNNING: 'upAndRunning', @@ -349,11 +351,16 @@ function onMessageEvent(type, data) { this.dispatchEvent(new MessageEvent(type, { data })); } +function isBroadcastChannel(value) { + return value?.[kType] === 'BroadcastChannel'; +} + class BroadcastChannel extends EventTarget { constructor(name) { if (arguments.length === 0) throw new ERR_MISSING_ARGS('name'); super(); + this[kType] = 'BroadcastChannel'; this[kName] = `${name}`; this[kHandle] = broadcastChannel(this[kName]); this[kOnMessage] = FunctionPrototypeBind(onMessageEvent, this, 'message'); @@ -364,6 +371,8 @@ class BroadcastChannel extends EventTarget { } [inspect.custom](depth, options) { + if (!isBroadcastChannel(this)) + throw new ERR_INVALID_THIS('BroadcastChannel'); if (depth < 0) return 'BroadcastChannel'; @@ -378,9 +387,15 @@ class BroadcastChannel extends EventTarget { }, opts)}`; } - get name() { return this[kName]; } + get name() { + if (!isBroadcastChannel(this)) + throw new ERR_INVALID_THIS('BroadcastChannel'); + return this[kName]; + } close() { + if (!isBroadcastChannel(this)) + throw new ERR_INVALID_THIS('BroadcastChannel'); if (this[kHandle] === undefined) return; this[kHandle].off('message', this[kOnMessage]); @@ -392,6 +407,8 @@ class BroadcastChannel extends EventTarget { } postMessage(message) { + if (!isBroadcastChannel(this)) + throw new ERR_INVALID_THIS('BroadcastChannel'); if (arguments.length === 0) throw new ERR_MISSING_ARGS('message'); if (this[kHandle] === undefined) @@ -400,19 +417,40 @@ class BroadcastChannel extends EventTarget { throw new DOMException('Message could not be posted.'); } + // The ref() method is Node.js specific and not part of the standard + // BroadcastChannel API definition. Typically we shouldn't extend Web + // Platform APIs with Node.js specific methods but ref and unref + // are a bit special. ref() { + if (!isBroadcastChannel(this)) + throw new ERR_INVALID_THIS('BroadcastChannel'); if (this[kHandle]) this[kHandle].ref(); return this; } + // The unref() method is Node.js specific and not part of the standard + // BroadcastChannel API definition. Typically we shouldn't extend Web + // Platform APIs with Node.js specific methods but ref and unref + // are a bit special. unref() { + if (!isBroadcastChannel(this)) + throw new ERR_INVALID_THIS('BroadcastChannel'); if (this[kHandle]) this[kHandle].unref(); return this; } } +const kEnumerableProperty = ObjectCreate(null); +kEnumerableProperty.enumerable = true; + +ObjectDefineProperties(BroadcastChannel.prototype, { + name: kEnumerableProperty, + close: kEnumerableProperty, + postMessage: kEnumerableProperty, +}); + defineEventHandler(BroadcastChannel.prototype, 'message'); defineEventHandler(BroadcastChannel.prototype, 'messageerror'); diff --git a/test/parallel/test-worker-broadcastchannel.js b/test/parallel/test-worker-broadcastchannel.js index 4212e5bf2f7afb..8e588994aab666 100644 --- a/test/parallel/test-worker-broadcastchannel.js +++ b/test/parallel/test-worker-broadcastchannel.js @@ -151,3 +151,20 @@ assert.throws(() => new BroadcastChannel(), { bc1.close(); bc2.close(); } + +{ + assert.throws(() => Reflect.get(BroadcastChannel.prototype, 'name', {}), { + code: 'ERR_INVALID_THIS', + }); + + [ + 'close', + 'postMessage', + 'ref', + 'unref', + ].forEach((i) => { + assert.throws(() => Reflect.apply(BroadcastChannel.prototype[i], [], {}), { + code: 'ERR_INVALID_THIS', + }); + }); +}