···
const noop = function () {} as any;
21
+
const _freeze = Object.freeze;
22
+
const _seal = Object.seal;
23
+
const _keys = Object.keys;
24
+
const _getOwnPropertyNames = Object.getOwnPropertyNames;
25
+
const _getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
26
+
const _defineProperty = Object.defineProperty;
27
+
const _create = Object.create;
type Object = Record<string | symbol, unknown>;
···
function freeze(target: Object): Object {
36
-
return typeof Object.freeze === 'function'
37
-
? Object.freeze(target)
43
+
try { _freeze(target); } catch (_error) {}
44
+
try { _seal(target); } catch (_error) {}
48
+
const masked = new Set();
// Wrap any given target with a masking object preventing access to prototype properties
42
-
function mask(target: any) {
51
+
function mask(target: any, toplevel: boolean) {
(typeof target !== 'function' && typeof target !== 'object')
···
// If the target isn't a function or object then skip
60
+
if (!('constructor' in target)) {
64
+
if (toplevel && masked.has(target)) {
66
+
} else if (toplevel) {
// Create a stand-in object or function
52
-
typeof target === 'function'
71
+
let standin = target;
73
+
standin = typeof target === 'function'
? (function (this: any) {
54
-
return target.apply(this, arguments);
75
+
if (new.target === undefined) {
76
+
return target.apply(this, arguments);
78
+
return new (target.bind.apply(target, arguments));
56
-
: Object.create(null);
// Copy all known keys over to the stand-in and recursively apply `withProxy`
// Prevent unsafe keys from being accessed
const keys = ["__proto__", "constructor"];
···
// Chromium already restricts access to certain globals in an
// iframe, this try catch block is to avoid
// "Failed to enumerate the properties of 'Storage': access is denied for this document"
64
-
keys.push(...Object.getOwnPropertyNames(target));
91
+
keys.push(..._getOwnPropertyNames(target));
93
+
keys.push(..._keys(target));
96
+
const seen = new Set();
for (let i = 0; i < keys.length; i++) {
99
+
if (seen.has(key)) {
(typeof standin !== 'function' || (key !== 'arguments' && key !== 'caller'))
73
-
Object.defineProperty(standin, key, {
75
-
get: safeKey(target, key)
77
-
return typeof target[key] === 'function' ||
78
-
typeof target[key] === 'object'
106
+
const descriptor = _getOwnPropertyDescriptor(standin, key) || {};
107
+
if (descriptor.configurable) {
108
+
_defineProperty(standin, key, {
109
+
enumerable: descriptor.enumerable,
110
+
configurable: descriptor.configurable,
112
+
if (!safeKey(target, key)) {
116
+
const value = mask(target[key], false);
117
+
return () => value;
122
+
return () => mask(target[key], false);
86
-
if (standin.prototype != null)
87
-
standin.prototype = freeze(Object.create(null));
130
+
if (standin.prototype != null) {
131
+
standin.prototype = _create(null);
···
// Get all available global names on `globalThis` and remove keys that are
107
-
const trueGlobalKeys = Object.getOwnPropertyNames(trueGlobal).filter(
153
+
const trueGlobalKeys = _getOwnPropertyNames(trueGlobal).filter(
···
document.head.appendChild(iframe);
// We copy over all known globals (as seen on the original `globalThis`)
// from the new global we receive from the iframe
126
-
vmGlobals = Object.create(null);
172
+
vmGlobals = _create(null);
for (let i = 0, l = trueGlobalKeys.length; i < l; i++) {
const key = trueGlobalKeys[i];
vmGlobals[key] = iframe.contentWindow![key];
···
if (iframe) iframe.remove();
183
+
} else if (typeof require === 'function') {
184
+
vmGlobals = _create(null);
185
+
const scriptGlobal = new (require('vm').Script)('exports = globalThis').runInNewContext({}).exports;
186
+
for (let i = 0, l = trueGlobalKeys.length; i < l; i++) {
187
+
const key = trueGlobalKeys[i];
188
+
vmGlobals[key] = scriptGlobal[key];
139
-
safeGlobal = Object.create(null);
192
+
safeGlobal = _create(null);
// The safe global is initialised by copying all values from either `globalThis`
// or the isolated global. They're wrapped using `withProxy` which further disallows
for (let i = 0, l = trueGlobalKeys.length; i < l; i++) {
const key = trueGlobalKeys[i];
146
-
safeGlobal[key] = mask(vmGlobals[key]);
199
+
safeGlobal[key] = mask(vmGlobals[key], true);
// We then reset all globals that are present on `globalThis` directly
···
for (const key in ignore) safeGlobal[key] = undefined;
// It _might_ be safe to expose the Function constructor like this... who knows
safeGlobal!.Function = SafeFunction;
// Lastly, we also disallow certain property accesses on the safe global
// Wrap any given target with a Proxy preventing access to unscopables
157
-
if (typeof Proxy === 'function') {
158
-
// Wrap the target in a Proxy that disallows access to some keys
159
-
return (safeGlobal = new Proxy(safeGlobal!, {
160
-
// Return a value, if it's allowed to be returned and mask this value
161
-
get(target, _key) {
162
-
const key = safeKey(target, _key);
163
-
return key !== undefined ? target[key] : undefined;
165
-
has(_target, _key) {
169
-
deleteProperty: noop,
170
-
defineProperty: noop,
171
-
getOwnPropertyDescriptor: noop,
174
-
// NOTE: Some property accesses may leak through here without the Proxy
175
-
return (safeGlobal = mask(safeGlobal));
211
+
return freeze(safeGlobal!);