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): Vetted shims #1705

Merged
merged 4 commits into from
Aug 28, 2023
Merged

feat(ses): Vetted shims #1705

merged 4 commits into from
Aug 28, 2023

Conversation

kriskowal
Copy link
Member

Closes #422

This change separates out the internal phases of lockdown: repairIntrinsics() and hardenIntrinsics().

@kriskowal
Copy link
Member Author

kriskowal commented Jul 29, 2023

Developer log (draft):

I’m attempting to make it clearer that ses is itself a stack of shims, so I’ve refactored index.js to reflect that and hope to break it down further. I’ve also attempted to factor x.js and x-shim.js modules such that x.js implements the behavior in a way that might be used without shimming.

In the course of doing this, it became clear that the errors sub-section should not be using ambient types. I’ve attempted to correct this. I’m stopping for now at this yak:

$ tsc
types.test-d.ts:91:1 - error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation.

91 assert.typeof(10.1, 'number');
...

@kriskowal kriskowal force-pushed the kriskowal/422-vetted-shims branch from b049ea5 to c619f09 Compare August 19, 2023 00:22
@kriskowal
Copy link
Member Author

I managed to getting vetted shims implemented without refactoring ambient types. Or rather, I managed to keep that work out of this pull request for purposes of limiting the scope of review and will submit that cleanup separately.

@kriskowal
Copy link
Member Author

For reviewers: I’ve created an initial commit that renames lockdown-shim.js and compartment-shim.js to lockdown.js and compartment.js so that the subsequent commit’s diff clearly illustrates that all of the shim behavior was extracted from those modules and moved into the (new) adjacent -shim.js layering.

@kriskowal kriskowal marked this pull request as ready for review August 19, 2023 00:38
@kriskowal kriskowal requested a review from erights August 19, 2023 00:38
Comment on lines -23 to -31
/** getThis returns globalThis in sloppy mode or undefined in strict mode. */
function getThis() {
return this;
}

if (getThis()) {
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_NO_SLOPPY.md
throw TypeError(`SES failed to initialize, sloppy mode (SES_NO_SLOPPY)`);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Moves to a new assert-sloppy-mode.js, as imported by lockdown-shim.js to ensure it runs first and fails fast.


assign(globalThis, {
lockdown,
Compartment,
Copy link
Member Author

Choose a reason for hiding this comment

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

Moves to compartment-shim.js

);

assign(globalThis, {
lockdown,
Copy link
Member Author

Choose a reason for hiding this comment

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

Moves to lockdown-shim.js

assign(globalThis, {
lockdown,
Compartment,
assert,
Copy link
Member Author

Choose a reason for hiding this comment

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

Moves to assert-shim.js

Comment on lines -33 to -39
const markVirtualizedNativeFunction = tameFunctionToString();

const Compartment = makeCompartmentConstructor(
makeCompartmentConstructor,
getGlobalIntrinsics(globalThis),
markVirtualizedNativeFunction,
);
Copy link
Member Author

Choose a reason for hiding this comment

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

Moves to compartment-shim.js

Comment on lines 21 to 34
globalThis.repairIntrinsics = options => {
const hardenIntrinsics = repairIntrinsics(options);
// Reveal hardenIntrinsics after repairs.
globalThis.hardenIntrinsics = () => {
// Reveal harden after hardenIntrinsics.
// Harden is dangerous before hardenIntrinsics because hardening just
// about anything will inadvertently render intrinsics irreparable.
// Also, for modules that must work both before or after lockdown (code
// that is portable between JS and SES), the existence of harden in global
// scope signals whether such code should attempt to use harden in the
// defense of its own API.
// @ts-ignore harden not yet recognized on globalThis.
globalThis.harden = hardenIntrinsics();
};
Copy link
Member Author

Choose a reason for hiding this comment

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

I’ve moved all of the globalThis assignment behaviors into the shim to decontaminate the non-shim modules.

So, note that the global repairIntrinsics doesn’t return hardenIntrinsics and the global hardenIntrinsics does not return harden, but the internal versions perform this cascade.

hardenIntrinsics did not previously return harden.

Copy link
Contributor

@leotm leotm left a comment

Choose a reason for hiding this comment

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

we should update docs too right (mb better as follow-up)

so lockdown and harden relevant parts then add repair section

can prob reuse bits from the well-written issue description too

@kriskowal kriskowal force-pushed the kriskowal/422-vetted-shims branch from db4a42e to fb969cb Compare August 21, 2023 20:12
@kriskowal
Copy link
Member Author

I’ve added a commit with documentation changes and the update for the change log. Thank you!

Copy link
Contributor

@erights erights left a comment

Choose a reason for hiding this comment

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

just doc comments so far. rest coming but will take longer.

hardenIntrinsics();
```

And, an application that choses to call these also has the option of running
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
And, an application that choses to call these also has the option of running
An application that choses to call these also has the option of running

I'll do things like a jarring initial "And, " when it adds enough meaning to be worth it. I don't think it is here.

Comment on lines 306 to 310
The globals in a child compartment include the shared intrinsics including
`harden` and a batch of evaluators that run programs that will also be
confined to the compartment including `eval`, `Function`, and `Compartment`
itself.
Compartments also load and confine modules.
Copy link
Contributor

@erights erights Aug 21, 2023

Choose a reason for hiding this comment

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

Need to be explicit that this is the post-repairIntrinsics behavior of Compartment. In our system as it is now, or as it is as of this PR, what is the status of Compartment before repairIntrinsics? I'm probably happy with any answer for now, since I only really care about after repairIntrinsics. But we should be explicit somewhere. If we are explicit elsewhere, can we link to it from here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Compartment exists before repairIntrinsics and the only current difference is that the intrinsics are not hardened. Compartments constructed before repairIntrinsics do not guarantee confinement even after repairIntrinsics.

Comment on lines 39 to 52
```js
import './non-ses-code-before-lockdown.js';
import './ses-repair-intrinsics.js'; // calls repairIntrinsics.
import './vetted-shim.js';
import './ses-harden-intrinsics.js'; // calls repairIntrinsics.
import './ses-code-after-lockdown.js';
```
Copy link
Contributor

Choose a reason for hiding this comment

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

It is even more important that this example import sequence appear in the guide. This is the use pattern we should generally encourage.

Copy link
Contributor

@erights erights left a comment

Choose a reason for hiding this comment

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

What about https://github.com/endojs/endo/blob/master/packages/ses/docs/lockdown.md ?

Should "Lockdown options" be renamed? What about LOCKDOWN_* environment variables?

IMO the env vars do not need to be renamed. The doc should at least be clear about what these options mean in the new world.

@kriskowal
Copy link
Member Author

In the type definition, I took the liberty of renaming LockdownOptions to RepairOptions and leaving an alias for backward compatibility. I agree that we do not need to change the names of the environment variables, or even really call attention to repairIntrinsics() unless it’s needed.

@kriskowal
Copy link
Member Author

Question for reviewers: do we need to tackle making harden available after repairIntrinsics but before hardenIntrinsics with the queuing behavior? The heart of this question is whether it’s necessary and whether we can expect any environment to repair but not harden indefinitely.

On the question of necessity, a vetted shim could be divided into layers that run before and after hardenIntrinsics(). So, I’d argue it’s not necessary but might make for better ergonomics once we start implementing hardened shims.

I also don’t think repairing but not hardening intrinsics is coherent, but I might be forgetting something.

@erights
Copy link
Contributor

erights commented Aug 22, 2023

Question for reviewers: do we need to tackle making harden available after repairIntrinsics but before hardenIntrinsics with the queuing behavior? The heart of this question is whether it’s necessary and whether we can expect any environment to repair but not harden indefinitely.

On the question of necessity, a vetted shim could be divided into layers that run before and after hardenIntrinsics(). So, I’d argue it’s not necessary but might make for better ergonomics once we start implementing hardened shims.

I also don’t think repairing but not hardening intrinsics is coherent, but I might be forgetting something.

How we want to bridge harden per-vs-post hardenIntrinsics is still an open design issue.
Let's keep this PR conservative without painting ourselves into any corners. I think the best way to do that is for hardenIntrinsics to introduce harden for now, but with a note that you should not count on harden being absent prior to hardenIntrinsics. In the future, some attenuated form of harden may exist between repairIntrinsics and hardenIntrinsics.

If we already have an issue where we're discussing that open design issue, we should link to it.

@mhofman
Copy link
Contributor

mhofman commented Aug 22, 2023

Agreed with @erights. Let's not introduce some queuing behavior for harden that may not be part of the final design. I am ok with requiring shims to run a part post hardenIntrinsics for now. Maybe we should consider for harden to throw before hardenIntrinsics is called (which I believe is currently the behavior of XS's harden implementation before lockdown is called).

To be clear: a world where shims explicitly harden after hardenIntrinsics is forward compatible with a world where a pre-hardenIntrinsics vetted shim is able to use some version of harden and avoid running a post-hardenIntrinsics step.

@erights
Copy link
Contributor

erights commented Aug 22, 2023

Maybe we should consider for harden to throw before ...

Also fine with me. Even better because the diagnostic can help set expectations. My favorite word for such messages is "yet" ;)

@erights
Copy link
Contributor

erights commented Aug 22, 2023

OTOH, absence would make feature testing pleasant. I'm ok either way.

@kriskowal
Copy link
Member Author

I’m settling on the current state: presence of globalThis.harden is a reliable signal that it’s available for use. I think this is more likely to be consistent with the @endo/harden ponyfill @mhofman and I have been discussing, and is the more reversible option.

@erights
Copy link
Contributor

erights commented Aug 28, 2023

For reviewers: I’ve created an initial commit that renames lockdown-shim.js and compartment-shim.js to lockdown.js and compartment.js so that the subsequent commit’s diff clearly illustrates that all of the shim behavior was extracted from those modules and moved into the (new) adjacent -shim.js layering.

Thank you! I was reviewing the overall differences until I got to one of those. I'm surprised that github diff loses the mv behavior for the overall diffs, but it does. Your separation is a lifesaver.

Copy link
Contributor

@erights erights left a comment

Choose a reason for hiding this comment

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

LGTM!!!!

I love that we can get this into the next endo release. Awesome!

@@ -36,6 +36,10 @@ export interface LockdownOptions {
__hardenTaming__?: 'safe' | 'unsafe';
}

export type LockdownOptions = RepairOptions;
Copy link
Contributor

Choose a reason for hiding this comment

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

Please comment that we preserve LockdownOptions only for legacy compat. Perhaps deprecate?

@kriskowal kriskowal force-pushed the kriskowal/422-vetted-shims branch from 0069174 to 76787e7 Compare August 28, 2023 04:51
@kriskowal kriskowal enabled auto-merge August 28, 2023 04:51
@kriskowal kriskowal merged commit 7f1f331 into master Aug 28, 2023
@kriskowal kriskowal deleted the kriskowal/422-vetted-shims branch August 28, 2023 04:53
leotm added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 28, 2023
- from (dist): endojs/endo#1705
- eslintignore unneeded since cjs not js
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.

Vetted shims
4 participants