From f529f73bd7a0c9046dcc2ea67760ac9b03e04b52 Mon Sep 17 00:00:00 2001 From: legendecas Date: Fri, 16 Sep 2022 00:51:58 +0800 Subject: [PATCH] lib: brand check event handler property receivers Event handler properties defined by `defineEventHandler` should check if the receiver is a valid `EventTarget`. PR-URL: https://github.com/nodejs/node/pull/44483 Reviewed-By: Antoine du Hamel Reviewed-By: Minwoo Jung Reviewed-By: James M Snell --- lib/internal/event_target.js | 65 +++++++++++++++++++++--------------- lib/internal/validators.js | 9 ++++- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index 2be1b9cf0b9f39..c60e80d5c5d4c7 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -34,7 +34,7 @@ const { ERR_INVALID_THIS, } } = require('internal/errors'); -const { validateObject, validateString } = require('internal/validators'); +const { validateObject, validateString, validateInternalField } = require('internal/validators'); const { customInspectSymbol, @@ -492,6 +492,7 @@ function initEventTarget(self) { self[kEvents] = new SafeMap(); self[kMaxEventTargetListeners] = EventEmitter.defaultMaxListeners; self[kMaxEventTargetListenersWarned] = false; + self[kHandlers] = new SafeMap(); } class EventTarget { @@ -1021,34 +1022,46 @@ function makeEventHandler(handler) { function defineEventHandler(emitter, name) { // 8.1.5.1 Event handlers - basically `on[eventName]` attributes - ObjectDefineProperty(emitter, `on${name}`, { + const propName = `on${name}`; + function get() { + validateInternalField(this, kHandlers, 'EventTarget'); + return this[kHandlers]?.get(name)?.handler ?? null; + } + ObjectDefineProperty(get, 'name', { __proto__: null, - get() { - return this[kHandlers]?.get(name)?.handler ?? null; - }, - set(value) { - if (!this[kHandlers]) { - this[kHandlers] = new SafeMap(); + value: `get ${propName}`, + }); + + function set(value) { + validateInternalField(this, kHandlers, 'EventTarget'); + let wrappedHandler = this[kHandlers]?.get(name); + if (wrappedHandler) { + if (typeof wrappedHandler.handler === 'function') { + this[kEvents].get(name).size--; + const size = this[kEvents].get(name).size; + this[kRemoveListener](size, name, wrappedHandler.handler, false); } - let wrappedHandler = this[kHandlers]?.get(name); - if (wrappedHandler) { - if (typeof wrappedHandler.handler === 'function') { - this[kEvents].get(name).size--; - const size = this[kEvents].get(name).size; - this[kRemoveListener](size, name, wrappedHandler.handler, false); - } - wrappedHandler.handler = value; - if (typeof wrappedHandler.handler === 'function') { - this[kEvents].get(name).size++; - const size = this[kEvents].get(name).size; - this[kNewListener](size, name, value, false, false, false, false); - } - } else { - wrappedHandler = makeEventHandler(value); - this.addEventListener(name, wrappedHandler); + wrappedHandler.handler = value; + if (typeof wrappedHandler.handler === 'function') { + this[kEvents].get(name).size++; + const size = this[kEvents].get(name).size; + this[kNewListener](size, name, value, false, false, false, false); } - this[kHandlers].set(name, wrappedHandler); - }, + } else { + wrappedHandler = makeEventHandler(value); + this.addEventListener(name, wrappedHandler); + } + this[kHandlers].set(name, wrappedHandler); + } + ObjectDefineProperty(set, 'name', { + __proto__: null, + value: `set ${propName}`, + }); + + ObjectDefineProperty(emitter, propName, { + __proto__: null, + get, + set, configurable: true, enumerable: true }); diff --git a/lib/internal/validators.js b/lib/internal/validators.js index de8a8bb9b83b34..a192f9e6302ddb 100644 --- a/lib/internal/validators.js +++ b/lib/internal/validators.js @@ -418,6 +418,12 @@ function validateLinkHeaderValue(value, name) { } } +const validateInternalField = hideStackFrames((object, fieldKey, className) => { + if (typeof object !== 'object' || object === null || !ObjectPrototypeHasOwnProperty(object, fieldKey)) { + throw new ERR_INVALID_ARG_TYPE('this', className, object); + } +}); + module.exports = { isInt32, isUint32, @@ -440,5 +446,6 @@ module.exports = { validateUndefined, validateUnion, validateAbortSignal, - validateLinkHeaderValue + validateLinkHeaderValue, + validateInternalField, };