You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a description for an alternate Compartment sandboxing approach relying on static analysis and code generation.
The generated code wraps the untrusted code and no transformation of the untrusted code is required.
This construction does not require a with-statement or a Proxy and is strict-mode compatible. Feral eval is optional and can be replaced with templating.
This approach may be useful when working in situations:
that require strict mode
where Proxy or with-statement are not available
where the performance impact of Proxy or with-statement is prohibitive
SES shim:
SES Sandboxing is composed from with-statement + proxy + eval
however:
you can replace eval with templating
you can replace with-statement + proxy with static analysis and code generation
SES Sandboxing shim uses the with-statement for both of its primary sandboxing tasks:
scope denial of variables / references in above scope (uses a proxy)
scope population of compartment globalThis properties
This approach:
The scope denial uses a Proxy in order to handle any possible uttered value. However if you have access to the code you can perform static analysis on it and as long as direct eval is not available, all potential utterances can be trivially known. Given a list of utterances you can prepare code to prevent them from accessing values in higher scopes.
// deny all unallowed utterancesconstfetch=undefined;// untrusted code here{// not able to access global fetchfetch();}
The scope population of Compartment properties can be accomplished by creating variables in scope. The synchronization of values between the Compartment globalThis properties and the variables can be accomplished by using setters and getters on the globalThis. Only utterable properties need to be synchronized, un-utterable properties can be defined directly on the Compartment globalThis. While the untrused code would be able to modify the property descriptors on the Compartment globalThis, this would only reduce shim fidelity and not reduce security.
// expose utterable globals as variablesletxyz=sandboxKit.endowments.xyz;constglobalThis={// expose allowed un-uttered (and un-utterable) endowments
...sandboxKit.endowments,// synchronized uttered globals with variablesgetxyz(){returnxyz;},setxyz(value){xyz=value;},}{// deny access to internal propertyconstsandboxKit=undefined;// untrusted code here{// variables are synchronized with their globalThis propertiesglobalThis.xyz=123;console.log(xyz)// 123// un-uttered properties are accessibleconsole.log(globalThis['abc'])// as endowed// un-utterable properties are also accessibleconsole.log(globalThis['api-key'])// as endowed}}
Endowments do not need to be known at the time of code generation. We don't need to distinguish between allowed and disallowed utterances.
// expose utterable globals as variablesletfetch=sandboxKit.endowments.fetch;letxyz=sandboxKit.endowments.xyz;constglobalThis={// expose allowed un-uttered (and un-utterable) endowments
...sandboxKit.endowments,// utterable globalsgetxyz(){returnxyz;},setxyz(value){xyz=value;},// if fetch is undefined in endowments it will be present on// the Compartment globalThis, but be undefinedgetfetch(){returnfetch;},setfetch(value){fetch=value;},}
Multiple evaluations in the same Compartment can be accomplished with some restrictions. In order to have a single Compartment globalThis object and have it correctly synchronize its properties with global variables, the evaluations must be evaluated within the same scope below the Compartment scope machinery.
Dynamic evaluations (requested at runtime, eg indirect eval) in the same compartment may be possible with some restrictions. Each evaluation may be unable to have utterances in common. ⚠️ Further research required.
// expose utterable globals as variablesletxyz=sandboxKit.endowments.xyz;constglobalThis={// expose allowed un-uttered (and un-utterable) endowments
...sandboxKit.endowments,// utterable globalsgetxyz(){returnxyz;},setxyz(value){xyz=value;},}// dynamic eval space{sandboxKit.exports.safeEval=function(code){constutterances=getUtterances(code)constsafeCode=wrapCodeUtterancesAsVars(code,utterances)defineUtterancesOnGlobalThis(utterances,globalThis)feralEvalOnce(safeCode)// may be able to get a new further nested safeEval here that evaluates// in a scope with the new utterances in scope}}
Limitations:
Relies on correct static analysis of utterances
Relies on correct code generation of wrapper
Relies on SES lockdown, though not discussed here
Untrusted code can reduce shim fidelity and detect shim environment
Relies on rejecting dynamic import expressions, as does the SES shim
Fidelity issue: Only supports values (not getters/setters) for global variables. Eg. can not support the browser's location = '(target url)'
This discussion was converted from issue #1561 on January 10, 2024 00:09.
Heading
Bold
Italic
Quote
Code
Link
Numbered list
Unordered list
Task list
Attach files
Mention
Reference
Menu
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
This is a description for an alternate Compartment sandboxing approach relying on static analysis and code generation.
The generated code wraps the untrusted code and no transformation of the untrusted code is required.
This construction does not require a with-statement or a Proxy and is strict-mode compatible. Feral eval is optional and can be replaced with templating.
This approach may be useful when working in situations:
SES shim:
SES Sandboxing is composed from with-statement + proxy + eval
however:
SES Sandboxing shim uses the with-statement for both of its primary sandboxing tasks:
This approach:
The scope denial uses a Proxy in order to handle any possible uttered value. However if you have access to the code you can perform static analysis on it and as long as direct eval is not available, all potential utterances can be trivially known. Given a list of utterances you can prepare code to prevent them from accessing values in higher scopes.
The scope population of Compartment properties can be accomplished by creating variables in scope. The synchronization of values between the Compartment globalThis properties and the variables can be accomplished by using setters and getters on the globalThis. Only utterable properties need to be synchronized, un-utterable properties can be defined directly on the Compartment globalThis. While the untrused code would be able to modify the property descriptors on the Compartment globalThis, this would only reduce shim fidelity and not reduce security.
Endowments do not need to be known at the time of code generation. We don't need to distinguish between allowed and disallowed utterances.
Multiple evaluations in the same Compartment can be accomplished with some restrictions. In order to have a single Compartment globalThis object and have it correctly synchronize its properties with global variables, the evaluations must be evaluated within the same scope below the Compartment scope machinery.
Dynamic evaluations (requested at runtime, eg indirect eval) in the same compartment may be possible with some restrictions. Each evaluation may be unable to have utterances in common.⚠️ Further research required.
Limitations:
location = '(target url)'
Beta Was this translation helpful? Give feedback.
All reactions