Mirror: A maybe slightly safer-ish wrapper around eval Function constructors
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Remove Proxy from implementation

+25 -54
+1 -1
README.md
··· 25 25 26 26 It only does three simple things: 27 27 - Isolate the [global object](https://developer.mozilla.org/en-US/docs/Glossary/Global_object) and uses a separate object using a `with` statement 28 - - Wraps all passed through globals, like `Array`, in a recursive proxy that disallows access to prototype-polluting propeties, such as `constructor` 28 + - Wraps all passed through globals, like `Array`, in a recursive masking object that disallows access to prototype-polluting propeties, such as `constructor` 29 29 - In the browser: Creates an `iframe` element and uses that frame's globals instead 30 30 31 31 If 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
··· 1 - // These are marked with `Symbol.unscopables` for the Proxy 2 - const unscopables = { 3 - __proto__: true, 4 - prototype: true, 5 - constructor: true, 6 - }; 7 - 8 1 // Keys that'll always not be included (for Node.js) 9 2 const ignore = { 10 3 sys: true, ··· 30 23 function safeKey(target: Object, key: string | symbol): string | undefined { 31 24 return key !== 'constructor' && 32 25 key !== '__proto__' && 33 - key !== 'constructor' && 26 + key !== 'prototype' && 34 27 typeof key !== 'symbol' && 35 28 key in target 36 29 ? key 37 30 : undefined; 38 31 } 39 32 40 - // Wrap any given target with a Proxy preventing access to unscopables 41 - function withProxy(target: any) { 33 + // Wrap any given target with a masking object preventing access to prototype properties 34 + function mask(target: any) { 42 35 if ( 43 36 target == null || 44 37 (typeof target !== 'function' && typeof target !== 'object') 45 38 ) { 46 39 // If the target isn't a function or object then skip 47 40 return target; 48 - } else if ( 49 - typeof Proxy === 'function' && 50 - typeof Symbol === 'function' && 51 - Symbol.unscopables 52 - ) { 53 - // Mark hidden keys as unscopable 54 - target[Symbol.unscopables] = unscopables; 55 - // Wrap the target in a Proxy that disallows access to some keys 56 - return new Proxy(target, { 57 - // Return a value, if it's allowed to be returned, and wrap that value in a proxy recursively 58 - get(target, _key) { 59 - const key = safeKey(target, _key); 60 - return key !== undefined ? withProxy(target[key]) : undefined; 61 - }, 62 - has(target, key) { 63 - return !!safeKey(target, key); 64 - }, 65 - set: noop, 66 - deleteProperty: noop, 67 - defineProperty: noop, 68 - getOwnPropertyDescriptor: noop, 69 - }); 70 41 } 71 - 72 42 // Create a stand-in object or function 73 43 const standin = 74 44 typeof target === 'function' 75 - ? function (this: any) { 45 + ? (function (this: any) { 76 46 return target.apply(this, arguments); 77 - } 47 + }) 78 48 : Object.create(null); 79 49 // Copy all known keys over to the stand-in and recursively apply `withProxy` 80 50 // Prevent unsafe keys from being accessed 81 - const keys = ['constructor', 'prototype', '__proto__'].concat( 82 - Object.getOwnPropertyNames(target) 83 - ); 51 + const keys = Object.getOwnPropertyNames(target) 84 52 for (let i = 0; i < keys.length; i++) { 85 53 const key = keys[i]; 86 - Object.defineProperty(standin, key, { 87 - enumerable: true, 88 - get: safeKey(target, key) 89 - ? () => { 90 - return typeof target[key] === 'function' || 91 - typeof target[key] === 'object' 92 - ? withProxy(target[key]) 93 - : target[key]; 94 - } 95 - : noop, 96 - }); 54 + if (key !== 'prototype') { 55 + Object.defineProperty(standin, key, { 56 + enumerable: true, 57 + get: safeKey(target, key) 58 + ? () => { 59 + return typeof target[key] === 'function' || 60 + typeof target[key] === 'object' 61 + ? mask(target[key]) 62 + : target[key]; 63 + } 64 + : noop, 65 + }); 66 + } 97 67 } 98 - 99 - return standin; 68 + return typeof Object.freeze === 'function' 69 + ? Object.freeze(standin) 70 + : standin; 100 71 } 101 72 102 73 let safeGlobal: Record<string | symbol, unknown> | void; ··· 153 124 // certain key accesses 154 125 for (let i = 0, l = trueGlobalKeys.length; i < l; i++) { 155 126 const key = trueGlobalKeys[i]; 156 - safeGlobal[key] = withProxy(vmGlobals[key]); 127 + safeGlobal[key] = mask(vmGlobals[key]); 157 128 } 158 129 159 130 // We then reset all globals that are present on `globalThis` directly ··· 163 134 // It _might_ be safe to expose the Function constructor like this... who knows 164 135 safeGlobal!.Function = SafeFunction; 165 136 // Lastly, we also disallow certain property accesses on the safe global 166 - return (safeGlobal = withProxy(safeGlobal!)); 137 + return (safeGlobal = mask(safeGlobal!)); 167 138 } 168 139 169 140 interface SafeFunction {