···25252626It only does three simple things:
2727- Isolate the [global object](https://developer.mozilla.org/en-US/docs/Glossary/Global_object) and uses a separate object using a `with` statement
2828-- Wraps all passed through globals, like `Array`, in a recursive proxy that disallows access to prototype-polluting propeties, such as `constructor`
2828+- Wraps all passed through globals, like `Array`, in a recursive masking object that disallows access to prototype-polluting propeties, such as `constructor`
2929- In the browser: Creates an `iframe` element and uses that frame's globals instead
30303131If you haven't run away screaming yet, maybe that's what you're looking for. Just a bit more safety.
+24-53
src/index.ts
···11-// These are marked with `Symbol.unscopables` for the Proxy
22-const unscopables = {
33- __proto__: true,
44- prototype: true,
55- constructor: true,
66-};
77-81// Keys that'll always not be included (for Node.js)
92const ignore = {
103 sys: true,
···3023function safeKey(target: Object, key: string | symbol): string | undefined {
3124 return key !== 'constructor' &&
3225 key !== '__proto__' &&
3333- key !== 'constructor' &&
2626+ key !== 'prototype' &&
3427 typeof key !== 'symbol' &&
3528 key in target
3629 ? key
3730 : undefined;
3831}
39324040-// Wrap any given target with a Proxy preventing access to unscopables
4141-function withProxy(target: any) {
3333+// Wrap any given target with a masking object preventing access to prototype properties
3434+function mask(target: any) {
4235 if (
4336 target == null ||
4437 (typeof target !== 'function' && typeof target !== 'object')
4538 ) {
4639 // If the target isn't a function or object then skip
4740 return target;
4848- } else if (
4949- typeof Proxy === 'function' &&
5050- typeof Symbol === 'function' &&
5151- Symbol.unscopables
5252- ) {
5353- // Mark hidden keys as unscopable
5454- target[Symbol.unscopables] = unscopables;
5555- // Wrap the target in a Proxy that disallows access to some keys
5656- return new Proxy(target, {
5757- // Return a value, if it's allowed to be returned, and wrap that value in a proxy recursively
5858- get(target, _key) {
5959- const key = safeKey(target, _key);
6060- return key !== undefined ? withProxy(target[key]) : undefined;
6161- },
6262- has(target, key) {
6363- return !!safeKey(target, key);
6464- },
6565- set: noop,
6666- deleteProperty: noop,
6767- defineProperty: noop,
6868- getOwnPropertyDescriptor: noop,
6969- });
7041 }
7171-7242 // Create a stand-in object or function
7343 const standin =
7444 typeof target === 'function'
7575- ? function (this: any) {
4545+ ? (function (this: any) {
7646 return target.apply(this, arguments);
7777- }
4747+ })
7848 : Object.create(null);
7949 // Copy all known keys over to the stand-in and recursively apply `withProxy`
8050 // Prevent unsafe keys from being accessed
8181- const keys = ['constructor', 'prototype', '__proto__'].concat(
8282- Object.getOwnPropertyNames(target)
8383- );
5151+ const keys = Object.getOwnPropertyNames(target)
8452 for (let i = 0; i < keys.length; i++) {
8553 const key = keys[i];
8686- Object.defineProperty(standin, key, {
8787- enumerable: true,
8888- get: safeKey(target, key)
8989- ? () => {
9090- return typeof target[key] === 'function' ||
9191- typeof target[key] === 'object'
9292- ? withProxy(target[key])
9393- : target[key];
9494- }
9595- : noop,
9696- });
5454+ if (key !== 'prototype') {
5555+ Object.defineProperty(standin, key, {
5656+ enumerable: true,
5757+ get: safeKey(target, key)
5858+ ? () => {
5959+ return typeof target[key] === 'function' ||
6060+ typeof target[key] === 'object'
6161+ ? mask(target[key])
6262+ : target[key];
6363+ }
6464+ : noop,
6565+ });
6666+ }
9767 }
9898-9999- return standin;
6868+ return typeof Object.freeze === 'function'
6969+ ? Object.freeze(standin)
7070+ : standin;
10071}
1017210273let safeGlobal: Record<string | symbol, unknown> | void;
···153124 // certain key accesses
154125 for (let i = 0, l = trueGlobalKeys.length; i < l; i++) {
155126 const key = trueGlobalKeys[i];
156156- safeGlobal[key] = withProxy(vmGlobals[key]);
127127+ safeGlobal[key] = mask(vmGlobals[key]);
157128 }
158129159130 // We then reset all globals that are present on `globalThis` directly
···163134 // It _might_ be safe to expose the Function constructor like this... who knows
164135 safeGlobal!.Function = SafeFunction;
165136 // Lastly, we also disallow certain property accesses on the safe global
166166- return (safeGlobal = withProxy(safeGlobal!));
137137+ return (safeGlobal = mask(safeGlobal!));
167138}
168139169140interface SafeFunction {