Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ses): hostEvaluators lockdown option #2723

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

leotm
Copy link
Contributor

@leotm leotm commented Feb 14, 2025

Refs: #1891 (tracker), tested on #2334, Endo Sync: 2025-01-29

TODO

  • add new lockdown option
    • legacyHermesTaming: safe (default), unsafe
    • hostEvaluators: all (default), none (csp), no-direct (hermes)
    • fail on safeEval (default) and no-direct (hermes) early
      • i.e. require unsafeEval and no-direct on hermes
    • add assertions
    • add warnings
    • update types
    • update ses/error-codes/SES_DIRECT_EVAL.md
    • update lockdown.md
    • refactor assertDirectEvalAvailable (return results, now used for further assertions)
    • ensure backwards compatibility (no option provided)
      • before: worked under CSP (allowed in assertDirectEvalAvailable try/catch)
      • after: { hostEvaluators: 'none' } now explicitly required
      • endo sync: 4th option undefined, warn to use the new option
    • attempted to disable compartment-shim when bundling ses for hermes (not trivial)
    • disable: globalThis Compartment and testCompartmentHooks
    • disable compartmentInstance.evaluate instead of deleting compartment
      • makeCompartmentConstructor setGlobalObjectEvaluators safeEvaluate is the default (no evalTaming yet)
      • CompartmentPrototype.evaluate returns compartmentEvaluate safeEvaluate is the default (no evalTaming)
      • endo sync: throw error on usage
  • test on hermes
    • evalTaming: noEval/safeEval/unsafeEval
    • hostEvaluators: all/none/no-direct
    • post-lockdown: eval(42), eval('42'), Function('return 42')()
  • check prev test branch (below)
  • fix typos

Follow-up: new Compartment() fails on removeUnpermittedIntrinsics at

  • Tolerating undeletable intrinsics.%CompartmentPrototype%.importNow.prototype === undefined
  • Uncaught TypeError: property is not configurable

test branch https://github.com/endojs/endo/tree/ses-hermes-p2 (from #2334)

  • yarn build:hermes bundle ses for hermes
  • yarn test:hermes run ses/test/_hermes-smoke.js

Hermes eval behaviour on bin/hermesc (standalone compiler) and bin/hermes (vm, eshost)

// Hermes direct eval cannot access or modify variables within its closure scope (sloppy and indirect)

eval = globalThis.eval; // remains direct eval, compiler: "error: invalid assignment left-hand side"

// calling globalThis.eval(...) uses indirect eval

// calling eval on Hermes VM emits: "warning: Direct call to eval(), but lexical scope is not supported."

print(eval(1 + 1));
print(eval("1 + 1"));

function a(eval, TEST) {
  // compiler: "error: cannot declare 'eval'"
  eval("TEST=true");
  return TEST;
}
print(a(eval, false)); // vm: false

function b(eval) {
  // compiler: "error: cannot declare 'eval'"
  var TEST = false;
  eval("TEST=true");
  return TEST;
}
print(b(eval)); // vm: false

function c() {
  var TEST = false;
  eval("TEST=true");
  return TEST;
}
print(c()); // vm: false

function d() {
  var TEST2 = 42;
  return eval("TEST2");
}
print(d()); // ReferenceError: Property 'TEST2' doesn't exist

// Note: eshost only runs bin/hermes (vm), not bin/hermesc (compiler)

@leotm leotm changed the title feat(ses): Hermes eval and compartment taming feat(ses): Hermes eval and compartment taming lockdown option Feb 17, 2025
if (legacyHermesTaming === 'unsafe') {
globalThis.testCompartmentHooks = undefined;
// @ts-ignore Compartment does exist on globalThis
delete globalThis.Compartment;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IF an environment is lacking the direct eval support, we won't be able to provide the compartment evaluation capabilities, but we can have a constructor that creates instances with fresh tamed copies of compartmentInstance.globalThis that we use in bundling (lavamoat's webpack and browserify) and might want to use in Hermes in the future. So I'm not 100% sure if we want to delete Compartment or make evaluation related functionality throw a descriptive error from the implementation that depends on direct eval.

Copy link
Contributor Author

@leotm leotm Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so currently deleting compartment breaks hermes on webpack (repack)? 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and only disabling compartmentInstance.evaluate would be more ideal?
to still be able to construct instances (with fresh tamed copies of compartmentInstance.globalThis)

Copy link
Contributor Author

@leotm leotm Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see, bundling ses without compartment-shim proved difficult, partial compartments seem a better alternative

// Creating a compartment should be possible in no-eval environments
// It also allows more global constants to be captured by the optimizer

*/
export const makeFunctionConstructor = safeEvaluate => {
export const makeFunctionConstructor = evaluator => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIR our conclusion was, as long as we don't want compartmentalization, the indirect eval used for tamed eval and tamed Function would transparently work and we don't need to throw an error when that's used.

Copy link
Contributor Author

@leotm leotm Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was more a refactor since the arg name and JSDoc both assume safeEvaluate which is incorrect (it could be noEvaluate)

Copy link
Contributor Author

@leotm leotm Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regardless of compartmentalization, the (hermes) indirect eval used for tamed eval (string args only) and tamed Function doesn't transparently work, since safeEval eventually calls makeSafeEvaluator, which throws Hermes' ambiguousUncaught SyntaxError: 2:5:invalid statement encountered, so i think it's better to throw an error that safeEval requires an engine that supports with statement

@leotm leotm force-pushed the ses-hermes-lockdown-taming branch 5 times, most recently from 69a97f5 to 7e2b07a Compare February 19, 2025 17:27
@leotm leotm changed the base branch from ses-hermes-fn-caller-arguments-permits-check to master February 19, 2025 17:29
@leotm leotm changed the base branch from master to ses-hermes-fn-caller-arguments-permits-check February 19, 2025 17:30
@leotm leotm force-pushed the ses-hermes-lockdown-taming branch 3 times, most recently from fa54256 to 5793270 Compare February 19, 2025 18:13
@leotm leotm force-pushed the ses-hermes-lockdown-taming branch 3 times, most recently from 1e87397 to 063ec33 Compare February 19, 2025 19:04
@leotm leotm force-pushed the ses-hermes-lockdown-taming branch 3 times, most recently from 8cf7a5e to d0269ff Compare February 24, 2025 17:08
@leotm leotm changed the title feat(ses): Hermes eval and compartment taming lockdown option feat(ses): hostEvaluators lockdown option Feb 25, 2025
@leotm leotm changed the title feat(ses): hostEvaluators lockdown option feat(ses): hostEvaluators lockdown option Feb 25, 2025
@leotm leotm force-pushed the ses-hermes-lockdown-taming branch 2 times, most recently from 819c22f to de25e68 Compare February 25, 2025 16:41
@leotm leotm force-pushed the ses-hermes-lockdown-taming branch from de25e68 to 13989af Compare February 25, 2025 17:30
@leotm leotm force-pushed the ses-hermes-lockdown-taming branch from 13989af to 88ddc37 Compare February 26, 2025 14:14
@leotm leotm changed the base branch from ses-hermes-fn-caller-arguments-permits-check to master February 26, 2025 14:39
@leotm leotm force-pushed the ses-hermes-lockdown-taming branch 5 times, most recently from f85b1c8 to f974d83 Compare February 27, 2025 18:33
@leotm leotm force-pushed the ses-hermes-lockdown-taming branch from f974d83 to 70fa62e Compare February 27, 2025 18:38
@@ -189,6 +188,10 @@ export const repairIntrinsics = (options = {}) => {
/** @param {string} debugName */
debugName => debugName !== '',
),
hostEvaluators = /** @type { 'all' | 'none' | 'no-direct' } */ (
// TODO: Breaking change, ensure backwards compatibility under CSP.
getenv('LOCKDOWN_HOST_EVALUATORS', 'all')
Copy link
Contributor Author

@leotm leotm Feb 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undefined as default value not currently supported by getEnvironmentOption

@leotm leotm force-pushed the ses-hermes-lockdown-taming branch from 70fa62e to 4f714c0 Compare February 27, 2025 22:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants