@caridy/sjs
Advanced tools
Comparing version 0.2.13 to 0.2.14
@@ -1,2 +0,2 @@ | ||
import { apply, assign, construct, ReflectSetPrototypeOf, freeze, isFunction, ObjectCreate, isUndefined, ReflectGetOwnPropertyDescriptor, ReflectDefineProperty, ErrorCreate, ReflectGetPrototypeOf, ReflectGet, ReflectSet, ReflectHas, map, isNull, isNullOrUndefined, unconstruct, ownKeys, ReflectIsExtensible, ReflectPreventExtensions, deleteProperty, hasOwnProperty, emptyArray, } from "./shared.js"; | ||
import { apply, assign, construct, ReflectSetPrototypeOf, freeze, isFunction, ObjectCreate, isUndefined, ReflectGetOwnPropertyDescriptor, ReflectDefineProperty, ReflectGetPrototypeOf, ReflectGet, ReflectSet, ReflectHas, map, isNull, isNullOrUndefined, unconstruct, ownKeys, ReflectIsExtensible, ReflectPreventExtensions, deleteProperty, hasOwnProperty, emptyArray, } from "./shared.js"; | ||
function renameFunction(provider, receiver) { | ||
@@ -122,20 +122,3 @@ try { | ||
catch (e) { | ||
// This error occurred when the blue realm attempts to call a | ||
// function from the sandbox. By throwing a new blue error, we eliminates the stack | ||
// information from the sandbox as a consequence. | ||
let blueError; | ||
const { message, constructor } = e; | ||
try { | ||
// the error constructor must be a red error since it occur when calling | ||
// a function from the sandbox. | ||
const blueErrorConstructor = env.getBlueRef(constructor); | ||
// the blue constructor must be registered (done during construction of env) | ||
// otherwise we need to fallback to a regular error. | ||
blueError = construct(blueErrorConstructor, [message]); | ||
} | ||
catch (_a) { | ||
// in case the constructor inference fails | ||
blueError = ErrorCreate(message); | ||
} | ||
throw blueError; | ||
throw env.getBlueValue(e); | ||
} | ||
@@ -156,20 +139,3 @@ return env.getBlueValue(red); | ||
catch (e) { | ||
// This error occurred when the blue realm attempts to new a | ||
// constructor from the sandbox. By throwing a new blue error, we eliminates the stack | ||
// information from the sandbox as a consequence. | ||
let blueError; | ||
const { message, constructor } = e; | ||
try { | ||
// the error constructor must be a red error since it occur when calling | ||
// a function from the sandbox. | ||
const blueErrorConstructor = env.getBlueRef(constructor); | ||
// the blue constructor must be registered (done during construction of env) | ||
// otherwise we need to fallback to a regular error. | ||
blueError = construct(blueErrorConstructor, [message]); | ||
} | ||
catch (_a) { | ||
// in case the constructor inference fails | ||
blueError = ErrorCreate(message); | ||
} | ||
throw blueError; | ||
throw env.getBlueValue(e); | ||
} | ||
@@ -176,0 +142,0 @@ return env.getBlueValue(red); |
import { SecureEnvironment } from "./environment.js"; | ||
import { ReflectGetPrototypeOf, ReflectSetPrototypeOf, getOwnPropertyDescriptors, construct, ErrorCreate, WeakMapCreate, isUndefined, ObjectCreate, WeakMapGet, assign, ownKeys, unapply, ReflectGetOwnPropertyDescriptor, } from "./shared.js"; | ||
; | ||
const cachedGlobalMap = WeakMapCreate(); | ||
/** | ||
* Given a Window reference, extract a set of references that are important | ||
* for the sandboxing mechanism, this includes: | ||
* - Unforgeable prototypes | ||
* - Descriptor maps for those unforgeable prototypes | ||
*/ | ||
function getCachedReferences(global) { | ||
let record = WeakMapGet(cachedGlobalMap, global); | ||
if (!isUndefined(record)) { | ||
return record; | ||
} | ||
record = ObjectCreate(null); | ||
// caching references to object values that can't be replaced | ||
// window -> Window -> WindowProperties -> EventTarget | ||
record.window = global.window; | ||
record.document = global.document; | ||
record.WindowProto = ReflectGetPrototypeOf(record.window); | ||
record.WindowPropertiesProto = ReflectGetPrototypeOf(record.WindowProto); | ||
record.EventTargetProto = ReflectGetPrototypeOf(record.WindowPropertiesProto); | ||
record.DocumentProto = ReflectGetPrototypeOf(record.document); | ||
// caching descriptors | ||
record.windowDescriptors = getOwnPropertyDescriptors(record.window); | ||
record.WindowProtoDescriptors = getOwnPropertyDescriptors(record.WindowProto); | ||
record.WindowPropertiesProtoDescriptors = getOwnPropertyDescriptors(record.WindowPropertiesProto); | ||
record.EventTargetProtoDescriptors = getOwnPropertyDescriptors(record.EventTargetProto); | ||
return record; | ||
} | ||
/** | ||
* Initialization operation to capture and cache all unforgeable references | ||
* and their respective descriptor maps before any other code runs, this | ||
* usually help because this library runs before anything else that can poison | ||
* the environment. | ||
*/ | ||
getCachedReferences(globalThis); | ||
/** | ||
* global descriptors are a combination of 3 set of descriptors: | ||
* - first, the key of the red descriptors define the descriptors | ||
* provided by the browser when creating a brand new window. | ||
* - second, once we know the base keys, we get the actual descriptors | ||
* from the blueDescriptors, since those are the one we want to provide | ||
* access to via the membrane. | ||
* - third, the user of this library can provide endowments, which define | ||
* global descriptors that should be installed into the sandbox on top | ||
* of the base descriptors. | ||
* | ||
* Note: The main reason for using redDescriptors as the base keys instead | ||
* of blueDescriptor is because there is no guarantee that this library is | ||
* the first one to be evaluated in the host app, which means it has no ways | ||
* to determine what is a real DOM API vs app specific globals. | ||
* | ||
* Quirk: The only quirk here is for the case in which this library runs | ||
* after some other code that patches some of the DOM APIs. This means | ||
* the installed proxy in the sandbox will point to the patched global | ||
* API in the blue realm, rather than the original, because we don't have | ||
* a way to access the original anymore. This should not be a deal-breaker | ||
* if the patched API behaves according to the spec. | ||
* | ||
* The result of this method is a descriptor map that contains everything | ||
* that will be installed (via the membrane) as global descriptors in | ||
* the red realm. | ||
*/ | ||
function aggregateGlobalDescriptors(redDescriptors, blueDescriptors, globalDescriptors) { | ||
const to = ObjectCreate(null); | ||
const baseKeys = ownKeys(redDescriptors); | ||
for (let i = 0, len = baseKeys.length; i < len; i++) { | ||
const key = baseKeys[i]; | ||
to[key] = blueDescriptors[key]; | ||
} | ||
// global descriptors are user provided descriptors via endowments | ||
// which will overrule any default descriptor inferred from the | ||
// detached iframe. | ||
assign(to, globalDescriptors); | ||
// removing unforgeable descriptors that cannot be installed | ||
delete to.location; | ||
delete to.document; | ||
delete to.window; | ||
// Some DOM APIs do brand checks for TypeArrays and others objects, | ||
// in this case, if the API is not dangerous, and works in a detached | ||
// iframe, we can let the sandbox to use the iframe's api directly, | ||
// instead of remapping it to the blue realm. | ||
// TODO [issue #67]: review this list | ||
delete to.crypto; | ||
return to; | ||
} | ||
import { unapply, ReflectGetOwnPropertyDescriptor } from "./shared.js"; | ||
import { linkIntrinsics, getFilteredEndowmentDescriptors } from "./intrinsics.js"; | ||
import { getCachedReferences, linkUnforgeables, tameDOM } from "./window.js"; | ||
// A comprehensive list of policy feature directives can be found at | ||
@@ -147,26 +63,20 @@ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy#Directives | ||
const iframe = createDetachableIframe(); | ||
// For Chrome we evaluate the `window` object to kickstart the realm so that | ||
// `window` persists when the iframe is removed from the document. | ||
const redGlobalThis = iframe.contentWindow.window; | ||
const { eval: redIndirectEval } = redGlobalThis; | ||
redIndirectEval('window'); | ||
const blueGlobalThis = globalThis; | ||
removeIframe(iframe); | ||
const blueRefs = getCachedReferences(blueGlobalThis); | ||
const redRefs = getCachedReferences(redGlobalThis); | ||
const blueWindow = window; | ||
const redWindow = iframe.contentWindow.window; | ||
const endowmentsDescriptors = getFilteredEndowmentDescriptors(endowments || {}); | ||
const { eval: redIndirectEval } = redWindow; | ||
// extract the global references and descriptors before any interference | ||
const blueRefs = getCachedReferences(blueWindow); | ||
const redRefs = getCachedReferences(redWindow); | ||
// creating a new environment | ||
const env = new SecureEnvironment({ | ||
blueGlobalThis, | ||
redGlobalThis, | ||
blueGlobalThis: blueWindow, | ||
redGlobalThis: redWindow, | ||
distortionMap, | ||
}); | ||
const globalDescriptors = aggregateGlobalDescriptors(redRefs.windowDescriptors, blueRefs.windowDescriptors, endowments && getOwnPropertyDescriptors(endowments)); | ||
// remapping globals | ||
env.remap(redRefs.window, blueRefs.window, globalDescriptors); | ||
// remapping unforgeable objects | ||
env.remap(redRefs.document, blueRefs.document); // no descriptors needed, document.location is unforgeable | ||
env.remap(redRefs.EventTargetProto, blueRefs.EventTargetProto, blueRefs.EventTargetProtoDescriptors); | ||
env.remap(redRefs.WindowPropertiesProto, blueRefs.WindowPropertiesProto, blueRefs.WindowPropertiesProtoDescriptors); | ||
env.remap(redRefs.WindowProto, blueRefs.WindowProto, blueRefs.WindowProtoDescriptors); | ||
// adjusting proto chains when possible | ||
ReflectSetPrototypeOf(redRefs.document, env.getRedValue(blueRefs.DocumentProto)); | ||
linkIntrinsics(env, blueWindow, redWindow); | ||
linkUnforgeables(env, blueRefs, redRefs); | ||
tameDOM(env, blueRefs, redRefs, endowmentsDescriptors); | ||
// once we get the iframe info ready, and all mapped, we can proceed to detach the iframe | ||
removeIframe(iframe); | ||
// finally, we return the evaluator function | ||
@@ -178,18 +88,3 @@ return (sourceText) => { | ||
catch (e) { | ||
// This error occurred when the blue realm attempts to evaluate a | ||
// sourceText into the sandbox. By throwing a new blue error, which | ||
// eliminates the stack information from the sandbox as a consequence. | ||
let blueError; | ||
const { message, constructor } = e; | ||
try { | ||
const blueErrorConstructor = env.getBlueRef(constructor); | ||
// the constructor must be registered (done during construction of env) | ||
// otherwise we need to fallback to a regular error. | ||
blueError = construct(blueErrorConstructor, [message]); | ||
} | ||
catch (_a) { | ||
// in case the constructor inference fails | ||
blueError = ErrorCreate(message); | ||
} | ||
throw blueError; | ||
throw env.getBlueValue(e); | ||
} | ||
@@ -196,0 +91,0 @@ }; |
@@ -1,21 +0,4 @@ | ||
import { apply, assign, isUndefined, isNullOrUndefined, ObjectCreate, isFunction, hasOwnProperty, ReflectDefineProperty, emptyArray, ErrorCreate, ESGlobalKeys, ReflectGetOwnPropertyDescriptor, ReflectIsExtensible, SetHas, WeakMapCreate, WeakMapHas, WeakMapSet, ReflectiveIntrinsicObjectNames, WeakMapGet, construct, ownKeys, } from "./shared.js"; | ||
import { apply, assign, isUndefined, ObjectCreate, isFunction, ReflectDefineProperty, emptyArray, ErrorCreate, ReflectGetOwnPropertyDescriptor, WeakMapCreate, WeakMapSet, WeakMapGet, construct, isTrue, ownKeys, } from "./shared.js"; | ||
import { serializedRedEnvSourceText } from "./red.js"; | ||
import { blueProxyFactory } from "./blue.js"; | ||
const cachedReflectiveIntrinsicsMap = WeakMapCreate(); | ||
function getReflectiveIntrinsics(global) { | ||
let reflectiveIntrinsics = WeakMapGet(cachedReflectiveIntrinsicsMap, global); | ||
if (!isUndefined(reflectiveIntrinsics)) { | ||
return reflectiveIntrinsics; | ||
} | ||
reflectiveIntrinsics = ObjectCreate(null); | ||
WeakMapSet(cachedReflectiveIntrinsicsMap, global, reflectiveIntrinsics); | ||
// remapping intrinsics that are realm's agnostic | ||
for (let i = 0, len = ReflectiveIntrinsicObjectNames.length; i < len; i += 1) { | ||
const name = ReflectiveIntrinsicObjectNames[i]; | ||
reflectiveIntrinsics[name] = global[name]; | ||
} | ||
return reflectiveIntrinsics; | ||
} | ||
// caching from the blue realm right away to avoid picking up modified entries | ||
getReflectiveIntrinsics(globalThis); | ||
export class SecureEnvironment { | ||
@@ -30,3 +13,3 @@ constructor(options) { | ||
} | ||
const { blueGlobalThis, redGlobalThis, distortionMap } = options; | ||
const { redGlobalThis, distortionMap } = options; | ||
this.distortionMap = WeakMapCreate(); | ||
@@ -43,2 +26,7 @@ // validating distortion entries | ||
const redEnvFactory = redGlobalThis.eval(`(${serializedRedEnvSourceText})`); | ||
// Important Note: removing the indirection for apply and construct breaks | ||
// chrome karma tests for some unknown reasons. What is seems harmless turns out | ||
// to be fatal, why? it seems that this is because Chrome does identity checks | ||
// on those intrinsics, and fails if the detached iframe is calling an intrinsic | ||
// from another realm. | ||
const blueHooks = { | ||
@@ -54,12 +42,2 @@ apply(target, thisArgument, argumentsList) { | ||
this.getBlueValue = blueProxyFactory(this); | ||
// remapping intrinsics that are realm's agnostic | ||
const blueIntrinsics = getReflectiveIntrinsics(blueGlobalThis); | ||
const redIntrinsics = getReflectiveIntrinsics(redGlobalThis); | ||
for (let i = 0, len = ReflectiveIntrinsicObjectNames.length; i < len; i += 1) { | ||
const name = ReflectiveIntrinsicObjectNames[i]; | ||
const blue = blueIntrinsics[name]; | ||
const red = redIntrinsics[name]; | ||
this.setRefMapEntries(red, blue); | ||
this.setRefMapEntries(red.prototype, blue.prototype); | ||
} | ||
} | ||
@@ -90,119 +68,52 @@ getBlueValue(red) { | ||
remap(redValue, blueValue, blueDescriptors) { | ||
this.setRefMapEntries(redValue, blueValue); | ||
if (!isUndefined(blueDescriptors)) { | ||
const keys = ownKeys(blueDescriptors); | ||
for (let i = 0, len = keys.length; i < len; i += 1) { | ||
const key = keys[i]; | ||
// TODO: this whole loop needs cleanup and simplification avoid | ||
// overriding ECMAScript global keys. | ||
if (SetHas(ESGlobalKeys, key) || !hasOwnProperty(blueDescriptors, key)) { | ||
continue; | ||
const broker = this; | ||
const keys = ownKeys(blueDescriptors); | ||
for (let i = 0, len = keys.length; i < len; i += 1) { | ||
const key = keys[i]; | ||
if (!canRedPropertyBeTamed(redValue, key)) { | ||
console.warn(`Property ${String(key)} of ${redValue} cannot be remapped.`); | ||
continue; | ||
} | ||
// avoid poisoning by only installing own properties from blueDescriptors | ||
// @ts-expect-error because PropertyDescriptorMap does not accept symbols ATM | ||
const blueDescriptor = assign(ObjectCreate(null), blueDescriptors[key]); | ||
const redDescriptor = assign(ObjectCreate(null), blueDescriptor); | ||
if ('value' in blueDescriptor) { | ||
redDescriptor.value = broker.getRedValue(blueDescriptor.value); | ||
} | ||
else { | ||
// Use the original getter to return a red object, but if the | ||
// sandbox attempts to set it to a new value, this mutation will | ||
// only affect the sandbox's global object, and the getter will | ||
// start returning the new provided value rather than calling onto | ||
// the blue realm. This is to preserve the object graph of the | ||
// blue realm. | ||
let currentBlueGetter = () => undefined; | ||
if (isFunction(blueDescriptor.get)) { | ||
const { get: blueGetter } = blueDescriptor; | ||
const blueDistortedGetter = WeakMapGet(this.distortionMap, blueGetter) || blueGetter; | ||
currentBlueGetter = function () { | ||
const value = apply(blueDistortedGetter, broker.getBlueValue(this), emptyArray); | ||
return broker.getRedValue(value); | ||
}; | ||
redDescriptor.get = function () { | ||
return apply(currentBlueGetter, this, emptyArray); | ||
}; | ||
} | ||
// avoid poisoning by only installing own properties from blueDescriptors | ||
// @ts-ignore PropertyDescriptorMap def defines properties as being only of string type | ||
const blueDescriptor = assign(ObjectCreate(null), blueDescriptors[key]); | ||
if ('value' in blueDescriptor) { | ||
// TODO: maybe we should make everything a getter/setter that way | ||
// we don't pay the cost of creating the proxy in the first place | ||
blueDescriptor.value = this.getRedValue(blueDescriptor.value); | ||
} | ||
else { | ||
// Use the original getter to return a red object, but if the | ||
// sandbox attempts to set it to a new value, this mutation will | ||
// only affect the sandbox's global object, and the getter will | ||
// start returning the new provided value rather than calling onto | ||
// the blue realm. This is to preserve the object graph of the | ||
// blue realm. | ||
const env = this; | ||
const { get: originalGetter } = blueDescriptor; | ||
let currentGetter = () => undefined; | ||
if (isFunction(originalGetter)) { | ||
const originalOrDistortedGetter = WeakMapGet(this.distortionMap, originalGetter) || originalGetter; | ||
currentGetter = function () { | ||
const value = apply(originalOrDistortedGetter, env.getBlueValue(this), emptyArray); | ||
return env.getRedValue(value); | ||
}; | ||
} | ||
blueDescriptor.get = function () { | ||
return apply(currentGetter, this, emptyArray); | ||
if (isFunction(blueDescriptor.set)) { | ||
redDescriptor.set = function (v) { | ||
// if a global setter is invoke, the value will be use as it is as the result of the getter operation | ||
currentBlueGetter = () => v; | ||
}; | ||
if (isFunction(blueDescriptor.set)) { | ||
blueDescriptor.set = function (v) { | ||
// if a global setter is invoke, the value will be use as it is as the result of the getter operation | ||
currentGetter = () => v; | ||
}; | ||
} | ||
} | ||
const redDescriptor = ReflectGetOwnPropertyDescriptor(redValue, key); | ||
if (!isUndefined(redDescriptor) && | ||
hasOwnProperty(redDescriptor, 'configurable') && | ||
redDescriptor.configurable === false) { | ||
const redPropertyValue = redValue[key]; | ||
if (isNullOrUndefined(redPropertyValue)) { | ||
continue; | ||
} | ||
// this is the case where the red realm has a global descriptor that was supposed to be | ||
// overrule but can't be done because it is a non-configurable. Instead we try to | ||
// fallback to some more advanced gymnastics | ||
if (hasOwnProperty(redDescriptor, 'value')) { | ||
// valid proxy target (intentionally ignoring the case of document.all since it is not a value descriptor) | ||
if (typeof redPropertyValue === 'function' || typeof redPropertyValue === 'object') { | ||
if (!WeakMapHas(this.redMap, redPropertyValue)) { | ||
// remapping the value of the red object graph to the blue realm graph | ||
const { value: blueDescriptorValue } = blueDescriptor; | ||
if (redValue !== blueDescriptorValue) { | ||
if (this.getBlueValue(redValue) !== blueValue) { | ||
console.error('need remapping: ', key, blueValue, blueDescriptor); | ||
} | ||
else { | ||
// it was already mapped | ||
} | ||
} | ||
else { | ||
// window.top is the classic example of a descriptor that leaks access to the blue | ||
// window reference, and there is no containment for that case yet. | ||
console.error('leaking: ', key, blueValue, blueDescriptor); | ||
} | ||
} | ||
else { | ||
// an example of this is circular window.window ref | ||
console.info('circular: ', key, blueValue, blueDescriptor); | ||
} | ||
} | ||
} | ||
else if (hasOwnProperty(redDescriptor, 'get')) { | ||
// internationally ignoring the case of (typeof document.all === 'undefined') because | ||
// it is specified as configurable, you never get one of those exotic objects in this branch | ||
if (typeof redPropertyValue === 'function' || typeof redPropertyValue === 'object') { | ||
if (redPropertyValue === redValue[key]) { | ||
// this is the case for window.document which is identity preserving getter | ||
// const blueDescriptorValue = blueValue[key]; | ||
// this.setRefMapEntries(redDescriptorValue, blueDescriptorValue); | ||
// this.installDescriptors(redDescriptorValue, blueDescriptorValue, getOwnPropertyDescriptors(blueDescriptorValue)); | ||
console.error('need remapping: ', key, blueValue, blueDescriptor); | ||
if (ReflectIsExtensible(redPropertyValue)) { | ||
// remapping proto chain | ||
// ReflectSetPrototypeOf(redDescriptorValue, this.getRedValue(ReflectGetPrototypeOf(redDescriptorValue))); | ||
console.error('needs prototype remapping: ', key, blueValue); | ||
} | ||
else { | ||
console.error('leaking prototype: ', key, blueValue, blueDescriptor); | ||
} | ||
} | ||
else { | ||
console.error('leaking a getter returning values without identity: ', key, blueValue, blueDescriptor); | ||
} | ||
} | ||
else { | ||
console.error('skipping: ', key, blueValue, blueDescriptor); | ||
} | ||
} | ||
} | ||
else { | ||
ReflectDefineProperty(redValue, key, blueDescriptor); | ||
} | ||
} | ||
ReflectDefineProperty(redValue, key, redDescriptor); | ||
} | ||
} | ||
} | ||
function canRedPropertyBeTamed(redValue, key) { | ||
const redDescriptor = ReflectGetOwnPropertyDescriptor(redValue, key); | ||
// TODO: what about writable - non-configurable? | ||
return isUndefined(redDescriptor) || isTrue(redDescriptor.configurable); | ||
} | ||
//# sourceMappingURL=environment.js.map |
import { isUndefined, ObjectCreate, WeakMapCreate, WeakMapSet, WeakMapGet, SetCreate, SetHas, ownKeys, getOwnPropertyDescriptors, } from "./shared.js"; | ||
const cachedReflectiveIntrinsicsMap = WeakMapCreate(); | ||
export const ESGlobalKeys = SetCreate([ | ||
const ESGlobalKeys = SetCreate([ | ||
// *** 18.1 Value Properties of the Global Object | ||
@@ -35,3 +35,5 @@ 'Infinity', | ||
'Object', | ||
'Promise', | ||
// Allow Blue `Promise` constructor to overwrite the Red one so that promises | ||
// created by the `Promise` constructor or APIs like `fetch` will work. | ||
//'Promise', | ||
'Proxy', | ||
@@ -68,2 +70,3 @@ 'RangeError', | ||
const ReflectiveIntrinsicObjectNames = [ | ||
'Array', | ||
'Error', | ||
@@ -79,18 +82,2 @@ 'EvalError', | ||
]; | ||
export const UndeniableGlobalNames = SetCreate([ | ||
'Array', | ||
'Boolean', | ||
'Error', | ||
'EvalError', | ||
'Function', | ||
'Object', | ||
'Promise', | ||
'RangeError', | ||
'ReferenceError', | ||
'RegExp', | ||
'String', | ||
'SyntaxError', | ||
'TypeError', | ||
'URIError', | ||
]); | ||
function getReflectiveIntrinsics(global) { | ||
@@ -110,3 +97,3 @@ let reflectiveIntrinsics = WeakMapGet(cachedReflectiveIntrinsicsMap, global); | ||
} | ||
export function linkIntrinsics(registry, blueGlobalThis, redGlobalThis) { | ||
export function linkIntrinsics(env, blueGlobalThis, redGlobalThis) { | ||
// remapping intrinsics that are realm's agnostic | ||
@@ -119,4 +106,4 @@ const blueIntrinsics = getReflectiveIntrinsics(blueGlobalThis); | ||
const red = redIntrinsics[name]; | ||
registry.setRefMapEntries(red, blue); | ||
registry.setRefMapEntries(red.prototype, blue.prototype); | ||
env.setRefMapEntries(red, blue); | ||
env.setRefMapEntries(red.prototype, blue.prototype); | ||
} | ||
@@ -133,5 +120,7 @@ } | ||
// avoid overriding ECMAScript global names that correspond | ||
// to undeniable intrinsics. This guarantee that those entries | ||
// to global intrinsics. This guarantee that those entries | ||
// will be ignored if present in the endowments object. | ||
if (!SetHas(UndeniableGlobalNames, key)) { | ||
// TODO: what if the intent is to polyfill one of those | ||
// intrinsics? | ||
if (!SetHas(ESGlobalKeys, key)) { | ||
to[key] = endowmentsDescriptors[key]; | ||
@@ -142,2 +131,5 @@ } | ||
} | ||
export function isIntrinsicGlobalName(key) { | ||
return SetHas(ESGlobalKeys, key); | ||
} | ||
//# sourceMappingURL=intrinsics.js.map |
import { SecureEnvironment } from "./environment.js"; | ||
import { getOwnPropertyDescriptors, construct, ErrorCreate } from "./shared.js"; | ||
import { runInNewContext } from 'vm'; | ||
import { getFilteredEndowmentDescriptors, linkIntrinsics } from "./intrinsics.js"; | ||
// note: in a node module, the top-level 'this' is not the global object | ||
@@ -11,2 +11,3 @@ // (it's *something* but we aren't sure what), however an indirect eval of | ||
const redGlobalThis = runInNewContext(unsafeGlobalEvalSrc); | ||
const endowmentsDescriptors = getFilteredEndowmentDescriptors(endowments || {}); | ||
const { eval: redIndirectEval } = redGlobalThis; | ||
@@ -19,4 +20,5 @@ const blueGlobalThis = globalThis; | ||
}); | ||
linkIntrinsics(env, blueGlobalThis, redGlobalThis); | ||
// remapping globals | ||
env.remap(redGlobalThis, blueGlobalThis, endowments && getOwnPropertyDescriptors(endowments)); | ||
env.remap(redGlobalThis, blueGlobalThis, endowmentsDescriptors); | ||
return (sourceText) => { | ||
@@ -27,18 +29,3 @@ try { | ||
catch (e) { | ||
// This error occurred when the blue realm attempts to evaluate a | ||
// sourceText into the sandbox. By throwing a new blue error, which | ||
// eliminates the stack information from the sandbox as a consequence. | ||
let blueError; | ||
const { message, constructor } = e; | ||
try { | ||
const blueErrorConstructor = env.getBlueRef(constructor); | ||
// the constructor must be registered (done during construction of env) | ||
// otherwise we need to fallback to a regular error. | ||
blueError = construct(blueErrorConstructor, [message]); | ||
} | ||
catch (_a) { | ||
// in case the constructor inference fails | ||
blueError = ErrorCreate(message); | ||
} | ||
throw blueError; | ||
throw env.getBlueValue(e); | ||
} | ||
@@ -45,0 +32,0 @@ }; |
@@ -224,17 +224,3 @@ export const serializedRedEnvSourceText = (function redEnvFactory(blueEnv, hooks) { | ||
// we eliminates the stack information from the blue realm as a consequence. | ||
let redError; | ||
const { message, constructor } = e; | ||
try { | ||
// the error constructor must be a blue error since it occur when calling | ||
// a function from the blue realm. | ||
const redErrorConstructor = blueEnv.getRedRef(constructor); | ||
// the red constructor must be registered (done during construction of env) | ||
// otherwise we need to fallback to a regular error. | ||
redError = construct(redErrorConstructor, [message]); | ||
} | ||
catch (_a) { | ||
// in case the constructor inference fails | ||
redError = new Error(message); | ||
} | ||
throw redError; | ||
throw getRedValue(e); | ||
} | ||
@@ -258,17 +244,3 @@ return getRedValue(blue); | ||
// we eliminates the stack information from the blue realm as a consequence. | ||
let redError; | ||
const { message, constructor } = e; | ||
try { | ||
// the error constructor must be a blue error since it occur when calling | ||
// a function from the blue realm. | ||
const redErrorConstructor = blueEnv.getRedRef(constructor); | ||
// the red constructor must be registered (done during construction of env) | ||
// otherwise we need to fallback to a regular error. | ||
redError = construct(redErrorConstructor, [message]); | ||
} | ||
catch (_a) { | ||
// in case the constructor inference fails | ||
redError = new Error(message); | ||
} | ||
throw redError; | ||
throw getRedValue(e); | ||
} | ||
@@ -599,3 +571,3 @@ return getRedValue(blue); | ||
// is coerced to a string. | ||
.replace('{', `{'use strict'`); | ||
.replace('{', `{'use strict';`); | ||
//# sourceMappingURL=red.js.map |
@@ -37,79 +37,2 @@ export const { isArray: ArrayIsArray } = Array; | ||
export const emptyArray = []; | ||
export const ESGlobalKeys = SetCreate([ | ||
// *** 18.1 Value Properties of the Global Object | ||
'Infinity', | ||
'NaN', | ||
'undefined', | ||
// *** 18.2 Function Properties of the Global Object | ||
'eval', | ||
'isFinite', | ||
'isNaN', | ||
'parseFloat', | ||
'parseInt', | ||
'decodeURI', | ||
'decodeURIComponent', | ||
'encodeURI', | ||
'encodeURIComponent', | ||
// *** 18.3 Constructor Properties of the Global Object | ||
'Array', | ||
'ArrayBuffer', | ||
'Boolean', | ||
'DataView', | ||
'Date', | ||
'Error', | ||
'EvalError', | ||
'Float32Array', | ||
'Float64Array', | ||
'Function', | ||
'Int8Array', | ||
'Int16Array', | ||
'Int32Array', | ||
'Map', | ||
'Number', | ||
'Object', | ||
// Allow Blue `Promise` constructor to overwrite the Red one so that promises | ||
// created by the `Promise` constructor or APIs like `fetch` will work. | ||
//'Promise', | ||
'Proxy', | ||
'RangeError', | ||
'ReferenceError', | ||
'RegExp', | ||
'Set', | ||
'SharedArrayBuffer', | ||
'String', | ||
'Symbol', | ||
'SyntaxError', | ||
'TypeError', | ||
'Uint8Array', | ||
'Uint8ClampedArray', | ||
'Uint16Array', | ||
'Uint32Array', | ||
'URIError', | ||
'WeakMap', | ||
'WeakSet', | ||
// *** 18.4 Other Properties of the Global Object | ||
'Atomics', | ||
'JSON', | ||
'Math', | ||
'Reflect', | ||
// *** Annex B | ||
'escape', | ||
'unescape', | ||
// *** ECMA-402 | ||
'Intl', | ||
]); | ||
// These are foundational things that should never be wrapped but are equivalent | ||
// TODO: revisit this list. | ||
export const ReflectiveIntrinsicObjectNames = [ | ||
'Array', | ||
'Object', | ||
'Function', | ||
'URIError', | ||
'TypeError', | ||
'SyntaxError', | ||
'ReferenceError', | ||
'RangeError', | ||
'EvalError', | ||
'Error', | ||
]; | ||
//# sourceMappingURL=shared.js.map |
{ | ||
"name": "@caridy/sjs", | ||
"version": "0.2.13", | ||
"version": "0.2.14", | ||
"description": "Experimental JS Library to create a sandboxed JavaScript environment in browsers and node", | ||
@@ -40,3 +40,3 @@ "module": "lib/index.js", | ||
"ttypescript": "^1.5.10", | ||
"typescript": "^3.8.3" | ||
"typescript": "^3.9.6" | ||
}, | ||
@@ -43,0 +43,0 @@ "engines": { |
@@ -17,4 +17,4 @@ import { RedObject, RedFunction, BlueFunction, BlueObject, RedProxyTarget, BlueValue, RedValue, MembraneBroker, DistortionMap, RedProxy, BlueProxy, BlueProxyTarget } from './types'; | ||
setRefMapEntries(red: RedObject, blue: BlueObject): void; | ||
remap(redValue: RedValue, blueValue: BlueValue, blueDescriptors?: PropertyDescriptorMap): void; | ||
remap(redValue: RedValue, blueValue: BlueValue, blueDescriptors: PropertyDescriptorMap): void; | ||
} | ||
export {}; |
@@ -1,5 +0,4 @@ | ||
import { SandboxRegistry } from './registry'; | ||
export declare const ESGlobalKeys: any; | ||
export declare const UndeniableGlobalNames: any; | ||
export declare function linkIntrinsics(registry: SandboxRegistry, blueGlobalThis: typeof globalThis, redGlobalThis: typeof globalThis): void; | ||
import { SecureEnvironment } from './environment'; | ||
export declare function linkIntrinsics(env: SecureEnvironment, blueGlobalThis: typeof globalThis, redGlobalThis: typeof globalThis): void; | ||
export declare function getFilteredEndowmentDescriptors(endowments: object): PropertyDescriptorMap; | ||
export declare function isIntrinsicGlobalName(key: PropertyKey): boolean; |
@@ -40,3 +40,1 @@ export declare const ArrayIsArray: (arg: any) => arg is any[]; | ||
export declare const emptyArray: []; | ||
export declare const ESGlobalKeys: any; | ||
export declare const ReflectiveIntrinsicObjectNames: string[]; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
80
270629
3690