docs(ses): Clarify default powers of Compartments
MODIFY
packages/ses/README.md
packages/ses/README.md
@@ -30,20 +30,30 @@ npm install ses
### Lockdown
SES introduces the `lockdown()` function.
-Calling `lockdown()` alters the surrounding execution enviornment, or
+Calling `lockdown()` alters the surrounding execution environment, or
**realm**, such that no two programs running in the same realm can observe or
interfere with each other until they have been introduced.
-To this end, `lockdown()` freezes all objects accessible to any program in the
-realm.
+To do this, `lockdown()` tamper-proofs all of the JavaScript intrinsics, to
+prevent **prototype pollution**.
+After that, no program can subvert the methods of these objects (preventing
+some **man in the middle attacks**).
+Also, no program can use these mutable objects to pass notes to parties that
+haven't been expressly introduced (preventing some **covert communication
+channels**).
+
+Lockdown freezes all objects accessible to any program in the realm.
The set of accessible objects includes but is not limited to: `globalThis`,
`[].__proto__`, `{}.__proto__`, `(() => {}).__proto__` `(async () =>
{}).__proto__`, and the properties of any accessible object.
-The `lockdown()` function also **tames** some of those accessible objects
-that have powers that would otherwise allow programs to observe or interfere
-with one another like clocks, random number generators, and regular
-expressions.
+The `lockdown()` function also **tames** some objects including regular
+expressions, locale methods, and errors.
+A tamed `RexExp` does not have the deprecated `compile` method.
+A tamed error does not have a V8 `stack`, but the `console` can still see the
+stack.
+Lockdown replaces locale methods like `String.prototype.localeCompare` with
+lexical versions that do not reveal the user locale.
```js
import 'ses';
@@ -55,6 +65,10 @@ console.log(Object.isFrozen([].__proto__));
// true
```
+Lockdown does not erase any powerful objects from the initial global scope.
+Instead, **Compartments** give complete control over what powerful objects
+exist for client code.
+
### Harden
SES introduces the `harden` function.
@@ -95,8 +109,8 @@ SES introduces the `Compartment` constructor.
A compartment is an evaluation and execution environment with its own
`globalThis` and wholly independent system of modules, but otherwise shares
the same batch of intrinsics like `Array` with the surrounding compartment.
-The concept of a compartment implies the existence of a "start compartment",
-the initial execution environment of a **realm**.
+The concept of a compartment implies an **initial compartment**, the initial
+execution environment of a **realm**.
In the following example, we create a compartment endowed with a `print()`
function on `globalThis`.
@@ -137,6 +151,63 @@ c1.globalThis === c2.globalThis; // false
c1.globalThis.JSON === c2.globalThis.JSON; // true
```
+The global scope of every compartment includes a shallow, specialized copy of
+the JavaScript intrinsics, omitting `Date.now` and `Math.random`.
+Comaprtments leave these out since they can be used as covert communication
+channels between programs.
+However, a compartment may be expressly given access to these objects
+through:
+
+* the first argument to the compartment constructor or
+* by assigning them to the compartment's `globalThis` after construction.
+
+```js
+const powerfulCompartment = new Compartment({ Math });
+powerfulCompartment.globalThis.Date = Date;
+```
+
+### Compartment + Lockdown
+
+Together, Compartment and lockdown isolate client code in an environment with
+limited powers and communication channels.
+A compartment has only the capabilities it is expressly given and cannot modify
+any of the shared intrinsics.
+Every compartment gets its own globals, including such objects as the
+`Function` constructor.
+Yet, compartment and lockdown do not break `instanceof` for any of these
+intrinsics types!
+
+All of the evaluators in one compartment are captured by that compartment's
+global scope, including `Function`, indirect `eval`, dynamic `import`, and its
+own `Compartment` constructor for child compartments.
+For example, the `Function` constructor in one compartment creates functions
+that evaluate in the global scope of that compartment.
+
+```js
+const f = new Function("return this");
+f() === globalThis
+// true
+```
+
+Lockdown prepares for compartments with separate globals by freezing
+their shared prototypes and replacing their prototype constructors
+with powerless dummies.
+So, `Function` is different in two compartments, `Function.prototype` is the
+same, and `Function` is not the same as `Function.prototype.constructor`.
+The `Function.prototype.constructor` can only throw exceptions.
+So, a function passed between compartments does not carry access to
+its compartment's globals along with it.
+Yet, `f instanceof Function` works, even when `f` and `Function` are
+from different compartments.
+
+The `globalThis` in each compartment is mutable.
+This can and should be frozen before running any dynamic code in that
+compartment, yet is not strictly necessary if the compartment only
+runs code from a single party.
+
+
+### Modules
+
Any code executed within a compartment shares a set of module instances.
For modules to work within a compartment, the creator must provide
a `resolveHook` and an `importHook`.

docs(ses): Clarify default powers of Compartments (#494)