123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- /* global host, bridge, data, context */
- 'use strict';
- const {
- Object: localObject,
- Array: localArray,
- Error: LocalError,
- Reflect: localReflect,
- Proxy: LocalProxy,
- WeakMap: LocalWeakMap,
- Function: localFunction,
- Promise: localPromise,
- eval: localEval
- } = global;
- const {
- freeze: localObjectFreeze
- } = localObject;
- const {
- getPrototypeOf: localReflectGetPrototypeOf,
- apply: localReflectApply,
- deleteProperty: localReflectDeleteProperty,
- has: localReflectHas,
- defineProperty: localReflectDefineProperty,
- setPrototypeOf: localReflectSetPrototypeOf,
- getOwnPropertyDescriptor: localReflectGetOwnPropertyDescriptor
- } = localReflect;
- const {
- isArray: localArrayIsArray
- } = localArray;
- const {
- ensureThis,
- ReadOnlyHandler,
- from,
- fromWithFactory,
- readonlyFactory,
- connect,
- addProtoMapping,
- VMError,
- ReadOnlyMockHandler
- } = bridge;
- const {
- allowAsync,
- GeneratorFunction,
- AsyncFunction,
- AsyncGeneratorFunction
- } = data;
- const localWeakMapGet = LocalWeakMap.prototype.get;
- function localUnexpected() {
- return new VMError('Should not happen');
- }
- // global is originally prototype of host.Object so it can be used to climb up from the sandbox.
- if (!localReflectSetPrototypeOf(context, localObject.prototype)) throw localUnexpected();
- Object.defineProperties(global, {
- global: {value: global, writable: true, configurable: true, enumerable: true},
- globalThis: {value: global, writable: true, configurable: true},
- GLOBAL: {value: global, writable: true, configurable: true},
- root: {value: global, writable: true, configurable: true}
- });
- if (!localReflectDefineProperty(global, 'VMError', {
- __proto__: null,
- value: VMError,
- writable: true,
- enumerable: false,
- configurable: true
- })) throw localUnexpected();
- // Fixes buffer unsafe allocation
- /* eslint-disable no-use-before-define */
- class BufferHandler extends ReadOnlyHandler {
- apply(target, thiz, args) {
- if (args.length > 0 && typeof args[0] === 'number') {
- return LocalBuffer.alloc(args[0]);
- }
- return localReflectApply(LocalBuffer.from, LocalBuffer, args);
- }
- construct(target, args, newTarget) {
- if (args.length > 0 && typeof args[0] === 'number') {
- return LocalBuffer.alloc(args[0]);
- }
- return localReflectApply(LocalBuffer.from, LocalBuffer, args);
- }
- }
- /* eslint-enable no-use-before-define */
- const LocalBuffer = fromWithFactory(obj => new BufferHandler(obj), host.Buffer);
- if (!localReflectDefineProperty(global, 'Buffer', {
- __proto__: null,
- value: LocalBuffer,
- writable: true,
- enumerable: false,
- configurable: true
- })) throw localUnexpected();
- addProtoMapping(LocalBuffer.prototype, host.Buffer.prototype, 'Uint8Array');
- /**
- *
- * @param {*} size Size of new buffer
- * @this LocalBuffer
- * @return {LocalBuffer}
- */
- function allocUnsafe(size) {
- return LocalBuffer.alloc(size);
- }
- connect(allocUnsafe, host.Buffer.allocUnsafe);
- /**
- *
- * @param {*} size Size of new buffer
- * @this LocalBuffer
- * @return {LocalBuffer}
- */
- function allocUnsafeSlow(size) {
- return LocalBuffer.alloc(size);
- }
- connect(allocUnsafeSlow, host.Buffer.allocUnsafeSlow);
- /**
- * Replacement for Buffer inspect
- *
- * @param {*} recurseTimes
- * @param {*} ctx
- * @this LocalBuffer
- * @return {string}
- */
- function inspect(recurseTimes, ctx) {
- // Mimic old behavior, could throw but didn't pass a test.
- const max = host.INSPECT_MAX_BYTES;
- const actualMax = Math.min(max, this.length);
- const remaining = this.length - max;
- let str = this.hexSlice(0, actualMax).replace(/(.{2})/g, '$1 ').trim();
- if (remaining > 0) str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`;
- return `<${this.constructor.name} ${str}>`;
- }
- connect(inspect, host.Buffer.prototype.inspect);
- connect(localFunction.prototype.bind, host.Function.prototype.bind);
- connect(localObject.prototype.__defineGetter__, host.Object.prototype.__defineGetter__);
- connect(localObject.prototype.__defineSetter__, host.Object.prototype.__defineSetter__);
- connect(localObject.prototype.__lookupGetter__, host.Object.prototype.__lookupGetter__);
- connect(localObject.prototype.__lookupSetter__, host.Object.prototype.__lookupSetter__);
- /*
- * PrepareStackTrace sanitization
- */
- const oldPrepareStackTraceDesc = localReflectGetOwnPropertyDescriptor(LocalError, 'prepareStackTrace');
- let currentPrepareStackTrace = LocalError.prepareStackTrace;
- const wrappedPrepareStackTrace = new LocalWeakMap();
- if (typeof currentPrepareStackTrace === 'function') {
- wrappedPrepareStackTrace.set(currentPrepareStackTrace, currentPrepareStackTrace);
- }
- let OriginalCallSite;
- LocalError.prepareStackTrace = (e, sst) => {
- OriginalCallSite = sst[0].constructor;
- };
- new LocalError().stack;
- if (typeof OriginalCallSite === 'function') {
- LocalError.prepareStackTrace = undefined;
- function makeCallSiteGetters(list) {
- const callSiteGetters = [];
- for (let i=0; i<list.length; i++) {
- const name = list[i];
- const func = OriginalCallSite.prototype[name];
- callSiteGetters[i] = {__proto__: null,
- name,
- propName: '_' + name,
- func: (thiz) => {
- return localReflectApply(func, thiz, []);
- }
- };
- }
- return callSiteGetters;
- }
- function applyCallSiteGetters(thiz, callSite, getters) {
- for (let i=0; i<getters.length; i++) {
- const getter = getters[i];
- localReflectDefineProperty(thiz, getter.propName, {
- __proto__: null,
- value: getter.func(callSite)
- });
- }
- }
- const callSiteGetters = makeCallSiteGetters([
- 'getTypeName',
- 'getFunctionName',
- 'getMethodName',
- 'getFileName',
- 'getLineNumber',
- 'getColumnNumber',
- 'getEvalOrigin',
- 'isToplevel',
- 'isEval',
- 'isNative',
- 'isConstructor',
- 'isAsync',
- 'isPromiseAll',
- 'getPromiseIndex'
- ]);
- class CallSite {
- constructor(callSite) {
- applyCallSiteGetters(this, callSite, callSiteGetters);
- }
- getThis() {
- return undefined;
- }
- getFunction() {
- return undefined;
- }
- toString() {
- return 'CallSite {}';
- }
- }
- for (let i=0; i<callSiteGetters.length; i++) {
- const name = callSiteGetters[i].name;
- const funcProp = localReflectGetOwnPropertyDescriptor(OriginalCallSite.prototype, name);
- if (!funcProp) continue;
- const propertyName = callSiteGetters[i].propName;
- const func = {func() {
- return this[propertyName];
- }}.func;
- const nameProp = localReflectGetOwnPropertyDescriptor(func, 'name');
- if (!nameProp) throw localUnexpected();
- nameProp.value = name;
- if (!localReflectDefineProperty(func, 'name', nameProp)) throw localUnexpected();
- funcProp.value = func;
- if (!localReflectDefineProperty(CallSite.prototype, name, funcProp)) throw localUnexpected();
- }
- if (!localReflectDefineProperty(LocalError, 'prepareStackTrace', {
- configurable: false,
- enumerable: false,
- get() {
- return currentPrepareStackTrace;
- },
- set(value) {
- if (typeof(value) !== 'function') {
- currentPrepareStackTrace = value;
- return;
- }
- const wrapped = localReflectApply(localWeakMapGet, wrappedPrepareStackTrace, [value]);
- if (wrapped) {
- currentPrepareStackTrace = wrapped;
- return;
- }
- const newWrapped = (error, sst) => {
- if (localArrayIsArray(sst)) {
- for (let i=0; i < sst.length; i++) {
- const cs = sst[i];
- if (typeof cs === 'object' && localReflectGetPrototypeOf(cs) === OriginalCallSite.prototype) {
- sst[i] = new CallSite(cs);
- }
- }
- }
- return value(error, sst);
- };
- wrappedPrepareStackTrace.set(value, newWrapped);
- wrappedPrepareStackTrace.set(newWrapped, newWrapped);
- currentPrepareStackTrace = newWrapped;
- }
- })) throw localUnexpected();
- } else if (oldPrepareStackTraceDesc) {
- localReflectDefineProperty(LocalError, 'prepareStackTrace', oldPrepareStackTraceDesc);
- } else {
- localReflectDeleteProperty(LocalError, 'prepareStackTrace');
- }
- /*
- * Exception sanitization
- */
- const withProxy = localObjectFreeze({
- __proto__: null,
- has(target, key) {
- if (key === host.INTERNAL_STATE_NAME) return false;
- return localReflectHas(target, key);
- }
- });
- const interanState = localObjectFreeze({
- __proto__: null,
- wrapWith(x) {
- if (x === null || x === undefined) return x;
- return new LocalProxy(localObject(x), withProxy);
- },
- handleException: ensureThis,
- import(what) {
- throw new VMError('Dynamic Import not supported');
- }
- });
- if (!localReflectDefineProperty(global, host.INTERNAL_STATE_NAME, {
- __proto__: null,
- configurable: false,
- enumerable: false,
- writable: false,
- value: interanState
- })) throw localUnexpected();
- /*
- * Eval sanitization
- */
- function throwAsync() {
- return new VMError('Async not available');
- }
- function makeFunction(inputArgs, isAsync, isGenerator) {
- const lastArgs = inputArgs.length - 1;
- let code = lastArgs >= 0 ? `${inputArgs[lastArgs]}` : '';
- let args = lastArgs > 0 ? `${inputArgs[0]}` : '';
- for (let i = 1; i < lastArgs; i++) {
- args += `,${inputArgs[i]}`;
- }
- try {
- code = host.transformAndCheck(args, code, isAsync, isGenerator, allowAsync);
- } catch (e) {
- throw bridge.from(e);
- }
- return localEval(code);
- }
- const FunctionHandler = {
- __proto__: null,
- apply(target, thiz, args) {
- return makeFunction(args, this.isAsync, this.isGenerator);
- },
- construct(target, args, newTarget) {
- return makeFunction(args, this.isAsync, this.isGenerator);
- }
- };
- const EvalHandler = {
- __proto__: null,
- apply(target, thiz, args) {
- if (args.length === 0) return undefined;
- let code = `${args[0]}`;
- try {
- code = host.transformAndCheck(null, code, false, false, allowAsync);
- } catch (e) {
- throw bridge.from(e);
- }
- return localEval(code);
- }
- };
- const AsyncErrorHandler = {
- __proto__: null,
- apply(target, thiz, args) {
- throw throwAsync();
- },
- construct(target, args, newTarget) {
- throw throwAsync();
- }
- };
- function makeCheckFunction(isAsync, isGenerator) {
- if (isAsync && !allowAsync) return AsyncErrorHandler;
- return {
- __proto__: FunctionHandler,
- isAsync,
- isGenerator
- };
- }
- function overrideWithProxy(obj, prop, value, handler) {
- const proxy = new LocalProxy(value, handler);
- if (!localReflectDefineProperty(obj, prop, {__proto__: null, value: proxy})) throw localUnexpected();
- return proxy;
- }
- const proxiedFunction = overrideWithProxy(localFunction.prototype, 'constructor', localFunction, makeCheckFunction(false, false));
- if (GeneratorFunction) {
- if (!localReflectSetPrototypeOf(GeneratorFunction, proxiedFunction)) throw localUnexpected();
- overrideWithProxy(GeneratorFunction.prototype, 'constructor', GeneratorFunction, makeCheckFunction(false, true));
- }
- if (AsyncFunction) {
- if (!localReflectSetPrototypeOf(AsyncFunction, proxiedFunction)) throw localUnexpected();
- overrideWithProxy(AsyncFunction.prototype, 'constructor', AsyncFunction, makeCheckFunction(true, false));
- }
- if (AsyncGeneratorFunction) {
- if (!localReflectSetPrototypeOf(AsyncGeneratorFunction, proxiedFunction)) throw localUnexpected();
- overrideWithProxy(AsyncGeneratorFunction.prototype, 'constructor', AsyncGeneratorFunction, makeCheckFunction(true, true));
- }
- global.Function = proxiedFunction;
- global.eval = new LocalProxy(localEval, EvalHandler);
- /*
- * Promise sanitization
- */
- if (localPromise && !allowAsync) {
- const PromisePrototype = localPromise.prototype;
- overrideWithProxy(PromisePrototype, 'then', PromisePrototype.then, AsyncErrorHandler);
- // This seems not to work, and will produce
- // UnhandledPromiseRejectionWarning: TypeError: Method Promise.prototype.then called on incompatible receiver [object Object].
- // This is likely caused since the host.Promise.prototype.then cannot use the VM Proxy object.
- // Contextify.connect(host.Promise.prototype.then, Promise.prototype.then);
- if (PromisePrototype.finally) {
- overrideWithProxy(PromisePrototype, 'finally', PromisePrototype.finally, AsyncErrorHandler);
- // Contextify.connect(host.Promise.prototype.finally, Promise.prototype.finally);
- }
- if (Promise.prototype.catch) {
- overrideWithProxy(PromisePrototype, 'catch', PromisePrototype.catch, AsyncErrorHandler);
- // Contextify.connect(host.Promise.prototype.catch, Promise.prototype.catch);
- }
- }
- function readonly(other, mock) {
- // Note: other@other(unsafe) mock@other(unsafe) returns@this(unsafe) throws@this(unsafe)
- if (!mock) return fromWithFactory(readonlyFactory, other);
- const tmock = from(mock);
- return fromWithFactory(obj=>new ReadOnlyMockHandler(obj, tmock), other);
- }
- return {
- __proto__: null,
- readonly,
- global
- };
|