mirror of
https://github.com/xuxiaobo-bobo/boda_jsEnv.git
synced 2025-04-22 23:44:23 +08:00
1045 lines
34 KiB
JavaScript
1045 lines
34 KiB
JavaScript
/* global host */
|
|
/* eslint-disable block-spacing, no-multi-spaces, brace-style, no-array-constructor, new-cap, no-use-before-define */
|
|
// debugger
|
|
'use strict';
|
|
|
|
// eslint-disable-next-line no-invalid-this, no-shadow
|
|
const global = this;
|
|
// global.async = function (fn) {
|
|
// return fn();
|
|
// };
|
|
|
|
// global.await = function (promise) {
|
|
// return promise;
|
|
// };
|
|
// debugger
|
|
const local = host.Object.create(null);
|
|
local.Object = Object;
|
|
local.Array = Array;
|
|
local.Reflect = host.Object.create(null);
|
|
local.Reflect.ownKeys = Reflect.ownKeys;
|
|
local.Reflect.enumerate = Reflect.enumerate;
|
|
local.Reflect.getPrototypeOf = Reflect.getPrototypeOf;
|
|
local.Reflect.construct = Reflect.construct;
|
|
local.Reflect.apply = Reflect.apply;
|
|
local.Reflect.set = Reflect.set;
|
|
local.Reflect.deleteProperty = Reflect.deleteProperty;
|
|
local.Reflect.has = Reflect.has;
|
|
local.Reflect.defineProperty = Reflect.defineProperty;
|
|
local.Reflect.setPrototypeOf = Reflect.setPrototypeOf;
|
|
local.Reflect.isExtensible = Reflect.isExtensible;
|
|
local.Reflect.preventExtensions = Reflect.preventExtensions;
|
|
local.Reflect.getOwnPropertyDescriptor = Reflect.getOwnPropertyDescriptor;
|
|
// local.Reflect.async = function (fn) {
|
|
// return fn();
|
|
// };
|
|
|
|
// local.Reflect.await = function (promise) {
|
|
// return promise;
|
|
// };
|
|
// global is originally prototype of host.Object so it can be used to climb up from the sandbox.
|
|
Object.setPrototypeOf(global, Object.prototype);
|
|
/*
|
|
Object.defineProperties(global, {
|
|
global: {value: global},
|
|
GLOBAL: {value: global},
|
|
root: {value: global},
|
|
isVM: {value: true}
|
|
});
|
|
*/
|
|
const DEBUG = false;
|
|
const OPNA = 'Operation not allowed on contextified object.';
|
|
const captureStackTrace = Error.captureStackTrace;
|
|
// debugger
|
|
const FROZEN_TRAPS = host.Object.create(null);
|
|
FROZEN_TRAPS.set = (target, key) => false;
|
|
FROZEN_TRAPS.setPrototypeOf = (target, key) => false;
|
|
FROZEN_TRAPS.defineProperty = (target, key) => false;
|
|
FROZEN_TRAPS.deleteProperty = (target, key) => false;
|
|
FROZEN_TRAPS.isExtensible = (target, key) => false;
|
|
FROZEN_TRAPS.preventExtensions = (target) => false;
|
|
|
|
// Map of contextified objects to original objects
|
|
const Contextified = new host.WeakMap();
|
|
const Decontextified = new host.WeakMap();
|
|
|
|
// We can't use host's hasInstance method
|
|
const hasInstance = local.Object[Symbol.hasInstance];
|
|
function instanceOf(value, construct) {
|
|
try {
|
|
return host.Reflect.apply(hasInstance, construct, [value]);
|
|
} catch (ex) {
|
|
// Never pass the handled exception through!
|
|
throw new VMError('Unable to perform instanceOf check.');
|
|
// This exception actually never get to the user. It only instructs the caller to return null because we wasn't able to perform instanceOf check.
|
|
}
|
|
}
|
|
|
|
const SHARED_OBJECT = { __proto__: null };
|
|
|
|
function createBaseObject(obj) {
|
|
let base;
|
|
if (typeof obj === 'function') {
|
|
try {
|
|
// eslint-disable-next-line no-new
|
|
new new host.Proxy(obj, {
|
|
__proto__: null,
|
|
construct() {
|
|
return this;
|
|
}
|
|
})();
|
|
// eslint-disable-next-line func-names
|
|
base = function () { };
|
|
base.prototype = null;
|
|
} catch (e) {
|
|
base = () => { };
|
|
}
|
|
} else if (host.Array.isArray(obj)) {
|
|
base = [];
|
|
} else {
|
|
return { __proto__: null };
|
|
}
|
|
if (!local.Reflect.setPrototypeOf(base, null)) {
|
|
// Should not happen
|
|
return null;
|
|
}
|
|
return base;
|
|
}
|
|
|
|
/**
|
|
* VMError definition.
|
|
*/
|
|
|
|
class VMError extends Error {
|
|
constructor(message, code) {
|
|
super(message);
|
|
debugger
|
|
this.name = 'VMError';
|
|
this.code = code;
|
|
|
|
captureStackTrace(this, this.constructor);
|
|
}
|
|
}
|
|
|
|
global.VMError = VMError;
|
|
/*
|
|
* This function will throw a TypeError for accessing properties
|
|
* on a strict mode function
|
|
*/
|
|
function throwCallerCalleeArgumentsAccess(key) {
|
|
'use strict';
|
|
throwCallerCalleeArgumentsAccess[key];
|
|
return new VMError('Unreachable');
|
|
}
|
|
|
|
function unexpected() {
|
|
throw new VMError('Should not happen');
|
|
}
|
|
|
|
function doPreventExtensions(target, object, doProxy) {
|
|
const keys = local.Reflect.ownKeys(object);
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const key = keys[i];
|
|
let desc = local.Reflect.getOwnPropertyDescriptor(object, key);
|
|
if (!desc) continue;
|
|
if (!local.Reflect.setPrototypeOf(desc, null)) unexpected();
|
|
if (!desc.configurable) {
|
|
const current = local.Reflect.getOwnPropertyDescriptor(target, key);
|
|
if (current && !current.configurable) continue;
|
|
if (desc.get || desc.set) {
|
|
desc.get = doProxy(desc.get);
|
|
desc.set = doProxy(desc.set);
|
|
} else {
|
|
desc.value = doProxy(desc.value);
|
|
}
|
|
} else {
|
|
if (desc.get || desc.set) {
|
|
desc = {
|
|
__proto__: null,
|
|
configurable: true,
|
|
enumerable: desc.enumerable,
|
|
writable: true,
|
|
value: null
|
|
};
|
|
} else {
|
|
desc.value = null;
|
|
}
|
|
}
|
|
if (!local.Reflect.defineProperty(target, key, desc)) unexpected();
|
|
}
|
|
if (!local.Reflect.preventExtensions(target)) unexpected();
|
|
}
|
|
|
|
/**
|
|
* Decontextify.
|
|
*/
|
|
|
|
const Decontextify = host.Object.create(null);
|
|
Decontextify.proxies = new host.WeakMap();
|
|
|
|
Decontextify.arguments = args => {
|
|
if (!host.Array.isArray(args)) return new host.Array();
|
|
|
|
try {
|
|
const arr = new host.Array();
|
|
for (let i = 0, l = args.length; i < l; i++) arr[i] = Decontextify.value(args[i]);
|
|
return arr;
|
|
} catch (e) {
|
|
// Never pass the handled exception through!
|
|
return new host.Array();
|
|
}
|
|
};
|
|
Decontextify.instance = (instance, klass, deepTraps, flags, toStringTag) => {
|
|
if (typeof instance === 'function') return Decontextify.function(instance);
|
|
|
|
// We must not use normal object because there's a chance object already contains malicious code in the prototype
|
|
const base = host.Object.create(null);
|
|
|
|
base.get = (target, key, receiver) => {
|
|
try {
|
|
if (key === 'vmProxyTarget' && DEBUG) return instance;
|
|
if (key === 'isVMProxy') return true;
|
|
if (key === 'constructor') return klass;
|
|
if (key === '__proto__') return klass.prototype;
|
|
} catch (e) {
|
|
// Never pass the handled exception through! This block can't throw an exception under normal conditions.
|
|
return null;
|
|
}
|
|
|
|
if (key === '__defineGetter__') return host.Object.prototype.__defineGetter__;
|
|
if (key === '__defineSetter__') return host.Object.prototype.__defineSetter__;
|
|
if (key === '__lookupGetter__') return host.Object.prototype.__lookupGetter__;
|
|
if (key === '__lookupSetter__') return host.Object.prototype.__lookupSetter__;
|
|
if (key === host.Symbol.toStringTag && toStringTag) return toStringTag;
|
|
|
|
try {
|
|
return Decontextify.value(instance[key], null, deepTraps, flags);
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
base.getPrototypeOf = (target) => {
|
|
return klass && klass.prototype;
|
|
};
|
|
|
|
return Decontextify.object(instance, base, deepTraps, flags);
|
|
};
|
|
Decontextify.function = (fnc, traps, deepTraps, flags, mock) => {
|
|
// We must not use normal object because there's a chance object already contains malicious code in the prototype
|
|
const base = host.Object.create(null);
|
|
// eslint-disable-next-line prefer-const
|
|
let proxy;
|
|
|
|
base.apply = (target, context, args) => {
|
|
context = Contextify.value(context);
|
|
|
|
// Set context of all arguments to vm's context.
|
|
args = Contextify.arguments(args);
|
|
|
|
try {
|
|
return Decontextify.value(fnc.apply(context, args));
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
base.construct = (target, args, newTarget) => {
|
|
args = Contextify.arguments(args);
|
|
|
|
try {
|
|
return Decontextify.instance(new fnc(...args), proxy, deepTraps, flags);
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
base.get = (target, key, receiver) => {
|
|
try {
|
|
if (key === 'vmProxyTarget' && DEBUG) return fnc;
|
|
if (key === 'isVMProxy') return true;
|
|
if (mock && host.Object.prototype.hasOwnProperty.call(mock, key)) return mock[key];
|
|
if (key === 'constructor') return host.Function;
|
|
if (key === '__proto__') return host.Function.prototype;
|
|
} catch (e) {
|
|
// Never pass the handled exception through! This block can't throw an exception under normal conditions.
|
|
return null;
|
|
}
|
|
|
|
if (key === '__defineGetter__') return host.Object.prototype.__defineGetter__;
|
|
if (key === '__defineSetter__') return host.Object.prototype.__defineSetter__;
|
|
if (key === '__lookupGetter__') return host.Object.prototype.__lookupGetter__;
|
|
if (key === '__lookupSetter__') return host.Object.prototype.__lookupSetter__;
|
|
|
|
try {
|
|
return Decontextify.value(fnc[key], null, deepTraps, flags);
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
base.getPrototypeOf = (target) => {
|
|
return host.Function.prototype;
|
|
};
|
|
|
|
proxy = Decontextify.object(fnc, host.Object.assign(base, traps), deepTraps);
|
|
return proxy;
|
|
};
|
|
Decontextify.object = (object, traps, deepTraps, flags, mock) => {
|
|
// We must not use normal object because there's a chance object already contains malicious code in the prototype
|
|
const base = host.Object.create(null);
|
|
|
|
base.get = (target, key, receiver) => {
|
|
try {
|
|
if (key === 'vmProxyTarget' && DEBUG) return object;
|
|
if (key === 'isVMProxy') return true;
|
|
if (mock && host.Object.prototype.hasOwnProperty.call(mock, key)) return mock[key];
|
|
if (key === 'constructor') return host.Object;
|
|
if (key === '__proto__') return host.Object.prototype;
|
|
} catch (e) {
|
|
// Never pass the handled exception through! This block can't throw an exception under normal conditions.
|
|
return null;
|
|
}
|
|
|
|
if (key === '__defineGetter__') return host.Object.prototype.__defineGetter__;
|
|
if (key === '__defineSetter__') return host.Object.prototype.__defineSetter__;
|
|
if (key === '__lookupGetter__') return host.Object.prototype.__lookupGetter__;
|
|
if (key === '__lookupSetter__') return host.Object.prototype.__lookupSetter__;
|
|
|
|
try {
|
|
return Decontextify.value(object[key], null, deepTraps, flags);
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
base.set = (target, key, value, receiver) => {
|
|
value = Contextify.value(value);
|
|
|
|
try {
|
|
return local.Reflect.set(object, key, value);
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
base.getOwnPropertyDescriptor = (target, prop) => {
|
|
let def;
|
|
|
|
try {
|
|
def = host.Object.getOwnPropertyDescriptor(object, prop);
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
|
|
// Following code prevents V8 to throw
|
|
// TypeError: 'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for property '<prop>'
|
|
// which is either non-existant or configurable in the proxy target
|
|
|
|
let desc;
|
|
if (!def) {
|
|
return undefined;
|
|
} else if (def.get || def.set) {
|
|
desc = {
|
|
__proto__: null,
|
|
get: Decontextify.value(def.get) || undefined,
|
|
set: Decontextify.value(def.set) || undefined,
|
|
enumerable: def.enumerable === true,
|
|
configurable: def.configurable === true
|
|
};
|
|
} else {
|
|
desc = {
|
|
__proto__: null,
|
|
value: Decontextify.value(def.value),
|
|
writable: def.writable === true,
|
|
enumerable: def.enumerable === true,
|
|
configurable: def.configurable === true
|
|
};
|
|
}
|
|
if (!desc.configurable) {
|
|
try {
|
|
def = host.Object.getOwnPropertyDescriptor(target, prop);
|
|
if (!def || def.configurable || def.writable !== desc.writable) {
|
|
local.Reflect.defineProperty(target, prop, desc);
|
|
}
|
|
} catch (e) {
|
|
// Should not happen.
|
|
}
|
|
}
|
|
return desc;
|
|
};
|
|
base.defineProperty = (target, key, descriptor) => {
|
|
let success = false;
|
|
try {
|
|
success = local.Reflect.setPrototypeOf(descriptor, null);
|
|
} catch (e) {
|
|
// Should not happen
|
|
}
|
|
if (!success) return false;
|
|
// There's a chance accessing a property throws an error so we must not access them
|
|
// in try catch to prevent contextifying local objects.
|
|
|
|
const propertyDescriptor = host.Object.create(null);
|
|
if (descriptor.get || descriptor.set) {
|
|
propertyDescriptor.get = Contextify.value(descriptor.get, null, deepTraps, flags) || undefined;
|
|
propertyDescriptor.set = Contextify.value(descriptor.set, null, deepTraps, flags) || undefined;
|
|
propertyDescriptor.enumerable = descriptor.enumerable === true;
|
|
propertyDescriptor.configurable = descriptor.configurable === true;
|
|
} else {
|
|
propertyDescriptor.value = Contextify.value(descriptor.value, null, deepTraps, flags);
|
|
propertyDescriptor.writable = descriptor.writable === true;
|
|
propertyDescriptor.enumerable = descriptor.enumerable === true;
|
|
propertyDescriptor.configurable = descriptor.configurable === true;
|
|
}
|
|
|
|
try {
|
|
success = local.Reflect.defineProperty(object, key, propertyDescriptor);
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
if (success && !descriptor.configurable) {
|
|
try {
|
|
local.Reflect.defineProperty(target, key, descriptor);
|
|
} catch (e) {
|
|
// This should not happen.
|
|
return false;
|
|
}
|
|
}
|
|
return success;
|
|
};
|
|
base.deleteProperty = (target, prop) => {
|
|
try {
|
|
return Decontextify.value(local.Reflect.deleteProperty(object, prop));
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
base.getPrototypeOf = (target) => {
|
|
return host.Object.prototype;
|
|
};
|
|
base.setPrototypeOf = (target) => {
|
|
throw new host.Error(OPNA);
|
|
};
|
|
base.has = (target, key) => {
|
|
try {
|
|
return Decontextify.value(local.Reflect.has(object, key));
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
base.isExtensible = target => {
|
|
let result;
|
|
try {
|
|
result = local.Reflect.isExtensible(object);
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
if (!result) {
|
|
try {
|
|
if (local.Reflect.isExtensible(target)) {
|
|
doPreventExtensions(target, object, obj => Contextify.value(obj, null, deepTraps, flags));
|
|
}
|
|
} catch (e) {
|
|
// Should not happen
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
base.ownKeys = target => {
|
|
try {
|
|
return Decontextify.value(local.Reflect.ownKeys(object));
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
base.preventExtensions = target => {
|
|
let success;
|
|
try {
|
|
success = local.Reflect.preventExtensions(object);
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
if (success) {
|
|
try {
|
|
if (local.Reflect.isExtensible(target)) {
|
|
doPreventExtensions(target, object, obj => Contextify.value(obj, null, deepTraps, flags));
|
|
}
|
|
} catch (e) {
|
|
// Should not happen
|
|
}
|
|
}
|
|
return success;
|
|
};
|
|
base.enumerate = target => {
|
|
try {
|
|
return Decontextify.value(local.Reflect.enumerate(object));
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
|
|
host.Object.assign(base, traps, deepTraps);
|
|
|
|
let shallow;
|
|
if (host.Array.isArray(object)) {
|
|
const origGet = base.get;
|
|
shallow = {
|
|
__proto__: null,
|
|
ownKeys: base.ownKeys,
|
|
// TODO this get will call getOwnPropertyDescriptor of target all the time.
|
|
get: origGet
|
|
};
|
|
base.ownKeys = target => {
|
|
try {
|
|
const keys = local.Reflect.ownKeys(object);
|
|
// Do this hack so that console.log(decontextify([1,2,3])) doesn't write the properties twice
|
|
// a la [1,2,3,'0':1,'1':2,'2':3]
|
|
return Decontextify.value(keys.filter(key => typeof key !== 'string' || !key.match(/^\d+$/)));
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
base.get = (target, key, receiver) => {
|
|
if (key === host.Symbol.toStringTag) return;
|
|
return origGet(target, key, receiver);
|
|
};
|
|
} else {
|
|
shallow = SHARED_OBJECT;
|
|
}
|
|
|
|
const proxy = new host.Proxy(createBaseObject(object), base);
|
|
Decontextified.set(proxy, object);
|
|
// We need two proxies since nodes inspect just removes one.
|
|
const proxy2 = new host.Proxy(proxy, shallow);
|
|
Decontextify.proxies.set(object, proxy2);
|
|
Decontextified.set(proxy2, object);
|
|
return proxy2;
|
|
};
|
|
Decontextify.value = (value, traps, deepTraps, flags, mock) => {
|
|
try {
|
|
if (Contextified.has(value)) {
|
|
// Contextified object has returned back from vm
|
|
return Contextified.get(value);
|
|
} else if (Decontextify.proxies.has(value)) {
|
|
// Decontextified proxy already exists, reuse
|
|
return Decontextify.proxies.get(value);
|
|
}
|
|
// debugger
|
|
switch (typeof value) {
|
|
case 'object':
|
|
if (value === null) {
|
|
return null;
|
|
} else if (instanceOf(value, Number)) {
|
|
return Decontextify.instance(value, host.Number, deepTraps, flags, 'Number');
|
|
} else if (instanceOf(value, String)) {
|
|
return Decontextify.instance(value, host.String, deepTraps, flags, 'String');
|
|
} else if (instanceOf(value, Boolean)) {
|
|
return Decontextify.instance(value, host.Boolean, deepTraps, flags, 'Boolean');
|
|
} else if (instanceOf(value, Date)) {
|
|
return Decontextify.instance(value, host.Date, deepTraps, flags, 'Date');
|
|
} else if (instanceOf(value, RangeError)) {
|
|
return Decontextify.instance(value, host.RangeError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, ReferenceError)) {
|
|
return Decontextify.instance(value, host.ReferenceError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, SyntaxError)) {
|
|
return Decontextify.instance(value, host.SyntaxError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, TypeError)) {
|
|
return Decontextify.instance(value, host.TypeError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, VMError)) {
|
|
return Decontextify.instance(value, host.VMError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, EvalError)) {
|
|
return Decontextify.instance(value, host.EvalError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, URIError)) {
|
|
return Decontextify.instance(value, host.URIError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, Error)) {
|
|
debugger
|
|
return Decontextify.instance(value, host.Error, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, Array)) {
|
|
return Decontextify.instance(value, host.Array, deepTraps, flags, 'Array');
|
|
} else if (instanceOf(value, RegExp)) {
|
|
return Decontextify.instance(value, host.RegExp, deepTraps, flags, 'RegExp');
|
|
} else if (instanceOf(value, Map)) {
|
|
return Decontextify.instance(value, host.Map, deepTraps, flags, 'Map');
|
|
} else if (instanceOf(value, WeakMap)) {
|
|
return Decontextify.instance(value, host.WeakMap, deepTraps, flags, 'WeakMap');
|
|
} else if (instanceOf(value, Set)) {
|
|
return Decontextify.instance(value, host.Set, deepTraps, flags, 'Set');
|
|
} else if (instanceOf(value, WeakSet)) {
|
|
return Decontextify.instance(value, host.WeakSet, deepTraps, flags, 'WeakSet');
|
|
} else if (typeof Promise === 'function' && instanceOf(value, Promise)) {
|
|
return Decontextify.instance(value, host.Promise, deepTraps, flags, 'Promise');
|
|
} else if (local.Reflect.getPrototypeOf(value) === null) {
|
|
return Decontextify.instance(value, null, deepTraps, flags);
|
|
} else {
|
|
return Decontextify.object(value, traps, deepTraps, flags, mock);
|
|
}
|
|
case 'function':
|
|
return Decontextify.function(value, traps, deepTraps, flags, mock);
|
|
|
|
case 'undefined':
|
|
return undefined;
|
|
|
|
default: // string, number, boolean, symbol
|
|
return value;
|
|
}
|
|
} catch (ex) {
|
|
// Never pass the handled exception through! This block can't throw an exception under normal conditions.
|
|
return null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Contextify.
|
|
*/
|
|
|
|
const Contextify = host.Object.create(null);
|
|
Contextify.proxies = new host.WeakMap();
|
|
|
|
Contextify.arguments = args => {
|
|
if (!host.Array.isArray(args)) return new local.Array();
|
|
|
|
try {
|
|
const arr = new local.Array();
|
|
for (let i = 0, l = args.length; i < l; i++) arr[i] = Contextify.value(args[i]);
|
|
return arr;
|
|
} catch (e) {
|
|
// Never pass the handled exception through!
|
|
return new local.Array();
|
|
}
|
|
};
|
|
Contextify.instance = (instance, klass, deepTraps, flags, toStringTag) => {
|
|
if (typeof instance === 'function') return Contextify.function(instance);
|
|
|
|
// We must not use normal object because there's a chance object already contains malicious code in the prototype
|
|
const base = host.Object.create(null);
|
|
|
|
base.get = (target, key, receiver) => {
|
|
try {
|
|
if (key === 'vmProxyTarget' && DEBUG) return instance;
|
|
if (key === 'isVMProxy') return true;
|
|
if (key === 'constructor') return klass;
|
|
if (key === '__proto__') return klass.prototype;
|
|
} catch (e) {
|
|
// Never pass the handled exception through! This block can't throw an exception under normal conditions.
|
|
return null;
|
|
}
|
|
|
|
if (key === '__defineGetter__') return local.Object.prototype.__defineGetter__;
|
|
if (key === '__defineSetter__') return local.Object.prototype.__defineSetter__;
|
|
if (key === '__lookupGetter__') return local.Object.prototype.__lookupGetter__;
|
|
if (key === '__lookupSetter__') return local.Object.prototype.__lookupSetter__;
|
|
if (key === host.Symbol.toStringTag && toStringTag) return toStringTag;
|
|
|
|
try {
|
|
return Contextify.value(host.Reflect.get(instance, key), null, deepTraps, flags);
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
};
|
|
base.getPrototypeOf = (target) => {
|
|
return klass && klass.prototype;
|
|
};
|
|
|
|
return Contextify.object(instance, base, deepTraps, flags);
|
|
};
|
|
Contextify.function = (fnc, traps, deepTraps, flags, mock) => {
|
|
// We must not use normal object because there's a chance object already contains malicious code in the prototype
|
|
const base = host.Object.create(null);
|
|
// eslint-disable-next-line prefer-const
|
|
let proxy;
|
|
|
|
base.apply = (target, context, args) => {
|
|
context = Decontextify.value(context);
|
|
|
|
// Set context of all arguments to host's context.
|
|
args = Decontextify.arguments(args);
|
|
|
|
try {
|
|
return Contextify.value(fnc.apply(context, args));
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
};
|
|
base.construct = (target, args, newTarget) => {
|
|
// Fixes buffer unsafe allocation for node v6/7
|
|
if (host.version < 8 && fnc === host.Buffer && 'number' === typeof args[0]) {
|
|
args[0] = new Array(args[0]).fill(0);
|
|
}
|
|
|
|
args = Decontextify.arguments(args);
|
|
|
|
try {
|
|
return Contextify.instance(new fnc(...args), proxy, deepTraps, flags);
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
};
|
|
base.get = (target, key, receiver) => {
|
|
try {
|
|
if (key === 'vmProxyTarget' && DEBUG) return fnc;
|
|
if (key === 'isVMProxy') return true;
|
|
if (mock && host.Object.prototype.hasOwnProperty.call(mock, key)) return mock[key];
|
|
if (key === 'constructor') return Function;
|
|
if (key === '__proto__') return Function.prototype;
|
|
} catch (e) {
|
|
// Never pass the handled exception through! This block can't throw an exception under normal conditions.
|
|
return null;
|
|
}
|
|
|
|
if (key === '__defineGetter__') return local.Object.prototype.__defineGetter__;
|
|
if (key === '__defineSetter__') return local.Object.prototype.__defineSetter__;
|
|
if (key === '__lookupGetter__') return local.Object.prototype.__lookupGetter__;
|
|
if (key === '__lookupSetter__') return local.Object.prototype.__lookupSetter__;
|
|
|
|
if (key === 'caller' || key === 'callee' || key === 'arguments') throw throwCallerCalleeArgumentsAccess(key);
|
|
|
|
try {
|
|
return Contextify.value(host.Reflect.get(fnc, key), null, deepTraps, flags);
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
};
|
|
base.getPrototypeOf = (target) => {
|
|
return Function.prototype;
|
|
};
|
|
|
|
proxy = Contextify.object(fnc, host.Object.assign(base, traps), deepTraps);
|
|
return proxy;
|
|
};
|
|
Contextify.object = (object, traps, deepTraps, flags, mock) => {
|
|
// We must not use normal object because there's a chance object already contains malicious code in the prototype
|
|
const base = host.Object.create(null);
|
|
|
|
base.get = (target, key, receiver) => {
|
|
try {
|
|
if (key === 'vmProxyTarget' && DEBUG) return object;
|
|
if (key === 'isVMProxy') return true;
|
|
if (mock && host.Object.prototype.hasOwnProperty.call(mock, key)) return mock[key];
|
|
if (key === 'constructor') return Object;
|
|
if (key === '__proto__') return Object.prototype;
|
|
} catch (e) {
|
|
// Never pass the handled exception through! This block can't throw an exception under normal conditions.
|
|
return null;
|
|
}
|
|
|
|
if (key === '__defineGetter__') return local.Object.prototype.__defineGetter__;
|
|
if (key === '__defineSetter__') return local.Object.prototype.__defineSetter__;
|
|
if (key === '__lookupGetter__') return local.Object.prototype.__lookupGetter__;
|
|
if (key === '__lookupSetter__') return local.Object.prototype.__lookupSetter__;
|
|
|
|
try {
|
|
return Contextify.value(host.Reflect.get(object, key), null, deepTraps, flags);
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
};
|
|
base.set = (target, key, value, receiver) => {
|
|
if (key === '__proto__') return false;
|
|
if (flags && flags.protected && typeof value === 'function') return false;
|
|
|
|
value = Decontextify.value(value);
|
|
|
|
try {
|
|
return host.Reflect.set(object, key, value);
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
};
|
|
base.getOwnPropertyDescriptor = (target, prop) => {
|
|
let def;
|
|
|
|
try {
|
|
def = host.Object.getOwnPropertyDescriptor(object, prop);
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
|
|
// Following code prevents V8 to throw
|
|
// TypeError: 'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for property '<prop>'
|
|
// which is either non-existant or configurable in the proxy target
|
|
|
|
let desc;
|
|
if (!def) {
|
|
return undefined;
|
|
} else if (def.get || def.set) {
|
|
desc = {
|
|
__proto__: null,
|
|
get: Contextify.value(def.get, null, deepTraps, flags) || undefined,
|
|
set: Contextify.value(def.set, null, deepTraps, flags) || undefined,
|
|
enumerable: def.enumerable === true,
|
|
configurable: def.configurable === true
|
|
};
|
|
} else {
|
|
desc = {
|
|
__proto__: null,
|
|
value: Contextify.value(def.value, null, deepTraps, flags),
|
|
writable: def.writable === true,
|
|
enumerable: def.enumerable === true,
|
|
configurable: def.configurable === true
|
|
};
|
|
}
|
|
if (!desc.configurable) {
|
|
try {
|
|
def = host.Object.getOwnPropertyDescriptor(target, prop);
|
|
if (!def || def.configurable || def.writable !== desc.writable) {
|
|
local.Reflect.defineProperty(target, prop, desc);
|
|
}
|
|
} catch (e) {
|
|
// Should not happen.
|
|
}
|
|
}
|
|
return desc;
|
|
};
|
|
base.defineProperty = (target, key, descriptor) => {
|
|
let success = false;
|
|
try {
|
|
success = local.Reflect.setPrototypeOf(descriptor, null);
|
|
} catch (e) {
|
|
// Should not happen
|
|
}
|
|
if (!success) return false;
|
|
// There's a chance accessing a property throws an error so we must not access them
|
|
// in try catch to prevent contextifying local objects.
|
|
|
|
const descGet = descriptor.get;
|
|
const descSet = descriptor.set;
|
|
const descValue = descriptor.value;
|
|
|
|
if (flags && flags.protected) {
|
|
if (descGet || descSet || typeof descValue === 'function') return false;
|
|
}
|
|
|
|
const propertyDescriptor = host.Object.create(null);
|
|
if (descGet || descSet) {
|
|
propertyDescriptor.get = Decontextify.value(descGet, null, deepTraps, flags) || undefined;
|
|
propertyDescriptor.set = Decontextify.value(descSet, null, deepTraps, flags) || undefined;
|
|
propertyDescriptor.enumerable = descriptor.enumerable === true;
|
|
propertyDescriptor.configurable = descriptor.configurable === true;
|
|
} else {
|
|
propertyDescriptor.value = Decontextify.value(descValue, null, deepTraps, flags);
|
|
propertyDescriptor.writable = descriptor.writable === true;
|
|
propertyDescriptor.enumerable = descriptor.enumerable === true;
|
|
propertyDescriptor.configurable = descriptor.configurable === true;
|
|
}
|
|
|
|
try {
|
|
success = host.Reflect.defineProperty(object, key, propertyDescriptor);
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
if (success && !descriptor.configurable) {
|
|
try {
|
|
local.Reflect.defineProperty(target, key, descriptor);
|
|
} catch (e) {
|
|
// This should not happen.
|
|
return false;
|
|
}
|
|
}
|
|
return success;
|
|
};
|
|
base.deleteProperty = (target, prop) => {
|
|
try {
|
|
return Contextify.value(host.Reflect.deleteProperty(object, prop));
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
};
|
|
base.getPrototypeOf = (target) => {
|
|
return local.Object.prototype;
|
|
};
|
|
base.setPrototypeOf = (target) => {
|
|
throw new VMError(OPNA);
|
|
};
|
|
base.has = (target, key) => {
|
|
try {
|
|
return Contextify.value(host.Reflect.has(object, key));
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
};
|
|
base.isExtensible = target => {
|
|
let result;
|
|
try {
|
|
result = host.Reflect.isExtensible(object);
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
if (!result) {
|
|
try {
|
|
if (local.Reflect.isExtensible(target)) {
|
|
doPreventExtensions(target, object, obj => Decontextify.value(obj, null, deepTraps, flags));
|
|
}
|
|
} catch (e) {
|
|
// Should not happen
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
base.ownKeys = target => {
|
|
try {
|
|
return Contextify.value(host.Reflect.ownKeys(object));
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
};
|
|
base.preventExtensions = target => {
|
|
let success;
|
|
try {
|
|
success = local.Reflect.preventExtensions(object);
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
if (success) {
|
|
try {
|
|
if (local.Reflect.isExtensible(target)) {
|
|
doPreventExtensions(target, object, obj => Decontextify.value(obj, null, deepTraps, flags));
|
|
}
|
|
} catch (e) {
|
|
// Should not happen
|
|
}
|
|
}
|
|
return success;
|
|
};
|
|
base.enumerate = target => {
|
|
try {
|
|
return Contextify.value(host.Reflect.enumerate(object));
|
|
} catch (e) {
|
|
throw Contextify.value(e);
|
|
}
|
|
};
|
|
|
|
// const proxy = new host.Proxy(createBaseObject(object), host.Object.assign(base, traps, deepTraps));
|
|
// debugger
|
|
const proxy = object;
|
|
Contextify.proxies.set(object, proxy);
|
|
Contextified.set(proxy, object);
|
|
return proxy;
|
|
};
|
|
Contextify.value = (value, traps, deepTraps, flags, mock) => {
|
|
try {
|
|
if (Decontextified.has(value)) {
|
|
// Contextify.setGlobal Decontextified object has returned back to vm
|
|
return Decontextified.get(value);
|
|
} else if (Contextify.proxies.has(value)) {
|
|
// Contextified proxy already exists, reuse
|
|
return Contextify.proxies.get(value);
|
|
}
|
|
// debugger
|
|
switch (typeof value) {
|
|
case 'object':
|
|
if (value === null) {
|
|
return null;
|
|
} else if (instanceOf(value, host.Number)) {
|
|
return Contextify.instance(value, Number, deepTraps, flags, 'Number');
|
|
} else if (instanceOf(value, host.String)) {
|
|
return Contextify.instance(value, String, deepTraps, flags, 'String');
|
|
} else if (instanceOf(value, host.Boolean)) {
|
|
return Contextify.instance(value, Boolean, deepTraps, flags, 'Boolean');
|
|
} else if (instanceOf(value, host.Date)) {
|
|
return Contextify.instance(value, Date, deepTraps, flags, 'Date');
|
|
} else if (instanceOf(value, host.RangeError)) {
|
|
return Contextify.instance(value, RangeError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, host.ReferenceError)) {
|
|
return Contextify.instance(value, ReferenceError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, host.SyntaxError)) {
|
|
return Contextify.instance(value, SyntaxError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, host.TypeError)) {
|
|
return Contextify.instance(value, TypeError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, host.VMError)) {
|
|
return Contextify.instance(value, VMError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, host.EvalError)) {
|
|
return Contextify.instance(value, EvalError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, host.URIError)) {
|
|
return Contextify.instance(value, URIError, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, host.Error)) {
|
|
debugger
|
|
return Contextify.instance(value, Error, deepTraps, flags, 'Error');
|
|
} else if (instanceOf(value, host.Array)) {
|
|
return Contextify.instance(value, Array, deepTraps, flags, 'Array');
|
|
} else if (instanceOf(value, host.RegExp)) {
|
|
return Contextify.instance(value, RegExp, deepTraps, flags, 'RegExp');
|
|
} else if (instanceOf(value, host.Map)) {
|
|
return Contextify.instance(value, Map, deepTraps, flags, 'Map');
|
|
} else if (instanceOf(value, host.WeakMap)) {
|
|
return Contextify.instance(value, WeakMap, deepTraps, flags, 'WeakMap');
|
|
} else if (instanceOf(value, host.Set)) {
|
|
return Contextify.instance(value, Set, deepTraps, flags, 'Set');
|
|
} else if (instanceOf(value, host.WeakSet)) {
|
|
return Contextify.instance(value, WeakSet, deepTraps, flags, 'WeakSet');
|
|
} else if (typeof Promise === 'function' && instanceOf(value, host.Promise)) {
|
|
return Contextify.instance(value, Promise, deepTraps, flags, 'Promise');
|
|
} else if (instanceOf(value, host.Buffer)) {
|
|
return Contextify.instance(value, LocalBuffer, deepTraps, flags, 'Uint8Array');
|
|
} else if (host.Reflect.getPrototypeOf(value) === null) {
|
|
return Contextify.instance(value, null, deepTraps, flags);
|
|
} else {
|
|
return Contextify.object(value, traps, deepTraps, flags, mock);
|
|
}
|
|
case 'function':
|
|
return Contextify.function(value, traps, deepTraps, flags, mock);
|
|
|
|
case 'undefined':
|
|
return undefined;
|
|
|
|
default: // string, number, boolean, symbol
|
|
return value;
|
|
}
|
|
} catch (ex) {
|
|
// Never pass the handled exception through! This block can't throw an exception under normal conditions.
|
|
return null;
|
|
}
|
|
};
|
|
Contextify.setGlobal = (name, value) => {
|
|
const prop = Contextify.value(name);
|
|
try {
|
|
global[prop] = value;//Contextify.value(value);
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
Contextify.getGlobal = (name) => {
|
|
const prop = Contextify.value(name);
|
|
try {
|
|
return Decontextify.value(global[prop]);
|
|
} catch (e) {
|
|
throw Decontextify.value(e);
|
|
}
|
|
};
|
|
Contextify.readonly = (value, mock) => {
|
|
// debugger
|
|
return Contextify.value(value, null, FROZEN_TRAPS, null, mock);
|
|
};
|
|
Contextify.protected = (value, mock) => {
|
|
return Contextify.value(value, null, null, { protected: true }, mock);
|
|
};
|
|
Contextify.connect = (outer, inner) => {
|
|
Decontextified.set(outer, inner);
|
|
Contextified.set(inner, outer);
|
|
};
|
|
Contextify.makeModule = () => ({ exports: {} });
|
|
Contextify.isVMProxy = (obj) => Decontextified.has(obj);
|
|
|
|
const BufferMock = host.Object.create(null);
|
|
BufferMock.allocUnsafe = function allocUnsafe(size) {
|
|
return this.alloc(size);
|
|
};
|
|
BufferMock.allocUnsafeSlow = function allocUnsafeSlow(size) {
|
|
return this.alloc(size);
|
|
};
|
|
const BufferOverride = host.Object.create(null);
|
|
BufferOverride.inspect = 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}>`;
|
|
};
|
|
const LocalBuffer = global.Buffer = Contextify.readonly(host.Buffer, BufferMock);
|
|
Contextify.connect(host.Buffer.prototype.inspect, BufferOverride.inspect);
|
|
|
|
|
|
const exportsMap = host.Object.create(null);
|
|
exportsMap.Contextify = Contextify;
|
|
exportsMap.Decontextify = Decontextify;
|
|
exportsMap.Buffer = LocalBuffer;
|
|
exportsMap.sandbox = Decontextify.value(global);
|
|
exportsMap.Function = Function;
|
|
// debugger
|
|
return exportsMap;
|