setup-sandbox.js 12 KB

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