setup-sandbox.js 12 KB

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