setup-sandbox.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. /* global host, bridge, data, context */
  2. 'use strict';
  3. const {
  4. Object: localObject,
  5. Array: localArray,
  6. Error: LocalError,
  7. Reflect: localReflect,
  8. Proxy: LocalProxy,
  9. WeakMap: LocalWeakMap,
  10. Function: localFunction,
  11. Promise: localPromise,
  12. eval: localEval
  13. } = global;
  14. const {
  15. freeze: localObjectFreeze
  16. } = localObject;
  17. const {
  18. getPrototypeOf: localReflectGetPrototypeOf,
  19. apply: localReflectApply,
  20. deleteProperty: localReflectDeleteProperty,
  21. has: localReflectHas,
  22. defineProperty: localReflectDefineProperty,
  23. setPrototypeOf: localReflectSetPrototypeOf,
  24. getOwnPropertyDescriptor: localReflectGetOwnPropertyDescriptor
  25. } = localReflect;
  26. const {
  27. isArray: localArrayIsArray
  28. } = localArray;
  29. const {
  30. ensureThis,
  31. ReadOnlyHandler,
  32. from,
  33. fromWithFactory,
  34. readonlyFactory,
  35. connect,
  36. addProtoMapping,
  37. VMError,
  38. ReadOnlyMockHandler
  39. } = bridge;
  40. const {
  41. allowAsync,
  42. GeneratorFunction,
  43. AsyncFunction,
  44. AsyncGeneratorFunction
  45. } = data;
  46. const localWeakMapGet = LocalWeakMap.prototype.get;
  47. function localUnexpected() {
  48. return new VMError('Should not happen');
  49. }
  50. // global is originally prototype of host.Object so it can be used to climb up from the sandbox.
  51. if (!localReflectSetPrototypeOf(context, localObject.prototype)) throw localUnexpected();
  52. Object.defineProperties(global, {
  53. global: {value: global, writable: true, configurable: true, enumerable: true},
  54. globalThis: {value: global, writable: true, configurable: true},
  55. GLOBAL: {value: global, writable: true, configurable: true},
  56. root: {value: global, writable: true, configurable: true}
  57. });
  58. if (!localReflectDefineProperty(global, 'VMError', {
  59. __proto__: null,
  60. value: VMError,
  61. writable: true,
  62. enumerable: false,
  63. configurable: true
  64. })) throw localUnexpected();
  65. // Fixes buffer unsafe allocation
  66. /* eslint-disable no-use-before-define */
  67. class BufferHandler extends ReadOnlyHandler {
  68. apply(target, thiz, args) {
  69. if (args.length > 0 && typeof args[0] === 'number') {
  70. return LocalBuffer.alloc(args[0]);
  71. }
  72. return localReflectApply(LocalBuffer.from, LocalBuffer, args);
  73. }
  74. construct(target, args, newTarget) {
  75. if (args.length > 0 && typeof args[0] === 'number') {
  76. return LocalBuffer.alloc(args[0]);
  77. }
  78. return localReflectApply(LocalBuffer.from, LocalBuffer, args);
  79. }
  80. }
  81. /* eslint-enable no-use-before-define */
  82. const LocalBuffer = fromWithFactory(obj => new BufferHandler(obj), host.Buffer);
  83. if (!localReflectDefineProperty(global, 'Buffer', {
  84. __proto__: null,
  85. value: LocalBuffer,
  86. writable: true,
  87. enumerable: false,
  88. configurable: true
  89. })) throw localUnexpected();
  90. addProtoMapping(LocalBuffer.prototype, host.Buffer.prototype, 'Uint8Array');
  91. /**
  92. *
  93. * @param {*} size Size of new buffer
  94. * @this LocalBuffer
  95. * @return {LocalBuffer}
  96. */
  97. function allocUnsafe(size) {
  98. return LocalBuffer.alloc(size);
  99. }
  100. connect(allocUnsafe, host.Buffer.allocUnsafe);
  101. /**
  102. *
  103. * @param {*} size Size of new buffer
  104. * @this LocalBuffer
  105. * @return {LocalBuffer}
  106. */
  107. function allocUnsafeSlow(size) {
  108. return LocalBuffer.alloc(size);
  109. }
  110. connect(allocUnsafeSlow, host.Buffer.allocUnsafeSlow);
  111. /**
  112. * Replacement for Buffer inspect
  113. *
  114. * @param {*} recurseTimes
  115. * @param {*} ctx
  116. * @this LocalBuffer
  117. * @return {string}
  118. */
  119. function inspect(recurseTimes, ctx) {
  120. // Mimic old behavior, could throw but didn't pass a test.
  121. const max = host.INSPECT_MAX_BYTES;
  122. const actualMax = Math.min(max, this.length);
  123. const remaining = this.length - max;
  124. let str = this.hexSlice(0, actualMax).replace(/(.{2})/g, '$1 ').trim();
  125. if (remaining > 0) str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`;
  126. return `<${this.constructor.name} ${str}>`;
  127. }
  128. connect(inspect, host.Buffer.prototype.inspect);
  129. connect(localFunction.prototype.bind, host.Function.prototype.bind);
  130. connect(localObject.prototype.__defineGetter__, host.Object.prototype.__defineGetter__);
  131. connect(localObject.prototype.__defineSetter__, host.Object.prototype.__defineSetter__);
  132. connect(localObject.prototype.__lookupGetter__, host.Object.prototype.__lookupGetter__);
  133. connect(localObject.prototype.__lookupSetter__, host.Object.prototype.__lookupSetter__);
  134. /*
  135. * PrepareStackTrace sanitization
  136. */
  137. const oldPrepareStackTraceDesc = localReflectGetOwnPropertyDescriptor(LocalError, 'prepareStackTrace');
  138. let currentPrepareStackTrace = LocalError.prepareStackTrace;
  139. const wrappedPrepareStackTrace = new LocalWeakMap();
  140. if (typeof currentPrepareStackTrace === 'function') {
  141. wrappedPrepareStackTrace.set(currentPrepareStackTrace, currentPrepareStackTrace);
  142. }
  143. let OriginalCallSite;
  144. LocalError.prepareStackTrace = (e, sst) => {
  145. OriginalCallSite = sst[0].constructor;
  146. };
  147. new LocalError().stack;
  148. if (typeof OriginalCallSite === 'function') {
  149. LocalError.prepareStackTrace = undefined;
  150. function makeCallSiteGetters(list) {
  151. const callSiteGetters = [];
  152. for (let i=0; i<list.length; i++) {
  153. const name = list[i];
  154. const func = OriginalCallSite.prototype[name];
  155. callSiteGetters[i] = {__proto__: null,
  156. name,
  157. propName: '_' + name,
  158. func: (thiz) => {
  159. return localReflectApply(func, thiz, []);
  160. }
  161. };
  162. }
  163. return callSiteGetters;
  164. }
  165. function applyCallSiteGetters(thiz, callSite, getters) {
  166. for (let i=0; i<getters.length; i++) {
  167. const getter = getters[i];
  168. localReflectDefineProperty(thiz, getter.propName, {
  169. __proto__: null,
  170. value: getter.func(callSite)
  171. });
  172. }
  173. }
  174. const callSiteGetters = makeCallSiteGetters([
  175. 'getTypeName',
  176. 'getFunctionName',
  177. 'getMethodName',
  178. 'getFileName',
  179. 'getLineNumber',
  180. 'getColumnNumber',
  181. 'getEvalOrigin',
  182. 'isToplevel',
  183. 'isEval',
  184. 'isNative',
  185. 'isConstructor',
  186. 'isAsync',
  187. 'isPromiseAll',
  188. 'getPromiseIndex'
  189. ]);
  190. class CallSite {
  191. constructor(callSite) {
  192. applyCallSiteGetters(this, callSite, callSiteGetters);
  193. }
  194. getThis() {
  195. return undefined;
  196. }
  197. getFunction() {
  198. return undefined;
  199. }
  200. toString() {
  201. return 'CallSite {}';
  202. }
  203. }
  204. for (let i=0; i<callSiteGetters.length; i++) {
  205. const name = callSiteGetters[i].name;
  206. const funcProp = localReflectGetOwnPropertyDescriptor(OriginalCallSite.prototype, name);
  207. if (!funcProp) continue;
  208. const propertyName = callSiteGetters[i].propName;
  209. const func = {func() {
  210. return this[propertyName];
  211. }}.func;
  212. const nameProp = localReflectGetOwnPropertyDescriptor(func, 'name');
  213. if (!nameProp) throw localUnexpected();
  214. nameProp.value = name;
  215. if (!localReflectDefineProperty(func, 'name', nameProp)) throw localUnexpected();
  216. funcProp.value = func;
  217. if (!localReflectDefineProperty(CallSite.prototype, name, funcProp)) throw localUnexpected();
  218. }
  219. if (!localReflectDefineProperty(LocalError, 'prepareStackTrace', {
  220. configurable: false,
  221. enumerable: false,
  222. get() {
  223. return currentPrepareStackTrace;
  224. },
  225. set(value) {
  226. if (typeof(value) !== 'function') {
  227. currentPrepareStackTrace = value;
  228. return;
  229. }
  230. const wrapped = localReflectApply(localWeakMapGet, wrappedPrepareStackTrace, [value]);
  231. if (wrapped) {
  232. currentPrepareStackTrace = wrapped;
  233. return;
  234. }
  235. const newWrapped = (error, sst) => {
  236. if (localArrayIsArray(sst)) {
  237. for (let i=0; i < sst.length; i++) {
  238. const cs = sst[i];
  239. if (typeof cs === 'object' && localReflectGetPrototypeOf(cs) === OriginalCallSite.prototype) {
  240. sst[i] = new CallSite(cs);
  241. }
  242. }
  243. }
  244. return value(error, sst);
  245. };
  246. wrappedPrepareStackTrace.set(value, newWrapped);
  247. wrappedPrepareStackTrace.set(newWrapped, newWrapped);
  248. currentPrepareStackTrace = newWrapped;
  249. }
  250. })) throw localUnexpected();
  251. } else if (oldPrepareStackTraceDesc) {
  252. localReflectDefineProperty(LocalError, 'prepareStackTrace', oldPrepareStackTraceDesc);
  253. } else {
  254. localReflectDeleteProperty(LocalError, 'prepareStackTrace');
  255. }
  256. /*
  257. * Exception sanitization
  258. */
  259. const withProxy = localObjectFreeze({
  260. __proto__: null,
  261. has(target, key) {
  262. if (key === host.INTERNAL_STATE_NAME) return false;
  263. return localReflectHas(target, key);
  264. }
  265. });
  266. const interanState = localObjectFreeze({
  267. __proto__: null,
  268. wrapWith(x) {
  269. if (x === null || x === undefined) return x;
  270. return new LocalProxy(localObject(x), withProxy);
  271. },
  272. handleException: ensureThis,
  273. import(what) {
  274. throw new VMError('Dynamic Import not supported');
  275. }
  276. });
  277. if (!localReflectDefineProperty(global, host.INTERNAL_STATE_NAME, {
  278. __proto__: null,
  279. configurable: false,
  280. enumerable: false,
  281. writable: false,
  282. value: interanState
  283. })) throw localUnexpected();
  284. /*
  285. * Eval sanitization
  286. */
  287. function throwAsync() {
  288. return new VMError('Async not available');
  289. }
  290. function makeFunction(inputArgs, isAsync, isGenerator) {
  291. const lastArgs = inputArgs.length - 1;
  292. let code = lastArgs >= 0 ? `${inputArgs[lastArgs]}` : '';
  293. let args = lastArgs > 0 ? `${inputArgs[0]}` : '';
  294. for (let i = 1; i < lastArgs; i++) {
  295. args += `,${inputArgs[i]}`;
  296. }
  297. try {
  298. code = host.transformAndCheck(args, code, isAsync, isGenerator, allowAsync);
  299. } catch (e) {
  300. throw bridge.from(e);
  301. }
  302. return localEval(code);
  303. }
  304. const FunctionHandler = {
  305. __proto__: null,
  306. apply(target, thiz, args) {
  307. return makeFunction(args, this.isAsync, this.isGenerator);
  308. },
  309. construct(target, args, newTarget) {
  310. return makeFunction(args, this.isAsync, this.isGenerator);
  311. }
  312. };
  313. const EvalHandler = {
  314. __proto__: null,
  315. apply(target, thiz, args) {
  316. if (args.length === 0) return undefined;
  317. let code = `${args[0]}`;
  318. try {
  319. code = host.transformAndCheck(null, code, false, false, allowAsync);
  320. } catch (e) {
  321. throw bridge.from(e);
  322. }
  323. return localEval(code);
  324. }
  325. };
  326. const AsyncErrorHandler = {
  327. __proto__: null,
  328. apply(target, thiz, args) {
  329. throw throwAsync();
  330. },
  331. construct(target, args, newTarget) {
  332. throw throwAsync();
  333. }
  334. };
  335. function makeCheckFunction(isAsync, isGenerator) {
  336. if (isAsync && !allowAsync) return AsyncErrorHandler;
  337. return {
  338. __proto__: FunctionHandler,
  339. isAsync,
  340. isGenerator
  341. };
  342. }
  343. function overrideWithProxy(obj, prop, value, handler) {
  344. const proxy = new LocalProxy(value, handler);
  345. if (!localReflectDefineProperty(obj, prop, {__proto__: null, value: proxy})) throw localUnexpected();
  346. return proxy;
  347. }
  348. const proxiedFunction = overrideWithProxy(localFunction.prototype, 'constructor', localFunction, makeCheckFunction(false, false));
  349. if (GeneratorFunction) {
  350. if (!localReflectSetPrototypeOf(GeneratorFunction, proxiedFunction)) throw localUnexpected();
  351. overrideWithProxy(GeneratorFunction.prototype, 'constructor', GeneratorFunction, makeCheckFunction(false, true));
  352. }
  353. if (AsyncFunction) {
  354. if (!localReflectSetPrototypeOf(AsyncFunction, proxiedFunction)) throw localUnexpected();
  355. overrideWithProxy(AsyncFunction.prototype, 'constructor', AsyncFunction, makeCheckFunction(true, false));
  356. }
  357. if (AsyncGeneratorFunction) {
  358. if (!localReflectSetPrototypeOf(AsyncGeneratorFunction, proxiedFunction)) throw localUnexpected();
  359. overrideWithProxy(AsyncGeneratorFunction.prototype, 'constructor', AsyncGeneratorFunction, makeCheckFunction(true, true));
  360. }
  361. global.Function = proxiedFunction;
  362. global.eval = new LocalProxy(localEval, EvalHandler);
  363. /*
  364. * Promise sanitization
  365. */
  366. if (localPromise && !allowAsync) {
  367. const PromisePrototype = localPromise.prototype;
  368. overrideWithProxy(PromisePrototype, 'then', PromisePrototype.then, AsyncErrorHandler);
  369. // This seems not to work, and will produce
  370. // UnhandledPromiseRejectionWarning: TypeError: Method Promise.prototype.then called on incompatible receiver [object Object].
  371. // This is likely caused since the host.Promise.prototype.then cannot use the VM Proxy object.
  372. // Contextify.connect(host.Promise.prototype.then, Promise.prototype.then);
  373. if (PromisePrototype.finally) {
  374. overrideWithProxy(PromisePrototype, 'finally', PromisePrototype.finally, AsyncErrorHandler);
  375. // Contextify.connect(host.Promise.prototype.finally, Promise.prototype.finally);
  376. }
  377. if (Promise.prototype.catch) {
  378. overrideWithProxy(PromisePrototype, 'catch', PromisePrototype.catch, AsyncErrorHandler);
  379. // Contextify.connect(host.Promise.prototype.catch, Promise.prototype.catch);
  380. }
  381. }
  382. function readonly(other, mock) {
  383. // Note: other@other(unsafe) mock@other(unsafe) returns@this(unsafe) throws@this(unsafe)
  384. if (!mock) return fromWithFactory(readonlyFactory, other);
  385. const tmock = from(mock);
  386. return fromWithFactory(obj=>new ReadOnlyMockHandler(obj, tmock), other);
  387. }
  388. return {
  389. __proto__: null,
  390. readonly,
  391. global
  392. };