From 971f09abe486185247e1faf4e8d1419ba2506b8d Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 23 May 2024 00:03:35 +0200 Subject: [PATCH] fix(runtime): use more null proto objects (#23921) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a primordialization effort to improve resistance against users tampering with the global `Object` prototype. --------- Co-authored-by: Bartek IwaƄczuk --- cli/js/40_jupyter.js | 4 +- cli/js/40_test.js | 2 +- ext/console/01_console.js | 8 ++-- ext/fetch/20_headers.js | 6 +-- ext/fetch/23_request.js | 2 +- ext/fetch/23_response.js | 2 +- ext/fetch/26_fetch.js | 2 +- ext/fetch/27_eventsource.js | 2 +- ext/ffi/00_ffi.js | 2 +- ext/fs/30_fs.js | 22 +++++----- ext/http/00_serve.ts | 2 +- ext/http/02_websocket.ts | 2 +- ext/io/12_io.js | 2 +- ext/kv/01_db.ts | 4 +- ext/net/02_tls.js | 2 +- ext/web/02_event.js | 12 +++--- ext/web/06_streams.js | 12 +++--- ext/web/08_text_encoding.js | 4 +- ext/web/09_file.js | 4 +- ext/web/13_message_port.js | 2 +- ext/web/15_performance.js | 6 +-- ext/webgpu/01_webgpu.js | 18 ++++---- ext/webidl/00_webidl.js | 63 +++++++++++++++++++++------- ext/websocket/02_websocketstream.js | 2 +- runtime/js/10_permissions.js | 2 +- runtime/js/11_workers.js | 4 +- runtime/js/40_process.js | 12 +++--- runtime/js/40_signals.js | 2 +- runtime/js/90_deno_ns.js | 8 ++-- runtime/js/98_global_scope_shared.js | 2 +- runtime/js/99_main.js | 2 +- tests/unit/event_test.ts | 14 +++++++ 32 files changed, 141 insertions(+), 92 deletions(-) diff --git a/cli/js/40_jupyter.js b/cli/js/40_jupyter.js index 9fab1c4147..0e0a4d7ac5 100644 --- a/cli/js/40_jupyter.js +++ b/cli/js/40_jupyter.js @@ -342,7 +342,7 @@ function enableJupyter() { async function broadcast( msgType, content, - { metadata = {}, buffers = [] } = {}, + { metadata = { __proto__: null }, buffers = [] } = { __proto__: null }, ) { await op_jupyter_broadcast(msgType, content, metadata, buffers); } @@ -400,7 +400,7 @@ function enableJupyter() { if (options.update) { messageType = "update_display_data"; } - let transient = {}; + let transient = { __proto__: null }; if (options.display_id) { transient = { display_id: options.display_id }; } diff --git a/cli/js/40_test.js b/cli/js/40_test.js index d93228940e..2877bfa9b5 100644 --- a/cli/js/40_test.js +++ b/cli/js/40_test.js @@ -196,7 +196,7 @@ function testInner( nameOrFnOrOptions, optionsOrFn, maybeFn, - overrides = {}, + overrides = { __proto__: null }, ) { // No-op if we're not running in `deno test` subcommand. if (typeof op_register_test !== "function") { diff --git a/ext/console/01_console.js b/ext/console/01_console.js index 2b80756688..32d8d653c7 100644 --- a/ext/console/01_console.js +++ b/ext/console/01_console.js @@ -2930,7 +2930,7 @@ function cssToAnsi(css, prevCss = null) { return ansi; } -function inspectArgs(args, inspectOptions = {}) { +function inspectArgs(args, inspectOptions = { __proto__: null }) { const ctx = { ...getDefaultInspectOptions(), colors: inspectOptions.colors ?? !noColorStdout(), @@ -3124,7 +3124,7 @@ class Console { ); }; - dir = (obj = undefined, options = {}) => { + dir = (obj = undefined, options = { __proto__: null }) => { this.#printFunc( inspectArgs([obj], { ...getConsoleInspectOptions(noColorStdout()), @@ -3232,7 +3232,7 @@ class Console { resultData = [...new SafeSetIterator(data)]; } else if (isMapObject) { let idx = 0; - resultData = {}; + resultData = { __proto__: null }; MapPrototypeForEach(data, (v, k) => { resultData[idx] = { Key: k, Values: v }; @@ -3390,7 +3390,7 @@ const customInspect = SymbolFor("Deno.customInspect"); function inspect( value, - inspectOptions = {}, + inspectOptions = { __proto__: null }, ) { // Default options const ctx = { diff --git a/ext/fetch/20_headers.js b/ext/fetch/20_headers.js index 3ee9d91848..1690d9f7d6 100644 --- a/ext/fetch/20_headers.js +++ b/ext/fetch/20_headers.js @@ -100,7 +100,7 @@ function checkForInvalidValueChars(value) { return true; } -let HEADER_NAME_CACHE = {}; +let HEADER_NAME_CACHE = { __proto__: null }; let HEADER_CACHE_SIZE = 0; const HEADER_NAME_CACHE_SIZE_BOUNDARY = 4096; function checkHeaderNameForHttpTokenCodePoint(name) { @@ -112,7 +112,7 @@ function checkHeaderNameForHttpTokenCodePoint(name) { const valid = RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name); if (HEADER_CACHE_SIZE > HEADER_NAME_CACHE_SIZE_BOUNDARY) { - HEADER_NAME_CACHE = {}; + HEADER_NAME_CACHE = { __proto__: null }; HEADER_CACHE_SIZE = 0; } HEADER_CACHE_SIZE++; @@ -241,7 +241,7 @@ class Headers { // The order of steps are not similar to the ones suggested by the // spec but produce the same result. - const seenHeaders = {}; + const seenHeaders = { __proto__: null }; const entries = []; for (let i = 0; i < list.length; ++i) { const entry = list[i]; diff --git a/ext/fetch/23_request.js b/ext/fetch/23_request.js index 873d05a2b5..adebe13b30 100644 --- a/ext/fetch/23_request.js +++ b/ext/fetch/23_request.js @@ -300,7 +300,7 @@ class Request { * @param {RequestInfo} input * @param {RequestInit} init */ - constructor(input, init = {}) { + constructor(input, init = { __proto__: null }) { if (input === _brand) { this[_brand] = _brand; return; diff --git a/ext/fetch/23_response.js b/ext/fetch/23_response.js index a3805a97db..94fc69a986 100644 --- a/ext/fetch/23_response.js +++ b/ext/fetch/23_response.js @@ -282,7 +282,7 @@ class Response { * @param {ResponseInit} init * @returns {Response} */ - static json(data = undefined, init = {}) { + static json(data = undefined, init = { __proto__: null }) { const prefix = "Failed to execute 'Response.json'"; data = webidl.converters.any(data); init = webidl.converters["ResponseInit_fast"](init, prefix, "Argument 2"); diff --git a/ext/fetch/26_fetch.js b/ext/fetch/26_fetch.js index 458155a284..674d997090 100644 --- a/ext/fetch/26_fetch.js +++ b/ext/fetch/26_fetch.js @@ -305,7 +305,7 @@ function httpRedirectFetch(request, response, terminator) { * @param {RequestInfo} input * @param {RequestInit} init */ -function fetch(input, init = {}) { +function fetch(input, init = { __proto__: null }) { // There is an async dispatch later that causes a stack trace disconnect. // We reconnect it by assigning the result of that dispatch to `opPromise`, // awaiting `opPromise` in an inner function also named `fetch()` and diff --git a/ext/fetch/27_eventsource.js b/ext/fetch/27_eventsource.js index 1ab9d80095..685eb47c2d 100644 --- a/ext/fetch/27_eventsource.js +++ b/ext/fetch/27_eventsource.js @@ -144,7 +144,7 @@ class EventSource extends EventTarget { return this.#withCredentials; } - constructor(url, eventSourceInitDict = {}) { + constructor(url, eventSourceInitDict = { __proto__: null }) { super(); this[webidl.brand] = webidl.brand; const prefix = "Failed to construct 'EventSource'"; diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index a572ed61c1..19d62dfdd8 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -475,7 +475,7 @@ const UnsafeCallbackPrototype = UnsafeCallback.prototype; class DynamicLibrary { #rid; - symbols = {}; + symbols = { __proto__: null }; constructor(path, symbols) { ({ 0: this.#rid, 1: this.symbols } = op_ffi_load({ path, symbols })); diff --git a/ext/fs/30_fs.js b/ext/fs/30_fs.js index f0078392a5..d65f8560d7 100644 --- a/ext/fs/30_fs.js +++ b/ext/fs/30_fs.js @@ -155,7 +155,7 @@ function chdir(directory) { op_fs_chdir(pathFromURL(directory)); } -function makeTempDirSync(options = {}) { +function makeTempDirSync(options = { __proto__: null }) { return op_fs_make_temp_dir_sync( options.dir, options.prefix, @@ -163,7 +163,7 @@ function makeTempDirSync(options = {}) { ); } -function makeTempDir(options = {}) { +function makeTempDir(options = { __proto__: null }) { return op_fs_make_temp_dir_async( options.dir, options.prefix, @@ -171,7 +171,7 @@ function makeTempDir(options = {}) { ); } -function makeTempFileSync(options = {}) { +function makeTempFileSync(options = { __proto__: null }) { return op_fs_make_temp_file_sync( options.dir, options.prefix, @@ -179,7 +179,7 @@ function makeTempFileSync(options = {}) { ); } -function makeTempFile(options = {}) { +function makeTempFile(options = { __proto__: null }) { return op_fs_make_temp_file_async( options.dir, options.prefix, @@ -241,7 +241,7 @@ function realPath(path) { function removeSync( path, - options = {}, + options = { __proto__: null }, ) { op_fs_remove_sync( pathFromURL(path), @@ -251,7 +251,7 @@ function removeSync( async function remove( path, - options = {}, + options = { __proto__: null }, ) { await op_fs_remove_async( pathFromURL(path), @@ -773,7 +773,7 @@ class FsFile { return core.isTerminal(this.#rid); } - setRaw(mode, options = {}) { + setRaw(mode, options = { __proto__: null }) { const cbreak = !!(options.cbreak ?? false); op_set_raw(this.#rid, mode, cbreak); } @@ -889,7 +889,7 @@ async function readTextFile(path, options) { function writeFileSync( path, data, - options = {}, + options = { __proto__: null }, ) { options.signal?.throwIfAborted(); op_fs_write_file_sync( @@ -905,7 +905,7 @@ function writeFileSync( async function writeFile( path, data, - options = {}, + options = { __proto__: null }, ) { let cancelRid; let abortHandler; @@ -951,7 +951,7 @@ async function writeFile( function writeTextFileSync( path, data, - options = {}, + options = { __proto__: null }, ) { const encoder = new TextEncoder(); return writeFileSync(path, encoder.encode(data), options); @@ -960,7 +960,7 @@ function writeTextFileSync( function writeTextFile( path, data, - options = {}, + options = { __proto__: null }, ) { if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, data)) { return writeFile( diff --git a/ext/http/00_serve.ts b/ext/http/00_serve.ts index de94779dcd..1f83ce73d1 100644 --- a/ext/http/00_serve.ts +++ b/ext/http/00_serve.ts @@ -591,7 +591,7 @@ function serve(arg1, arg2) { throw new TypeError("A handler function must be provided."); } if (options === undefined) { - options = {}; + options = { __proto__: null }; } const wantsHttps = hasTlsKeyPairOptions(options); diff --git a/ext/http/02_websocket.ts b/ext/http/02_websocket.ts index 073929961a..21f403bff0 100644 --- a/ext/http/02_websocket.ts +++ b/ext/http/02_websocket.ts @@ -37,7 +37,7 @@ const _ws = Symbol("[[associated_ws]]"); const websocketCvf = buildCaseInsensitiveCommaValueFinder("websocket"); const upgradeCvf = buildCaseInsensitiveCommaValueFinder("upgrade"); -function upgradeWebSocket(request, options = {}) { +function upgradeWebSocket(request, options = { __proto__: null }) { const inner = toInnerRequest(request); const upgrade = request.headers.get("upgrade"); const upgradeHasWebSocketOption = upgrade !== null && diff --git a/ext/io/12_io.js b/ext/io/12_io.js index 094868371e..5c31c279b1 100644 --- a/ext/io/12_io.js +++ b/ext/io/12_io.js @@ -228,7 +228,7 @@ class Stdin { return this.#readable; } - setRaw(mode, options = {}) { + setRaw(mode, options = { __proto__: null }) { const cbreak = !!(options.cbreak ?? false); op_set_raw(this.#rid, mode, cbreak); } diff --git a/ext/kv/01_db.ts b/ext/kv/01_db.ts index 8917e09e03..9aa2036dd5 100644 --- a/ext/kv/01_db.ts +++ b/ext/kv/01_db.ts @@ -210,7 +210,7 @@ class Kv { cursor?: string; reverse?: boolean; consistency?: Deno.KvConsistencyLevel; - } = {}, + } = { __proto__: null }, ): KvListIterator { if (options.limit !== undefined && options.limit <= 0) { throw new Error("limit must be positive"); @@ -340,7 +340,7 @@ class Kv { finishMessageOps.clear(); } - watch(keys: Deno.KvKey[], options = {}) { + watch(keys: Deno.KvKey[], options = { __proto__: null }) { const raw = options.raw ?? false; const rid = op_kv_watch(this.#rid, keys); const lastEntries: (Deno.KvEntryMaybe | undefined)[] = ArrayFrom( diff --git a/ext/net/02_tls.js b/ext/net/02_tls.js index e51df7424a..81bcfb3bd1 100644 --- a/ext/net/02_tls.js +++ b/ext/net/02_tls.js @@ -281,7 +281,7 @@ async function startTls( hostname = "127.0.0.1", caCerts = [], alpnProtocols = undefined, - } = {}, + } = { __proto__: null }, ) { const { 0: rid, 1: localAddr, 2: remoteAddr } = op_tls_start({ rid: conn[internalRidSymbol], diff --git a/ext/web/02_event.js b/ext/web/02_event.js index bb7dea42ce..510085aacc 100644 --- a/ext/web/02_event.js +++ b/ext/web/02_event.js @@ -123,7 +123,7 @@ const _isTrusted = Symbol("[[isTrusted]]"); const _path = Symbol("[[path]]"); class Event { - constructor(type, eventInitDict = {}) { + constructor(type, eventInitDict = { __proto__: null }) { // TODO(lucacasonato): remove when this interface is spec aligned this[SymbolToStringTag] = "Event"; this[_canceledFlag] = false; @@ -1095,7 +1095,7 @@ class ErrorEvent extends Event { lineno = 0, colno = 0, error, - } = {}, + } = { __proto__: null }, ) { super(type, { bubbles: bubbles, @@ -1164,7 +1164,7 @@ class CloseEvent extends Event { wasClean = false, code = 0, reason = "", - } = {}) { + } = { __proto__: null }) { super(type, { bubbles: bubbles, cancelable: cancelable, @@ -1238,7 +1238,7 @@ const MessageEventPrototype = MessageEvent.prototype; class CustomEvent extends Event { #detail = null; - constructor(type, eventInitDict = {}) { + constructor(type, eventInitDict = { __proto__: null }) { super(type, eventInitDict); webidl.requiredArguments( arguments.length, @@ -1280,7 +1280,7 @@ ReflectDefineProperty(CustomEvent.prototype, "detail", { // ProgressEvent could also be used in other DOM progress event emits. // Current use is for FileReader. class ProgressEvent extends Event { - constructor(type, eventInitDict = {}) { + constructor(type, eventInitDict = { __proto__: null }) { super(type, eventInitDict); this.lengthComputable = eventInitDict?.lengthComputable ?? false; @@ -1329,7 +1329,7 @@ class PromiseRejectionEvent extends Event { composed, promise, reason, - } = {}, + } = { __proto__: null }, ) { super(type, { bubbles: bubbles, diff --git a/ext/web/06_streams.js b/ext/web/06_streams.js index 9c2a059805..e01ece6c81 100644 --- a/ext/web/06_streams.js +++ b/ext/web/06_streams.js @@ -5274,7 +5274,7 @@ class ReadableStream { "Argument 1", ); } else { - options = {}; + options = { __proto__: null }; } if (options.mode === undefined) { return acquireReadableStreamDefaultReader(this); @@ -5290,7 +5290,7 @@ class ReadableStream { * @param {PipeOptions=} options * @returns {ReadableStream} */ - pipeThrough(transform, options = {}) { + pipeThrough(transform, options = { __proto__: null }) { webidl.assertBranded(this, ReadableStreamPrototype); const prefix = "Failed to execute 'pipeThrough' on 'ReadableStream'"; webidl.requiredArguments(arguments.length, 1, prefix); @@ -5329,7 +5329,7 @@ class ReadableStream { * @param {PipeOptions=} options * @returns {Promise} */ - pipeTo(destination, options = {}) { + pipeTo(destination, options = { __proto__: null }) { try { webidl.assertBranded(this, ReadableStreamPrototype); const prefix = "Failed to execute 'pipeTo' on 'ReadableStream'"; @@ -5567,7 +5567,7 @@ class ReadableStreamBYOBReader { * @param {ReadableStreamBYOBReaderReadOptions} options * @returns {Promise} */ - read(view, options = {}) { + read(view, options = { __proto__: null }) { try { webidl.assertBranded(this, ReadableStreamBYOBReaderPrototype); const prefix = "Failed to execute 'read' on 'ReadableStreamBYOBReader'"; @@ -6151,8 +6151,8 @@ class TransformStream { */ constructor( transformer = undefined, - writableStrategy = {}, - readableStrategy = {}, + writableStrategy = { __proto__: null }, + readableStrategy = { __proto__: null }, ) { const prefix = "Failed to construct 'TransformStream'"; if (transformer !== undefined) { diff --git a/ext/web/08_text_encoding.js b/ext/web/08_text_encoding.js index 1b777d91b9..9920a81f88 100644 --- a/ext/web/08_text_encoding.js +++ b/ext/web/08_text_encoding.js @@ -63,7 +63,7 @@ class TextDecoder { * @param {string} label * @param {TextDecoderOptions} options */ - constructor(label = "utf-8", options = {}) { + constructor(label = "utf-8", options = { __proto__: null }) { const prefix = "Failed to construct 'TextDecoder'"; label = webidl.converters.DOMString(label, prefix, "Argument 1"); options = webidl.converters.TextDecoderOptions( @@ -288,7 +288,7 @@ class TextDecoderStream { * @param {string} label * @param {TextDecoderOptions} options */ - constructor(label = "utf-8", options = {}) { + constructor(label = "utf-8", options = { __proto__: null }) { const prefix = "Failed to construct 'TextDecoderStream'"; label = webidl.converters.DOMString(label, prefix, "Argument 1"); options = webidl.converters.TextDecoderOptions( diff --git a/ext/web/09_file.js b/ext/web/09_file.js index b98784b942..482a140122 100644 --- a/ext/web/09_file.js +++ b/ext/web/09_file.js @@ -223,7 +223,7 @@ class Blob { * @param {BlobPart[]} blobParts * @param {BlobPropertyBag} options */ - constructor(blobParts = [], options = {}) { + constructor(blobParts = [], options = { __proto__: null }) { const prefix = "Failed to construct 'Blob'"; blobParts = webidl.converters["sequence"]( blobParts, @@ -500,7 +500,7 @@ class File extends Blob { * @param {string} fileName * @param {FilePropertyBag} options */ - constructor(fileBits, fileName, options = {}) { + constructor(fileBits, fileName, options = { __proto__: null }) { const prefix = "Failed to construct 'File'"; webidl.requiredArguments(arguments.length, 2, prefix); diff --git a/ext/web/13_message_port.js b/ext/web/13_message_port.js index 93145e8f74..d94ca13826 100644 --- a/ext/web/13_message_port.js +++ b/ext/web/13_message_port.js @@ -150,7 +150,7 @@ class MessagePort extends EventTarget { * @param {any} message * @param {object[] | StructuredSerializeOptions} transferOrOptions */ - postMessage(message, transferOrOptions = {}) { + postMessage(message, transferOrOptions = { __proto__: null }) { webidl.assertBranded(this, MessagePortPrototype); const prefix = "Failed to execute 'postMessage' on 'MessagePort'"; webidl.requiredArguments(arguments.length, 1, prefix); diff --git a/ext/web/15_performance.js b/ext/web/15_performance.js index adaa501b53..5045c0d318 100644 --- a/ext/web/15_performance.js +++ b/ext/web/15_performance.js @@ -234,7 +234,7 @@ class PerformanceMark extends PerformanceEntry { constructor( name, - options = {}, + options = { __proto__: null }, ) { const prefix = "Failed to construct 'PerformanceMark'"; webidl.requiredArguments(arguments.length, 1, prefix); @@ -441,7 +441,7 @@ class Performance extends EventTarget { mark( markName, - markOptions = {}, + markOptions = { __proto__: null }, ) { webidl.assertBranded(this, PerformancePrototype); const prefix = "Failed to execute 'mark' on 'Performance'"; @@ -466,7 +466,7 @@ class Performance extends EventTarget { measure( measureName, - startOrMeasureOptions = {}, + startOrMeasureOptions = { __proto__: null }, endMark = undefined, ) { webidl.assertBranded(this, PerformancePrototype); diff --git a/ext/webgpu/01_webgpu.js b/ext/webgpu/01_webgpu.js index 502de21248..338678d383 100644 --- a/ext/webgpu/01_webgpu.js +++ b/ext/webgpu/01_webgpu.js @@ -358,7 +358,7 @@ class GPU { * @param {GPURequestAdapterOptions} options */ // deno-lint-ignore require-await - async requestAdapter(options = {}) { + async requestAdapter(options = { __proto__: null }) { webidl.assertBranded(this, GPUPrototype); options = webidl.converters.GPURequestAdapterOptions( options, @@ -449,7 +449,7 @@ class GPUAdapter { * @returns {Promise} */ // deno-lint-ignore require-await - async requestDevice(descriptor = {}) { + async requestDevice(descriptor = { __proto__: null }) { webidl.assertBranded(this, GPUAdapterPrototype); const prefix = "Failed to execute 'requestDevice' on 'GPUAdapter'"; descriptor = webidl.converters.GPUDeviceDescriptor( @@ -1174,7 +1174,7 @@ class GPUDevice extends EventTarget { * @param {GPUSamplerDescriptor} descriptor * @returns {GPUSampler} */ - createSampler(descriptor = {}) { + createSampler(descriptor = { __proto__: null }) { webidl.assertBranded(this, GPUDevicePrototype); const prefix = "Failed to execute 'createSampler' on 'GPUDevice'"; descriptor = webidl.converters.GPUSamplerDescriptor( @@ -1687,7 +1687,7 @@ class GPUDevice extends EventTarget { * @param {GPUCommandEncoderDescriptor} descriptor * @returns {GPUCommandEncoder} */ - createCommandEncoder(descriptor = {}) { + createCommandEncoder(descriptor = { __proto__: null }) { webidl.assertBranded(this, GPUDevicePrototype); const prefix = "Failed to execute 'createCommandEncoder' on 'GPUDevice'"; descriptor = webidl.converters.GPUCommandEncoderDescriptor( @@ -1842,7 +1842,7 @@ defineEventHandler(GPUDevice.prototype, "uncapturederror"); class GPUPipelineError extends DOMException { #reason; - constructor(message = "", options = {}) { + constructor(message = "", options = { __proto__: null }) { const prefix = "Failed to construct 'GPUPipelineError'"; message = webidl.converters.DOMString(message, prefix, "Argument 1"); options = webidl.converters.GPUPipelineErrorInit( @@ -2499,7 +2499,7 @@ class GPUTexture { /** * @param {GPUTextureViewDescriptor} descriptor */ - createView(descriptor = {}) { + createView(descriptor = { __proto__: null }) { webidl.assertBranded(this, GPUTexturePrototype); const prefix = "Failed to execute 'createView' on 'GPUTexture'"; webidl.requiredArguments(arguments.length, 0, prefix); @@ -3306,7 +3306,7 @@ class GPUCommandEncoder { /** * @param {GPUComputePassDescriptor} descriptor */ - beginComputePass(descriptor = {}) { + beginComputePass(descriptor = { __proto__: null }) { webidl.assertBranded(this, GPUCommandEncoderPrototype); const prefix = "Failed to execute 'beginComputePass' on 'GPUCommandEncoder'"; @@ -3757,7 +3757,7 @@ class GPUCommandEncoder { * @param {GPUCommandBufferDescriptor} descriptor * @returns {GPUCommandBuffer} */ - finish(descriptor = {}) { + finish(descriptor = { __proto__: null }) { webidl.assertBranded(this, GPUCommandEncoderPrototype); const prefix = "Failed to execute 'finish' on 'GPUCommandEncoder'"; descriptor = webidl.converters.GPUCommandBufferDescriptor( @@ -4774,7 +4774,7 @@ class GPURenderBundleEncoder { /** * @param {GPURenderBundleDescriptor} descriptor */ - finish(descriptor = {}) { + finish(descriptor = { __proto__: null }) { webidl.assertBranded(this, GPURenderBundleEncoderPrototype); const prefix = "Failed to execute 'finish' on 'GPURenderBundleEncoder'"; descriptor = webidl.converters.GPURenderBundleDescriptor( diff --git a/ext/webidl/00_webidl.js b/ext/webidl/00_webidl.js index 6bf6714c6a..9ea2200f33 100644 --- a/ext/webidl/00_webidl.js +++ b/ext/webidl/00_webidl.js @@ -192,7 +192,12 @@ function createIntegerConversion(bitLength, typeOpts) { const twoToTheBitLength = MathPow(2, bitLength); const twoToOneLessThanTheBitLength = MathPow(2, bitLength - 1); - return (V, prefix = undefined, context = undefined, opts = {}) => { + return ( + V, + prefix = undefined, + context = undefined, + opts = { __proto__: null }, + ) => { let x = toNumber(V); x = censorNegativeZero(x); @@ -251,7 +256,12 @@ function createLongLongConversion(bitLength, { unsigned }) { const lowerBound = unsigned ? 0 : NumberMIN_SAFE_INTEGER; const asBigIntN = unsigned ? BigIntAsUintN : BigIntAsIntN; - return (V, prefix = undefined, context = undefined, opts = {}) => { + return ( + V, + prefix = undefined, + context = undefined, + opts = { __proto__: null }, + ) => { let x = toNumber(V); x = censorNegativeZero(x); @@ -386,7 +396,12 @@ converters["unrestricted double"] = (V, _prefix, _context, _opts) => { return x; }; -converters.DOMString = function (V, prefix, context, opts = {}) { +converters.DOMString = function ( + V, + prefix, + context, + opts = { __proto__: null }, +) { if (typeof V === "string") { return V; } else if (V === null && opts.treatNullAsEmptyString) { @@ -464,7 +479,7 @@ converters.ArrayBuffer = ( V, prefix = undefined, context = undefined, - opts = {}, + opts = { __proto__: null }, ) => { if (!isArrayBuffer(V)) { if (opts.allowShared && !isSharedArrayBuffer(V)) { @@ -490,7 +505,7 @@ converters.DataView = ( V, prefix = undefined, context = undefined, - opts = {}, + opts = { __proto__: null }, ) => { if (!isDataView(V)) { throw makeException( @@ -539,7 +554,7 @@ ArrayPrototypeForEach( V, prefix = undefined, context = undefined, - opts = {}, + opts = { __proto__: null }, ) => { if (TypedArrayPrototypeGetSymbolToStringTag(V) !== name) { throw makeException( @@ -572,7 +587,7 @@ converters.ArrayBufferView = ( V, prefix = undefined, context = undefined, - opts = {}, + opts = { __proto__: null }, ) => { if (!ArrayBufferIsView(V)) { throw makeException( @@ -604,7 +619,7 @@ converters.BufferSource = ( V, prefix = undefined, context = undefined, - opts = {}, + opts = { __proto__: null }, ) => { if (ArrayBufferIsView(V)) { let buffer; @@ -722,7 +737,7 @@ function createDictionaryConverter(name, ...dictionaries) { return a.key < b.key ? -1 : 1; }); - const defaultValues = {}; + const defaultValues = { __proto__: null }; for (let i = 0; i < allMembers.length; ++i) { const member = allMembers[i]; if (ReflectHas(member, "defaultValue")) { @@ -747,7 +762,12 @@ function createDictionaryConverter(name, ...dictionaries) { } } - return function (V, prefix = undefined, context = undefined, opts = {}) { + return function ( + V, + prefix = undefined, + context = undefined, + opts = { __proto__: null }, + ) { const typeV = type(V); switch (typeV) { case "Undefined": @@ -812,7 +832,12 @@ function createDictionaryConverter(name, ...dictionaries) { function createEnumConverter(name, values) { const E = new SafeSet(values); - return function (V, prefix = undefined, _context = undefined, _opts = {}) { + return function ( + V, + prefix = undefined, + _context = undefined, + _opts = { __proto__: null }, + ) { const S = String(V); if (!E.has(S)) { @@ -828,7 +853,12 @@ function createEnumConverter(name, values) { } function createNullableConverter(converter) { - return (V, prefix = undefined, context = undefined, opts = {}) => { + return ( + V, + prefix = undefined, + context = undefined, + opts = { __proto__: null }, + ) => { // FIXME: If Type(V) is not Object, and the conversion to an IDL value is // being performed due to V being assigned to an attribute whose type is a // nullable callback function that is annotated with @@ -842,7 +872,12 @@ function createNullableConverter(converter) { // https://heycam.github.io/webidl/#es-sequence function createSequenceConverter(converter) { - return function (V, prefix = undefined, context = undefined, opts = {}) { + return function ( + V, + prefix = undefined, + context = undefined, + opts = { __proto__: null }, + ) { if (type(V) !== "Object") { throw makeException( TypeError, @@ -894,7 +929,7 @@ function createRecordConverter(keyConverter, valueConverter) { context, ); } - const result = {}; + const result = { __proto__: null }; // Fast path for common case (not a Proxy) if (!core.isProxy(V)) { for (const key in V) { diff --git a/ext/websocket/02_websocketstream.js b/ext/websocket/02_websocketstream.js index 01aef45d32..92b1c6eae5 100644 --- a/ext/websocket/02_websocketstream.js +++ b/ext/websocket/02_websocketstream.js @@ -474,7 +474,7 @@ class WebSocketError extends DOMException { #closeCode; #reason; - constructor(message = "", init = {}) { + constructor(message = "", init = { __proto__: null }) { super(message, "WebSocketError"); this[webidl.brand] = webidl.brand; diff --git a/runtime/js/10_permissions.js b/runtime/js/10_permissions.js index 4e7d0d3404..f2b3fba008 100644 --- a/runtime/js/10_permissions.js +++ b/runtime/js/10_permissions.js @@ -268,7 +268,7 @@ const permissions = new Permissions(illegalConstructorKey); /** Converts all file URLs in FS allowlists to paths. */ function serializePermissions(permissions) { if (typeof permissions == "object" && permissions != null) { - const serializedPermissions = {}; + const serializedPermissions = { __proto__: null }; for ( const key of new SafeArrayIterator(["read", "write", "run", "ffi"]) ) { diff --git a/runtime/js/11_workers.js b/runtime/js/11_workers.js index 5d24df93de..3853761920 100644 --- a/runtime/js/11_workers.js +++ b/runtime/js/11_workers.js @@ -91,7 +91,7 @@ class Worker extends EventTarget { // still be messages left to receive. #status = "RUNNING"; - constructor(specifier, options = {}) { + constructor(specifier, options = { __proto__: null }) { super(); specifier = String(specifier); const { @@ -254,7 +254,7 @@ class Worker extends EventTarget { } }; - postMessage(message, transferOrOptions = {}) { + postMessage(message, transferOrOptions = { __proto__: null }) { const prefix = "Failed to execute 'postMessage' on 'MessagePort'"; webidl.requiredArguments(arguments.length, 1, prefix); message = webidl.converters.any(message); diff --git a/runtime/js/40_process.js b/runtime/js/40_process.js index e6c8659286..6db04468f0 100644 --- a/runtime/js/40_process.js +++ b/runtime/js/40_process.js @@ -134,7 +134,7 @@ function run({ cmd, cwd = undefined, clearEnv = false, - env = {}, + env = { __proto__: null }, gid = undefined, uid = undefined, stdout = "inherit", @@ -172,7 +172,7 @@ function spawnChildInner(opFn, command, apiName, { args = [], cwd = undefined, clearEnv = false, - env = {}, + env = { __proto__: null }, uid = undefined, gid = undefined, stdin = "null", @@ -181,7 +181,7 @@ function spawnChildInner(opFn, command, apiName, { signal = undefined, windowsRawArguments = false, ipc = -1, -} = {}) { +} = { __proto__: null }) { const child = opFn({ cmd: pathFromURL(command), args: ArrayPrototypeMap(args, String), @@ -202,7 +202,7 @@ function spawnChildInner(opFn, command, apiName, { }); } -function spawnChild(command, options = {}) { +function spawnChild(command, options = { __proto__: null }) { return spawnChildInner( op_spawn_child, command, @@ -392,14 +392,14 @@ function spawnSync(command, { args = [], cwd = undefined, clearEnv = false, - env = {}, + env = { __proto__: null }, uid = undefined, gid = undefined, stdin = "null", stdout = "piped", stderr = "piped", windowsRawArguments = false, -} = {}) { +} = { __proto__: null }) { if (stdin === "piped") { throw new TypeError( "Piped stdin is not supported for this function, use 'Deno.Command().spawn()' instead", diff --git a/runtime/js/40_signals.js b/runtime/js/40_signals.js index 9d3cd4092d..41f25af677 100644 --- a/runtime/js/40_signals.js +++ b/runtime/js/40_signals.js @@ -26,7 +26,7 @@ function unbindSignal(rid) { // Stores signal listeners and resource data. This has type of // `Record void> }` -const signalData = {}; +const signalData = { __proto__: null }; /** Gets the signal handlers and resource data of the given signal */ function getSignalData(signo) { diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 02ac7b6020..2e13976a71 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -264,9 +264,9 @@ const unstableIds = { workerOptions: 11, }; -const denoNsUnstableById = {}; +const denoNsUnstableById = { __proto__: null }; -// denoNsUnstableById[unstableIds.broadcastChannel] = {} +// denoNsUnstableById[unstableIds.broadcastChannel] = { __proto__: null } denoNsUnstableById[unstableIds.cron] = { cron: cron.cron, @@ -308,13 +308,13 @@ denoNsUnstableById[unstableIds.net] = { ), }; -// denoNsUnstableById[unstableIds.unsafeProto] = {} +// denoNsUnstableById[unstableIds.unsafeProto] = { __proto__: null } denoNsUnstableById[unstableIds.webgpu] = { UnsafeWindowSurface: webgpuSurface.UnsafeWindowSurface, }; -// denoNsUnstableById[unstableIds.workerOptions] = {} +// denoNsUnstableById[unstableIds.workerOptions] = { __proto__: null } // when editing this list, also update unstableDenoProps in cli/tsc/99_main_compiler.js const denoNsUnstable = { diff --git a/runtime/js/98_global_scope_shared.js b/runtime/js/98_global_scope_shared.js index e7504143e2..b6e4802163 100644 --- a/runtime/js/98_global_scope_shared.js +++ b/runtime/js/98_global_scope_shared.js @@ -145,7 +145,7 @@ const windowOrWorkerGlobalScope = { [webidl.brand]: core.propNonEnumerable(webidl.brand), }; -const unstableForWindowOrWorkerGlobalScope = {}; +const unstableForWindowOrWorkerGlobalScope = { __proto__: null }; unstableForWindowOrWorkerGlobalScope[unstableIds.broadcastChannel] = { BroadcastChannel: core.propNonEnumerable(broadcastChannel.BroadcastChannel), }; diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index fcec6b91a5..6e423af6b6 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -252,7 +252,7 @@ function workerClose() { op_worker_close(); } -function postMessage(message, transferOrOptions = {}) { +function postMessage(message, transferOrOptions = { __proto__: null }) { const prefix = "Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope'"; webidl.requiredArguments(arguments.length, 1, prefix); diff --git a/tests/unit/event_test.ts b/tests/unit/event_test.ts index c82873cf65..bd398fd410 100644 --- a/tests/unit/event_test.ts +++ b/tests/unit/event_test.ts @@ -141,3 +141,17 @@ Deno.test(function inspectEvent() { `Event {\n bubbles: false,\n cancelable: false,`, ); }); + +Deno.test("default argument is null prototype", () => { + const event = new Event("test"); + assertEquals(event.bubbles, false); + + // @ts-ignore this is on purpose to check if overriding prototype + // has effect on `Event. + Object.prototype.bubbles = true; + const event2 = new Event("test"); + assertEquals(event2.bubbles, false); + + // @ts-ignore this is done on purpose + delete Object.prototype.bubbles; +});