1// Adapted from SES/Caja - Copyright (C) 2011 Google Inc. 2// Copyright (C) 2018 Agoric 3 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15// SPDX-License-Identifier: Apache-2.0 16 17// Based upon: 18// https://github.com/google/caja/blob/HEAD/src/com/google/caja/ses/startSES.js 19// https://github.com/google/caja/blob/HEAD/src/com/google/caja/ses/repairES5.js 20// https://github.com/tc39/proposal-ses/blob/e5271cc42a257a05dcae2fd94713ed2f46c08620/shim/src/freeze.js 21 22'use strict'; 23 24const { 25 AggregateError, 26 AggregateErrorPrototype, 27 Array, 28 ArrayBuffer, 29 ArrayBufferPrototype, 30 ArrayIteratorPrototype, 31 ArrayPrototype, 32 ArrayPrototypeForEach, 33 ArrayPrototypePush, 34 BigInt, 35 BigInt64Array, 36 BigInt64ArrayPrototype, 37 BigIntPrototype, 38 BigUint64Array, 39 BigUint64ArrayPrototype, 40 Boolean, 41 BooleanPrototype, 42 DataView, 43 DataViewPrototype, 44 Date, 45 DatePrototype, 46 Error, 47 ErrorPrototype, 48 EvalError, 49 EvalErrorPrototype, 50 FinalizationRegistry, 51 FinalizationRegistryPrototype, 52 Float32Array, 53 Float32ArrayPrototype, 54 Float64Array, 55 Float64ArrayPrototype, 56 Function, 57 FunctionPrototype, 58 Int16Array, 59 Int16ArrayPrototype, 60 Int32Array, 61 Int32ArrayPrototype, 62 Int8Array, 63 Int8ArrayPrototype, 64 IteratorPrototype, 65 Map, 66 MapPrototype, 67 Number, 68 NumberPrototype, 69 Object, 70 ObjectDefineProperty, 71 ObjectFreeze, 72 ObjectGetOwnPropertyDescriptor, 73 ObjectGetOwnPropertyDescriptors, 74 ObjectGetOwnPropertyNames, 75 ObjectGetOwnPropertySymbols, 76 ObjectGetPrototypeOf, 77 ObjectPrototype, 78 ObjectPrototypeHasOwnProperty, 79 Promise, 80 PromisePrototype, 81 Proxy, 82 RangeError, 83 RangeErrorPrototype, 84 ReferenceError, 85 ReferenceErrorPrototype, 86 ReflectOwnKeys, 87 RegExp, 88 RegExpPrototype, 89 SafeSet, 90 Set, 91 SetPrototype, 92 String, 93 StringIteratorPrototype, 94 StringPrototype, 95 Symbol, 96 SymbolIterator, 97 SymbolMatchAll, 98 SymbolPrototype, 99 SyntaxError, 100 SyntaxErrorPrototype, 101 TypeError, 102 TypeErrorPrototype, 103 TypedArray, 104 TypedArrayPrototype, 105 Uint16Array, 106 Uint16ArrayPrototype, 107 Uint32Array, 108 Uint32ArrayPrototype, 109 Uint8Array, 110 Uint8ArrayPrototype, 111 Uint8ClampedArray, 112 Uint8ClampedArrayPrototype, 113 URIError, 114 URIErrorPrototype, 115 WeakMap, 116 WeakMapPrototype, 117 WeakRef, 118 WeakRefPrototype, 119 WeakSet, 120 WeakSetPrototype, 121 decodeURI, 122 decodeURIComponent, 123 encodeURI, 124 encodeURIComponent, 125 escape, 126 globalThis, 127 unescape, 128} = primordials; 129 130const { 131 Atomics, 132 Intl, 133 SharedArrayBuffer, 134 WebAssembly, 135} = globalThis; 136 137module.exports = function() { 138 const { Console } = require('internal/console/constructor'); 139 const console = require('internal/console/global'); 140 const { 141 clearImmediate, 142 clearInterval, 143 clearTimeout, 144 setImmediate, 145 setInterval, 146 setTimeout, 147 } = require('timers'); 148 149 const intrinsicPrototypes = [ 150 // 20 Fundamental Objects 151 ObjectPrototype, // 20.1 152 FunctionPrototype, // 20.2 153 BooleanPrototype, // 20.3 154 SymbolPrototype, // 20.4 155 156 ErrorPrototype, // 20.5 157 AggregateErrorPrototype, 158 EvalErrorPrototype, 159 RangeErrorPrototype, 160 ReferenceErrorPrototype, 161 SyntaxErrorPrototype, 162 TypeErrorPrototype, 163 URIErrorPrototype, 164 165 // 21 Numbers and Dates 166 NumberPrototype, // 21.1 167 BigIntPrototype, // 21.2 168 DatePrototype, // 21.4 169 170 // 22 Text Processing 171 StringPrototype, // 22.1 172 StringIteratorPrototype, // 22.1.5 173 RegExpPrototype, // 22.2 174 // 22.2.7 RegExpStringIteratorPrototype 175 ObjectGetPrototypeOf(/e/[SymbolMatchAll]()), 176 177 // 23 Indexed Collections 178 ArrayPrototype, // 23.1 179 ArrayIteratorPrototype, // 23.1.5 180 181 TypedArrayPrototype, // 23.2 182 Int8ArrayPrototype, 183 Uint8ArrayPrototype, 184 Uint8ClampedArrayPrototype, 185 Int16ArrayPrototype, 186 Uint16ArrayPrototype, 187 Int32ArrayPrototype, 188 Uint32ArrayPrototype, 189 Float32ArrayPrototype, 190 Float64ArrayPrototype, 191 BigInt64ArrayPrototype, 192 BigUint64ArrayPrototype, 193 194 // 24 Keyed Collections 195 MapPrototype, // 24.1 196 // 24.1.5 MapIteratorPrototype 197 ObjectGetPrototypeOf(new Map()[SymbolIterator]()), 198 SetPrototype, // 24.2 199 // 24.2.5 SetIteratorPrototype 200 ObjectGetPrototypeOf(new Set()[SymbolIterator]()), 201 WeakMapPrototype, // 24.3 202 WeakSetPrototype, // 24.4 203 204 // 25 Structured Data 205 ArrayBufferPrototype, // 25.1 206 SharedArrayBuffer.prototype, // 25.2 207 DataViewPrototype, // 25.3 208 209 // 26 Managing Memory 210 WeakRefPrototype, // 26.1 211 FinalizationRegistryPrototype, // 26.2 212 213 // 27 Control Abstraction Objects 214 // 27.1 Iteration 215 IteratorPrototype, // 27.1.2 IteratorPrototype 216 // 27.1.3 AsyncIteratorPrototype 217 ObjectGetPrototypeOf(ObjectGetPrototypeOf(ObjectGetPrototypeOf( 218 (async function*() {})(), 219 ))), 220 PromisePrototype, // 27.2 221 222 // Other APIs / Web Compatibility 223 Console.prototype, 224 WebAssembly.Module.prototype, 225 WebAssembly.Instance.prototype, 226 WebAssembly.Table.prototype, 227 WebAssembly.Memory.prototype, 228 WebAssembly.CompileError.prototype, 229 WebAssembly.LinkError.prototype, 230 WebAssembly.RuntimeError.prototype, 231 ]; 232 const intrinsics = [ 233 // 10.2.4.1 ThrowTypeError 234 ObjectGetOwnPropertyDescriptor(FunctionPrototype, 'caller').get, 235 236 // 19 The Global Object 237 // 19.2 Function Properties of the Global Object 238 // eslint-disable-next-line node-core/prefer-primordials 239 eval, 240 // eslint-disable-next-line node-core/prefer-primordials 241 isFinite, 242 // eslint-disable-next-line node-core/prefer-primordials 243 isNaN, 244 // eslint-disable-next-line node-core/prefer-primordials 245 parseFloat, 246 // eslint-disable-next-line node-core/prefer-primordials 247 parseInt, 248 // 19.2.6 URI Handling Functions 249 decodeURI, 250 decodeURIComponent, 251 encodeURI, 252 encodeURIComponent, 253 254 // 20 Fundamental Objects 255 Object, // 20.1 256 Function, // 20.2 257 Boolean, // 20.3 258 Symbol, // 20.4 259 260 Error, // 20.5 261 AggregateError, 262 EvalError, 263 RangeError, 264 ReferenceError, 265 SyntaxError, 266 TypeError, 267 URIError, 268 269 // 21 Numbers and Dates 270 Number, // 21.1 271 BigInt, // 21.2 272 // eslint-disable-next-line node-core/prefer-primordials 273 Math, // 21.3 274 Date, // 21.4 275 276 // 22 Text Processing 277 String, // 22.1 278 StringIteratorPrototype, // 22.1.5 279 RegExp, // 22.2 280 // 22.2.7 RegExpStringIteratorPrototype 281 ObjectGetPrototypeOf(/e/[SymbolMatchAll]()), 282 283 // 23 Indexed Collections 284 Array, // 23.1 285 ArrayIteratorPrototype, // 23.1.5 286 // 23.2 TypedArray 287 TypedArray, 288 Int8Array, 289 Uint8Array, 290 Uint8ClampedArray, 291 Int16Array, 292 Uint16Array, 293 Int32Array, 294 Uint32Array, 295 Float32Array, 296 Float64Array, 297 BigInt64Array, 298 BigUint64Array, 299 300 // 24 Keyed Collections 301 Map, // 24.1 302 // 24.1.5 MapIteratorPrototype 303 ObjectGetPrototypeOf(new Map()[SymbolIterator]()), 304 Set, // 24.2 305 // 24.2.5 SetIteratorPrototype 306 ObjectGetPrototypeOf(new Set()[SymbolIterator]()), 307 WeakMap, // 24.3 308 WeakSet, // 24.4 309 310 // 25 Structured Data 311 ArrayBuffer, // 25.1 312 SharedArrayBuffer, // 25.2 313 DataView, // 25.3 314 Atomics, // 25.4 315 // eslint-disable-next-line node-core/prefer-primordials 316 JSON, // 25.5 317 318 // 26 Managing Memory 319 WeakRef, // 26.1 320 FinalizationRegistry, // 26.2 321 322 // 27 Control Abstraction Objects 323 // 27.1 Iteration 324 ObjectGetPrototypeOf(ArrayIteratorPrototype), // 27.1.2 IteratorPrototype 325 // 27.1.3 AsyncIteratorPrototype 326 ObjectGetPrototypeOf(ObjectGetPrototypeOf(ObjectGetPrototypeOf( 327 (async function*() {})(), 328 ))), 329 Promise, // 27.2 330 // 27.3 GeneratorFunction 331 ObjectGetPrototypeOf(function* () {}), 332 // 27.4 AsyncGeneratorFunction 333 ObjectGetPrototypeOf(async function* () {}), 334 // 27.7 AsyncFunction 335 ObjectGetPrototypeOf(async function() {}), 336 337 // 28 Reflection 338 // eslint-disable-next-line node-core/prefer-primordials 339 Reflect, // 28.1 340 Proxy, // 28.2 341 342 // B.2.1 343 escape, 344 unescape, 345 346 // Other APIs / Web Compatibility 347 clearImmediate, 348 clearInterval, 349 clearTimeout, 350 setImmediate, 351 setInterval, 352 setTimeout, 353 console, 354 WebAssembly, 355 ]; 356 357 if (typeof Intl !== 'undefined') { 358 ArrayPrototypePush(intrinsicPrototypes, 359 Intl.Collator.prototype, 360 Intl.DateTimeFormat.prototype, 361 Intl.ListFormat.prototype, 362 Intl.NumberFormat.prototype, 363 Intl.PluralRules.prototype, 364 Intl.RelativeTimeFormat.prototype, 365 ); 366 ArrayPrototypePush(intrinsics, Intl); 367 } 368 369 ArrayPrototypeForEach(intrinsicPrototypes, enableDerivedOverrides); 370 371 const frozenSet = new WeakSet(); 372 ArrayPrototypeForEach(intrinsics, deepFreeze); 373 374 // 19.1 Value Properties of the Global Object 375 ObjectDefineProperty(globalThis, 'globalThis', { 376 __proto__: null, 377 configurable: false, 378 writable: false, 379 value: globalThis, 380 }); 381 382 // Objects that are deeply frozen. 383 function deepFreeze(root) { 384 /** 385 * "innerDeepFreeze()" acts like "Object.freeze()", except that: 386 * 387 * To deepFreeze an object is to freeze it and all objects transitively 388 * reachable from it via transitive reflective property and prototype 389 * traversal. 390 */ 391 function innerDeepFreeze(node) { 392 // Objects that we have frozen in this round. 393 const freezingSet = new SafeSet(); 394 395 // If val is something we should be freezing but aren't yet, 396 // add it to freezingSet. 397 function enqueue(val) { 398 if (Object(val) !== val) { 399 // ignore primitives 400 return; 401 } 402 const type = typeof val; 403 if (type !== 'object' && type !== 'function') { 404 // NB: handle for any new cases in future 405 } 406 if (frozenSet.has(val) || freezingSet.has(val)) { 407 // TODO: Use uncurried form 408 // Ignore if already frozen or freezing 409 return; 410 } 411 freezingSet.add(val); // TODO: Use uncurried form 412 } 413 414 function doFreeze(obj) { 415 // Immediately freeze the object to ensure reactive 416 // objects such as proxies won't add properties 417 // during traversal, before they get frozen. 418 419 // Object are verified before being enqueued, 420 // therefore this is a valid candidate. 421 // Throws if this fails (strict mode). 422 ObjectFreeze(obj); 423 424 // We rely upon certain commitments of Object.freeze and proxies here 425 426 // Get stable/immutable outbound links before a Proxy has a chance to do 427 // something sneaky. 428 const proto = ObjectGetPrototypeOf(obj); 429 const descs = ObjectGetOwnPropertyDescriptors(obj); 430 enqueue(proto); 431 ArrayPrototypeForEach(ReflectOwnKeys(descs), (name) => { 432 const desc = descs[name]; 433 if (ObjectPrototypeHasOwnProperty(desc, 'value')) { 434 // todo uncurried form 435 enqueue(desc.value); 436 } else { 437 enqueue(desc.get); 438 enqueue(desc.set); 439 } 440 }); 441 } 442 443 function dequeue() { 444 // New values added before forEach() has finished will be visited. 445 freezingSet.forEach(doFreeze); // TODO: Curried forEach 446 } 447 448 function commit() { 449 // TODO: Curried forEach 450 // We capture the real WeakSet.prototype.add above, in case someone 451 // changes it. The two-argument form of forEach passes the second 452 // argument as the 'this' binding, so we add to the correct set. 453 freezingSet.forEach(frozenSet.add, frozenSet); 454 } 455 456 enqueue(node); 457 dequeue(); 458 commit(); 459 } 460 461 innerDeepFreeze(root); 462 return root; 463 } 464 465 /** 466 * For a special set of properties (defined below), it ensures that the 467 * effect of freezing does not suppress the ability to override these 468 * properties on derived objects by simple assignment. 469 * 470 * Because of lack of sufficient foresight at the time, ES5 unfortunately 471 * specified that a simple assignment to a non-existent property must fail if 472 * it would override a non-writable data property of the same name. (In 473 * retrospect, this was a mistake, but it is now too late and we must live 474 * with the consequences.) As a result, simply freezing an object to make it 475 * tamper proof has the unfortunate side effect of breaking previously correct 476 * code that is considered to have followed JS best practices, if this 477 * previous code used assignment to override. 478 * 479 * To work around this mistake, deepFreeze(), prior to freezing, replaces 480 * selected configurable own data properties with accessor properties which 481 * simulate what we should have specified -- that assignments to derived 482 * objects succeed if otherwise possible. 483 */ 484 function enableDerivedOverride(obj, prop, desc) { 485 if (ObjectPrototypeHasOwnProperty(desc, 'value') && desc.configurable) { 486 const value = desc.value; 487 488 function getter() { 489 return value; 490 } 491 492 // Re-attach the data property on the object so 493 // it can be found by the deep-freeze traversal process. 494 getter.value = value; 495 496 function setter(newValue) { 497 if (obj === this) { 498 // eslint-disable-next-line no-restricted-syntax 499 throw new TypeError( 500 `Cannot assign to read only property '${prop}' of object '${obj}'`, 501 ); 502 } 503 if (ObjectPrototypeHasOwnProperty(this, prop)) { 504 this[prop] = newValue; 505 } else { 506 ObjectDefineProperty(this, prop, { 507 __proto__: null, 508 value: newValue, 509 writable: true, 510 enumerable: true, 511 configurable: true, 512 }); 513 } 514 } 515 516 ObjectDefineProperty(obj, prop, { 517 __proto__: null, 518 get: getter, 519 set: setter, 520 enumerable: desc.enumerable, 521 configurable: desc.configurable, 522 }); 523 } 524 } 525 526 function enableDerivedOverrides(obj) { 527 if (!obj) { 528 return; 529 } 530 const descs = ObjectGetOwnPropertyDescriptors(obj); 531 if (!descs) { 532 return; 533 } 534 ArrayPrototypeForEach(ObjectGetOwnPropertyNames(obj), (prop) => { 535 return enableDerivedOverride(obj, prop, descs[prop]); 536 }); 537 ArrayPrototypeForEach(ObjectGetOwnPropertySymbols(obj), (prop) => { 538 return enableDerivedOverride(obj, prop, descs[prop]); 539 }); 540 } 541}; 542