{"id":13482302,"url":"https://github.com/tc39/proposal-ses","last_synced_at":"2025-08-20T22:31:38.122Z","repository":{"id":54454820,"uuid":"39663010","full_name":"tc39/proposal-ses","owner":"tc39","description":"Draft proposal for SES (Secure EcmaScript)","archived":false,"fork":false,"pushed_at":"2021-02-18T22:19:19.000Z","size":14723,"stargazers_count":217,"open_issues_count":28,"forks_count":20,"subscribers_count":45,"default_branch":"master","last_synced_at":"2024-04-13T16:45:38.179Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tc39.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-24T23:21:49.000Z","updated_at":"2024-04-04T19:23:17.000Z","dependencies_parsed_at":"2022-08-13T16:10:40.229Z","dependency_job_id":null,"html_url":"https://github.com/tc39/proposal-ses","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tc39/proposal-ses","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-ses","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-ses/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-ses/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-ses/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tc39","download_url":"https://codeload.github.com/tc39/proposal-ses/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-ses/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271397954,"owners_count":24752639,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-20T02:00:09.606Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T17:01:00.692Z","updated_at":"2025-08-20T22:31:36.334Z","avatar_url":"https://github.com/tc39.png","language":"HTML","funding_links":[],"categories":["HTML"],"sub_categories":[],"readme":"# Draft Proposal for SES (Secure EcmaScript)\n\n* Most of the action on the SES-shim is happening at https://github.com/Agoric/SES-shim/tree/master/packages/ses .\n* Moddable is also directly building a SES machine as the primary configuration of the XS implementation of JS for embedded, as standardized in Ecma TC53.\n* The most relevant proposal these days is https://github.com/tc39/proposal-compartments which is much more up to date than this proposal repository is.\n* We should update this proposal repository with text from https://github.com/Agoric/SES-shim/tree/master/packages/ses which is much more current.\n* MetaMask and Agoric both have plugin architectures running on SES.\n* MetaMask and MetaMask's LavaMoat run on the SES-shim.\n* The Agoric framework runs on both on the SES-shim on Node, and increasingly on a version of XS called XSnap that also supports orthogonal persistence.\n\nNote that this proposal was previously called \"proposal-frozen-realms\". However, with progress on [proposal-realms](https://github.com/tc39/proposal-realms), the [realms-shim](https://github.com/Agoric/realms-shim), and the [ses-shim](https://github.com/Agoric/SES), we found we no longer needed to distinguish frozen-realms from SES. Most historical references to \"Frozen Realms\" are best interpreted as being about an older version of SES.\n\nChampions\n  - Mark S. Miller, Agoric\n  - JF Paradis, Agoric\n  - Caridy Patiño, Salesforce\n  - Patrick Soquet, Moddable\n  - Bradley Farias, GoDaddy, Node\n\n----\n\nThis document specifies \"compartments\", a concept focused on making _lightweight realms_ designed to be used with a shared _immutable realm_. The proposal here is intended to compose well with the various `Realm` proposals but is independent. These proposals each have utility without the other, and so can be proposed separately. However, together they have more power than each separately.\n\nWe motivate the SES API presented here with a variety of examples.\n\n### Status\n\nCurrent Stage:\n\n * __Stage 1__\n\n### External links\n\n[Moddable's Compartment API](https://github.com/Moddable-OpenSource/moddable/tree/public/examples/js/compartments), the direct ancestor to this proposal, implemented on the XS SES engine.\n\n[Making Javascript Safe and Secure](https://www.youtube.com/playlist?list=PLzDw4TTug5O25J5M3fwErKImrjOrqGikj) Talks by Mark S. Miller (Agoric), Peter Hoddie (Moddable), and Dan Finlay (MetaMask)\n\nPresentation to TC53 [Omit Needless Words](https://www.youtube.com/watch?v=aMHV7LCt8Es\u0026list=PLzDw4TTug5O0ywHrOz4VevVTYr6Kj_KtW)\n\n[LavaMoat - Securing your dependency graph](https://www.youtube.com/watch?v=pOTEJy_FqIA) by Kumavis (MetaMask)\n\nPresentation to Node Security [Securing EcmaScript](https://www.youtube.com/watch?v=9Snbss_tawI\u0026list=PLzDw4TTug5O0ywHrOz4VevVTYr6Kj_KtW)\n\n**Historical**\n\n[Automated Analysis of Security-Critical JavaScript APIs](https://research.google/pubs/pub37199/) by Ankur Taly Úlfar Erlingsson John C. Mitchell Mark S. Miller Jasvir Nagra\n\n[Frozen Realms: Draft Standard Support for Safer JavaScript\nPlugins](https://www.youtube.com/watch?v=tuMG7688Ndw)\nis an in-depth talk that covers the important ideas, but is very stale\nregarding specifics.\n\nThe [old Realms API proposal](https://gist.github.com/dherman/7568885) and the [current Realms proposal](https://github.com/tc39/proposal-realms). The original plan was to settle the Realms proposal first, but with the current approach, this is no longer required.\n\nThe original efforts to rebuild frozen realms on top of these Realms is:\n   * [Realms shim](https://github.com/Agoric/realms-shim)\n   * [SES shim](https://github.com/Agoric/SES)\n\n## Summary\n\nIn ECMAScript, a _realm_ consists of a global object and an associated set of _primordial objects_ -- mutable objects like `Array.prototype` that must exist before any code runs. Objects within a realm implicitly share these primordials and can therefore easily disrupt\neach other by _primordial poisoning_ -- modifying these objects to behave badly. This disruption may happen accidentally or maliciously. Today, in the browser, realms can be created via _same origin iframes_,\nand in Node via `vm` contexts. On creation, these realms are separate from each other because they share no mutable state. Because prototypes are mutable, each realm needs its own set, making this separation too expensive to be used at fine grain.\n\nRealms are currently not exposed directly to JavaScript but are represented in the specs by the _realm record_, of which the most important slots are the _intrinsics_, the _global object_, and the _global lexical environment_ (see [ECMA262 sections 8.2 Realms](https://tc39.es/ecma262/#sec-code-realms)).\n\nWe propose to add the concept of _compartments_, to designate _lightweight child realms_ inside a realm. Each compartment has its own global object and global lexical scope, but all compartments inside a given realm share their intrinsics. Separation is achieved by making the intrinsics immutable, preventing an object in one compartment\nfrom poisoning the prototypes used by the other compartments.\n\nThis means that each compartment consists of a new _global object_, and a new _global lexical environment_:\n\n| Record slots         | Realm          | Compartment       |\n| -------------------- | -------------- | ----------------- |\n| intrinsics           | mutable        | immutable, shared |\n| global object        | mutable        | mutable           |\n| global lexical scope | mutable        | mutable           |\n\nThe _compartment record_ is like a _realm record_, except that its _intrinsics_ slot points to the parent realm record. Everywhere the specs refers to the the realm record, the compartment record can be subsituted with no further changes.\n\n### The Compartment constructor\n\n**Superceded by the [Compartments](https://github.com/tc39/proposal-compartments) proposal.**\n\nWe propose a `Compartment` class, whose instances is a reification of the concept of \"compartment\" introduced above, for making multiple _lightweight child realms_ inside a given realm.\n\nThough initially separate, compartments can be brought into intimate contact with each other via global object and modules.\n\n```js\nclass Compartment {\n  constructor: (\n    endowments: object?,     // extra bindings added to the global object\n    moduleMap: object?,      // maps child specifier to parent specifier\n    options: object?         // including hooks like isDirectEvalHook\n  ) -\u003e object                // an exotic compartment object\n\n  get global -\u003e object       // access this compartment's global object\n\n  evaluate(                  // do a strict indirect eval in this compartment\n    src: stringable,\n    options: object?         // per-evaluation rather than per-compartment\n  ) -\u003e any\n\n  // same signature as dynamic import\n  async import(specifier: string) -\u003e promise\u003cModuleNamespace\u003e\n  importSync(specifier: string) -\u003e ModuleNamespace\n}\n```\n\nThe compartment constructor creates a new lightweight child realm with a new `global`, a new `eval` function, a new `Function` constructor, and a new `Compartment` constructor.\n\n- The compartment global object consists of all the primordial state defined by\nECMA262, but contains no host provided objects, so `window`, `document`, `XMLHttpRequest`,\n`require`, `process` etc. are all absent. Thus, a compartment contains none of the objects needed for interacting with the outside world, like the user or the network.\n\n- The new `eval`, `Function`, and `Compartment` will evaluate code in the global scope of the new compartment: the new compartment's `global` becomes their global object.\n\n- The new `eval`, `Function`, and `Compartment` inherit from the shared %FunctionPrototype%.\n\n- The new `Function.prototype` is the shared %FunctionPrototype%.\n\n- The new `Compartment` constructor...?\n\n- The new `Compartment.prototype` is the shared %CompartmentPrototype%.\n\nThe constructor then copies the values of the own enumerable properties from the `endowments` parameter onto the new `global` and returns the new compartment instance. With these additional endowments, users provide the\n*virtual host objects* that they wish to be available in the spawned compartment.\n\nThe Compartment constructor is only available on the global object after lockdown has been invoked (see below).\n\n### The Compartment prototype\n\nWe propose on the shared `Compartment.prototype`, to be inherited by instances of the all Compartment classes:\n- a `global` getter to provide access to the compartment global object. Its behavior is similar to the `globalThis` global object.\n- an `evaluate` method to evaluate code in the global scope of the new compartment. Its signature is identical to the `eval()` function\n  but possibly with an additional optional options argument.\n- an asynchronous `import` method to dynamically load modules in the new compartment. Its signature is identical to the\n  dynamic import function.\n\n### The `lockdown` method\n\nWe propose a static method, `lockdown()` or `Realm.lockdown()`, for converting the current realm into a state with immutable primordials. We call such a realm an _immutable realm_. The `Realm` global object will be specified by the Realms proposal.\n\nThe lockdown operation consists of:\n- taming some globals (see below).\n- taming the function constructors (see below).\n- freezing all intrinsics (see below).\n- disabling the default mechanism causing the _override mistake_ (see below).\n- exposing the `Compartment` constructor via the global object which is not available before lockdown (see below).\n\nAlthough `Compartment` and `Realm.lockdown()` appear orthogonal, they are only interesting when directly composed:\n\n```js\nRealm.lockdown();\nconst cmpA = new Compartment();\nconst cmpB = new Compartment();\n```\n\nAfter lockdown, all the primordials that `cmpA` and `cmpB` share are immutable, so neither can poison the prototypes of the other. Because they share no mutable state, they are as fully separate from each other as two full realms created by two same origin iframes\n(except the shared identity of frozen primordials, thus avoiding identity discontinuity explained below).\n\nModification of the prototypes is allowed before lockdown is called\n(which raises interesting issues re what is frozen by lockdown).\n\n(edit with next two paragraphs)\n\nA long recognized best practice is \"don't monkey-patch primordials\" -- don't mutate any primordial state. Most legacy code obeying this practice is already compatible with lightweight realms descending from an immutable root realm. Some further qualifications are explained in the rest of this document.\n\nIf customization of the intrinsics is required, it can be done before lockdown is called and before any compartment is created.\n\n\n## The Compartment global object\n\nThe compartment constructor is unavailable before `lockdown()` is called, to avoid the risk of omitting lockdown and creating compartments with non-frozen primordials (which would not provide the intended isolation).\n\n## Freezing intrinsics and Taming globals\n\nIn order for the intrinsics to be shared safely, they must be transitively immutable. Fortunately, of the standard primordials in ES2016, the only mutable primordial state is:\n  * Mutable own properties of primordial objects\n  * The mutable internal [[Prototype]] slot of primordial objects\n  * The ability to add properties\n  * `Math.random`\n  * `Date.now`\n  * The `Date` constructor called as a constructor with no arguments\n  * The `Date` constructor called as a function, no matter what arguments\n    (Surprised me!)\n  * Normative optional proposed `RegExp` static methods (link)\n  * Normative optional proposed `Error.prototype.stack` accessor (link)\n\nTo make a transitively immutable root realm, we, respectively\n  * Remove all non-standard properties\n  * Remove `Math.random`\n  * Remove `Date.now`\n  * Have `new Date()` throw a `TypeError`\n  * Have `Date(any...)` throw a `TypeError`\n  * Remove the `RegExp` static methods if present\n  * Remove `RegExp.prototype.compile`\n  * Remove `Error.prototype.stack` if present\n  * Make all primordial objects non-extensible.\n  * Make all remaining properties non-configurable, non-writable. If an\n    accessor property, we specify that its getter must always return\n    the same value\n    without mutating any state, and its setter either be absent or throw an error\n    without mutating any state.\n\nLikewise, any new addition to the specifications need to follow the same policy, in order to avoid introducing mutable state in a compartment.\n\nA user can effectively add the missing functionality of `Date` and `Math` back in when necessary, or substitute safe implementations. For example\n\n```js\nconst DateNow = Date.now;\n\nRealm.lockdown();\n\nfunction unsafeDate() {\n  return Date(...arguments);\n}\nObject.defineProperties(unsafeDate, Object.getOwnPropertyDescriptors(Date));\nObject.defineProperty(unsafeDate, 'now', {\n\tvalue: DateNow,\n\twritable: true,\n\tenumerable: false,\n\tconfigurable: true\n});\n\nconst cmp = new Compartment({ Date: unsafeDate });\n```\n\n## Taming the function constructors.\n\nAll intrinsics are shared, but the %Function%, %GeneratorFunction%, %AsyncFunction% and %AsyncGeneratorFunction% perform by default source code evaluation in the global scope of the realm.\n\nAfter lockdown, these constructor should be replaced with functions that throw instead of evaluating source code, so they can be safely shared.\nWe could specify that their throwing behavior is the same as when the host hook (for CSP) suppresses evaluation, mapping it to an already possible behavior.\nIf `Compartment` is a per-realm global rather than per-Compartment, then\n`Compartment.prototype.constructor === Compartment`, which is not tamed? Let's talk about this.\n\n## Override mistake\n\nBecause of lack of sufficient foresight at the time, ES5 unfortunately specified that a simple assignment to a non-existent property must fail if it would override a non-writable data property of the same name. (In retrospect, this was a mistake, but it is now too late and we must live with the consequences.) It is inconsistent with overriding by classes and object literals, since they do `[[DefineOwnProperty]]` rather than assignment.\n\nAs a result, simply freezing an object to make it immutable has the unfortunate side effect of breaking previously correct code that is considered to have followed JS best practices, if this previous code used assignment to override. For example this assignment will fail:\n\n```js\nObject.freeze(Array.prototype);\nconst arr = []\narr.join = true; // throws in strict mode, ignore in sloppy mode.\n```\n\nFor that reason, after freezing the primordials, we need to [Make non-writable prototype properties not prevent assigning to instance](https://github.com/tc39/ecma262/pull/1320).\n\nSee the [override mistake](https://web.archive.org/web/20141230041441/http://wiki.ecmascript.org/doku.php?id=strawman:fixing_override_mistake). (better link?)\n\n(We need another bit of semantic state to distinguish these two ways of being frozen. We should specify that `petrify` and perhaps even `harden` also protect against override mistake, even though we avoid fully shimming that.)\n\n## Identity discontinuity\n\nTwo realms, made by same origin iframes or vm contexts, can be put in contact. Once in contact, they can mix their object graphs freely. When realms do this, they encounter an inconvenience and source of bugs we will here call _identity discontinuity_. For example if code from iframeA makes an array `arr` that it passes to code from iframeB, and iframeB tests `arr instanceof Array`, the answer will be `false` since `arr` inherits from the `Array.prototype` of iframeA which is a different object than the `Array.prototype` of iframeB.\n\nBy contrast, since `cmpA` and `cmpB` share the same `Array.prototype`, an array `arr` created by one still passes the `arr instanceof Array` as tested by the other.\n\n\n###################################\n# TODO BELOW\n\n\n## Confinement examples\n\n```js\nfunction confine(src, endowments) {\n  return sharedRoot.spawn(endowments).eval(src);\n}\n```\n\nThis `confine` function is an example of a security abstraction we can\neasily build by composing the primitives above. It uses `spawn` to\nmake a lightweight realm descendant from our immutable `sharedRoot`\nrealm above, copies the own enumerable properties of `endowments` onto\nthe global of that lightweight realm, and then evaluates `src` in the\nscope of that global and returns the result. This `confine` function is\nespecially useful for\n[_object-capability_ programming](https://en.wikipedia.org/wiki/Object-capability_model). These\nprimitives (together with membranes) can also help to support other\nsecurity models such as\n_[decentralized dynamic information flow](https://slang.soe.ucsc.edu/cormac/proxy.pdf)_\nthough more mechanism may additionally be needed. We have not yet\nexplored this in any detail.\n\n(The `confine` function is from SES, which has a\n[formal semantics](http://research.google.com/pubs/pub37199.html)\nsupporting automated verification of some security properties of SES\ncode.  It was developed as part of the Google\n[Caja](https://github.com/google/caja) project; you can read more\nabout SES and Caja on the Caja website.)\n\n\n```js\nconfine('x + y', {x: 3, y: 4})  // -\u003e 7\n\nconfine('Object', {})           // -\u003e Object constructor of an immutable root\n\nconfine('window', {})           // ReferenceError, no 'window' in scope\n```\n\n## Plugin separation example\n\n\n```js\nfunction Counter() {\n  let count = 0;\n  return Object.freeze({\n    incr: Object.freeze(() =\u003e ++count),\n    decr: Object.freeze(() =\u003e --count)\n  });\n}\nconst counter = new Counter();\n\n// ...obtain billSrc and joanSrc from untrusted clients...\nconst bill = confine(billSrc, {change: counter.incr});\nconst joan = confine(joanSrc, {change: counter.decr});\n```\n\nSay the code above is executed by a program we call Alice. Within this\ncode, Alice obtains source code for plugins Bill and Joan. Alice does\nnot know how well these plugins are written, and so wishes to protect\nherself from their misbehavior, as well as protect each of them from\nthe misbehavior of the other. It does not matter whether Alice is\nworried about accidental or malicious misbehavior.\n\nWith the code above, Alice presents to each of these plugins an API\nsurface of her design, characteristic of the plugin framework she\ndefines. In this trivial example, she provides to each a function they\nwill know as `change` for manipulating the state of a shared\ncounter. By calling his `change` variable, Bill can only increment the\ncounter and see the result. By calling her `change` variable, Joan can\nonly decrement the counter and see the result. By using her `counter`\nvariable Alice can do both.\n\nIf Alice's code above is normal JavaScript code, then she does not achieve this\ngoal. For example, Bill or Joan could use the expression `change.__proto__` to\naccess and poison Alice's prototypes, and to interact with each other in ways\nAlice did not intend to enable. The API surface that Alice exposed to Bill and\nJoan was not _defensive_; it did not protect itself and Alice from Bill and\nJoan's misbehavior.\n\nAlice's code above is properly defensive if it is evaluated in a realm\ndescendant from an immutable root realm. Alice places Bill and Joan in\nsuch a realm to confine them. She places herself in such a realm for\nits _defensibility_, which Alice can use to define defensive\nabstractions that are safe to expose to Bill and Joan. If Alice, Bill,\nand Joan all descend from `sharedRoot`, then their further\ninteractions are defensible and free of identity discontinuities.\n\n\n## A convenience: `def(obj)`\n\nAll those calls to `Object.freeze` above are ugly. The [Caja\n`def(obj)`](https://github.com/google/caja/blob/master/src/com/google/caja/ses/startSES.js#L1180)\nfunction is an example of a convenience that should be provided by a\nlibrary. It applies `Object.freeze` recursively to all objects it finds\nstarting at `obj` by following property and `[[Prototype]]` links. This gives\nall these objects a tamper proof API surface (Note, though, that it *does not*\nmake them immutable except in special cases.) The name `def` means \"_define_ a\n_defensible_ object\".\n\nUsing `def`, we can rewrite our Counter example code as\n\n```js\nfunction Counter() {\n  let count = 0;\n  return def({\n    incr() { return ++count; }\n    decr() { return --count; }\n  });\n}\n```\n\nTo be efficient, `def` needs to somehow be in bed with this\nproposal, so it can know to stop traversing when it hits any of these\ntransitively immutable primordials. We leave it to a later proposal to\nwork out this integration issue.\n\n\n## Compartments example\n\nBy composing\n[revocable membranes](http://soft.vub.ac.be/~tvcutsem/invokedynamic/js-membranes)\nand `confine`, we can make compartments:\n\n```js\nfunction makeCompartment(src, endowments) {\n  const {wrapper,\n         revoke} = makeMembrane(confine);\n  return {wrapper: wrapper(src, endowments),\n          revoke};\n}\n\n// ...obtain billSrc and joanSrc from untrusted clients...\nconst {wrapper: bill,\n       revoke: killBill} = makeCompartment(billSrc, endowments);\nconst {wrapper: joan,\n       revoke: killJoan} = makeCompartment(joanSrc, endowments);\n\n// ... introduce mutually suspicious Bill and Joan to each other...\n// ... use both ...\nkillBill();\n// ... Bill is inaccessible to us and to Joan. GC can collect Bill ...\n```\n\nAfter `killBill` is called, there is nothing the Bill code can do to\ncause further effects.\n\n\n## Detailed Proposal\n\nYou can view the spec text draft in [ecmarkup](spec/index.emu) format or rendered as [HTML](https://rawgit.com/tc39/frozen-realms/master/index.html).\n\n  1. Introduce the `Realm` class as an officially recognized part of the\n     ECMAScript standard API.\n\n  1. Add to the `Realm` class a static method, `Realm.immutableRoot()`,\n     which obtains an _immutable root realm_ in which all primordials\n     are already transitively immutable. These primordials include\n     *all* the primordials defined as mandatory in ES2016. (And those\n     in [draft ES2017](https://tc39.github.io/ecma262/) as of March\n     17, 2016, the time of this writing.)  These primordials must\n     include no other objects or properties beyond those specified\n     here. In an immutable root realm the global object itself is also\n     transitively immutable. Specifically, it contains no\n     host-specific objects. This frozen global object is a plain\n     object whose `[[Prototype]]` is `Object.prototype`, i.e., the\n     `%ObjectPrototype%` intrinsic of that immutable root realm.\n\n     * Since two immutable root realms are forever the same in all\n       ways except object identity, we leave it implementation-defined\n       whether `Realm.immutableRoot()` always creates a fresh one, or\n       always returns the same one. On any given implementation, it\n       must either be always fresh or always the same.\n\n  1. In order to attain the necessary deep immutability of an\n     immutable root realm, two of its primordials must be modified\n     from the existing standard: An immutable root realm's `Date`\n     object has its `now()` method removed and its default constructor\n     changed to throw a `TypeError` rather than reveal the current\n     time.  An immutable root realm's `Math` object has its `random()`\n     method removed.\n\n  1. Add to the `Realm` class an instance method, `spawn(endowments)`.\n     1. `spawn` creates a new lightweight child realm with its own\n        fresh global object (denoted below by the symbol\n        `freshGlobal`) whose `[[Prototype]]` is the parent realm's\n        global object. This fresh global is also a plain\n        object. Unlike the global of an immutable root realm, this new\n        `freshGlobal` is _not_ frozen by default.\n\n     1. `spawn` populates this `freshGlobal` with overriding\n        bindings for the evaluators that have global names (currently\n        only `eval` and `Function`). It binds each of these names to\n        fresh objects whose `[[Prototype]]`s are the corresponding\n        objects from the parent realm.\n\n     1. `spawn` copies the own enumerable properties from the\n        `endowments` record onto the `freshGlobal`.\n\n     1. `spawn` returns that new child realm instance.\n\n     The total cost of a lightweight realm is four objects: the realm\n     instance itself, the `freshGlobal`, and the `eval` function and\n     `Function` constructor specific to it.\n\n  1. The evaluators of a spawned realm evaluate code in the global\n     scope of that realm's global, using that\n     global as their global object.\n\n     A lightweight realm's initial `eval` inherits from its parent's\n     `eval`. For each of the overriding constructors (currently only\n     `Function`), its `prototype` property initially has the same\n     value as the constructor they inherit from. Thus, a function\n     `foo` from one descendant realm passes the `foo instanceof\n     Function` test using the `Function` constructor of another\n     descendant of the same parent realm. Among sibling lightweight\n     realms, `instanceof` on primordial types simply works.\n\n\n\n### Polyfill example\n\nIn the **Punchlines** section below, we explain the non-overt channel\nthreats that motivate the removal of `Date.now` and\n`Math.random`. However, usually this threat is not of interest, in\nwhich case we'd rather include the full API of ES2016, since it is\notherwise safe. Indeed, Caja has always provided the full\nfunctionality of `Date` and `Math` because Caja's threat model did not\ndemand that they be denied.\n\nThe following `makeColdRealm(GoodDate, goodRandom)` function, given a\ngood `Date` constructor and `Math.random` function, makes a new\nfrozen-enough lightweight realm, that can be used as if it is an\nimmutable root realm -- as a spawning root for making lightweight\nchild realms. These children are separated-enough from each other,\nif one is not worried about non-overt channels. Unlike the lightweight\nrealms directly descendant from an immutable root realm, children\nspawned from a common cold realm share a fully functional `Date` and\n`Math`.\n\n\n```js\nfunction makeColdRealm(GoodDate, goodRandom) {\n  const goodNow = GoodDate.now;\n  const {Date: SharedDate, Math: SharedMath} = sharedRoot;\n  function FreshDate(...args) {\n    if (new.target) {\n      if (args.length === 0) {\n        args = [+goodNow()];\n      }\n      return Reflect.construct(SharedDate, args, new.target);\n    } else {\n      return String(GoodDate());\n    }\n  }\n  FreshDate.now = () =\u003e +goodNow();\n  FreshDate.prototype = SharedDate.prototype;  // so instanceof works\n  FreshDate.name = SharedDate.name;\n  FreshDate.__proto__ = SharedDate;\n\n  const FreshMath = {\n    __proto__: SharedMath,\n    random() { return +goodRandom(); }\n  };\n\n  return def(sharedRoot.spawn({Date: FreshDate, Math: FreshMath}));\n}\n```\n\nIn addition to `Date` and `Math`, we can create abstractions to endow\na fresh global with virtualized emulations of expected host-provided\nglobals like `window`, `document`, or `XMLHttpRequest`. These\nemulations may map into the caller's own or\nnot. [Caja's Domado library](https://github.com/google/caja/blob/master/src/com/google/caja/plugin/domado.js)\nuses exactly this technique to emulate most of the conventional\nbrowser and DOM APIs, by mapping the confined code's virtual DOM into\nthe portions of the caller's \"physical\" DOM that the caller\nspecifies. In this sense, the confined code is like user-mode code in\nan operating system, whose virtual memory accesses are mapped to\nphysical memory by a mapping it does not see or control. Domado remaps\nURI space in a similar manner. By emulating the browser API, much\nexisting browser code runs compatibly in a virtualized browser\nenvironment as configured by the caller using Domado.\n\nBecause `eval`, `Function`, and the above `Date` and `Math` observably\nshadow the corresponding objects from their parent realm, the spawned\nenvironment is not a fully faithful emulation of standard\nECMAScript. However, these breaks in the illusion are a necessary\nprice of avoiding identity discontinuities between lightweight realms\nspawned from a common parent. We have chosen these breaks carefully to\nbe compatible with virtually all code not written specifically to test\nstandards conformance.\n\n\n### Mobile code example\n\nMap-Reduce frameworks vividly demonstrate the power of sending the\ncode to the data, rather than the data to the code. Flexible\ndistributed computing systems must be able to express both.\n\nNow that `Function.prototype.toString` will give a\n[reliably evaluable string](http://tc39.github.io/Function-prototype-toString-revision/)\nthat can be sent, an immutable root realm provides a safe way for the\nreceiver to evaluate it, in order to reconstitute that function's call\nbehavior in a safe manner. Say we have a `RemotePromise` constructor\nthat makes a\n[remote promise for an object that is elsewhere](https://github.com/kriskowal/q-connection),\npotentially on another machine. Below, assume that the `RemotePromise`\nconstructor initializes this remote promise's\n[private instance variable](https://zenparsing.github.io/es-private-fields/)\n`#farEval` to be another remote promise, for the `eval` method of an\nimmutable root realm at the location (vat, worker, agent, event loop,\nplace, ...) where this promise's fulfillment will be. If this promise\nrejects, then its `#farEval` promise likewise rejects.\n\n```js\nclass QPromise extends Promise {\n  // ... API from https://github.com/kriskowal/q/wiki/API-Reference\n  // All we actually use below is fcall\n}\n\n// See https://github.com/kriskowal/q-connection\nclass RemotePromise extends QPromise {\n  ...\n  // callback must be a closed function, i.e., one whose only free\n  // variables are the globals defined by ES2016 and therefore present\n  // on the proto-global.\n  there(callback, errback = void 0) {\n    const callbackSrc = Function.prototype.toString.call(callback);\n    const farCallback = #farEval.fcall(callbackSrc);\n    return farCallback.fcall(this).catch(errback);\n  }\n}\n```\n\nWe explain `there` by analogy. The familiar expression\n`Promise.resolve(p).then(callback)` postpones the `callback` function\nto some future time after the promise `p` has been fulfilled. In like\nmanner, the expression `RemotePromise.resolve(r).there(callback)`\npostpones and migrates the closed `callback` function to some future\ntime and space, where the object that will be designated by the\nfulfilled remote promise `r` is located. Both `then` and `there`\nreturn a promise for what `callback` or `errback` will return.\n\nThis supports a federated form of the\n[Asynchronous Partitioned Global Address Space](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.464.557)\nconcurrency model used by the X10 supercomputer language, integrated\nsmoothly with our promise framework for handling asynchrony.\n\n\n## How Deterministic?\n\n_We do not include any form of replay within the goals of this\nproposal, so this \"How Deterministic\" section is only important\nbecause of the punchlines at the end of this section._\n\nGiven a deterministic spec, one could be sure that two computations,\nrun on two conforming implementations, starting from the same state\nand fed the same inputs, will compute the same new states and\noutputs. The ES5 and ES2015 specs come tantalizingly close to being\ndeterministic. ECMAScript has avoided some common but unnecessary\nsources of non-determinism like Java's `System.identityHashCode` or\nthe enumeration order of identity hash tables. But the ECMAScript\nspecs fail for three reasons:\n\n  * Genuine non-determinism, such as by `Math.random()`.\n  * Unspecified but unavoidable failure, such as out-of-memory.\n  * Explicit underspecification, i.e. leaving some observable behavior\n    up to the implementation.\n\nThe explicitly non-deterministic abilities to sense the current time\n(via `new Date()` and `Date.now()`) or generate random numbers (via\n`Math.random()`) are disabled in an immutable root realm, and\ntherefore by default in each realm spawned from it. New sources of\nnon-determinism, like `makeWeakRef` and `getStack` will not be added\nto immutable root realms or will be similarly disabled.\n\nThe ECMAScript specs to date have never admitted the possibility of\nfailures such as out-of-memory. In theory this means that a conforming\nECMAScript implementation requires an infinite memory\nmachine. Unfortunately, such machines are currently difficult to obtain. Since\nECMAScript is an implicitly-allocating language, the out-of-memory\ncondition could cause computation to fail at any time. If these\nfailures are reported by\n[unpredictably throwing a catchable exception](https://docs.oracle.com/javase/8/docs/api/java/lang/VirtualMachineError.html),\nthen defensive programming becomes impossible. This would be contrary\nto the goals\n[of much ECMAScript code](https://github.com/tc39/ecmascript_sharedmem/issues/55). Thus,\nany ECMAScript computation that wishes to defend its invariants, and\nany synchronous computation it is entangled with must, on encountering an\nunpredictable error,\n[preemptively abort without running further user code](https://github.com/tc39/ecmascript_sharedmem/issues/55).\n\nEven if ECMAScript were otherwise deterministically replayable, these\nunpredictable preemptive failures would prevent it. We examine instead\nthe weaker property of *fail-stop determinism*, where each replica\neither fails, or succeeds in a manner identical to every other\nnon-failing replica.\n\nAlthough few in number, there _are_ specification\nissues that are observably left to implementations, upon which\nimplementations may differ. Some of these may eventually be closed by\nfuture TC39 agreement, such as enumeration order if objects are\nmodified during enumeration (TODO link). Others, like the sort\nalgorithm used by `Array.prototype.sort` are less likely to be\nclosed. However, *implementation-defined* is not necessarily genuine\nnon-determinism. On a given implementation, operations which are only\nimplementation-defined can be deterministic within the scope of that\nimplementation. They should be fail-stop reproducible when run on the\nsame implementation. To make use of this for replay, however, we would\nneed to pin down what we mean by \"same implementation\", which seems\nslippery and difficult.\n\n### The punchlines\n\nEven without pinning down the precise meaning of\n\"implementation-defined\", a computation that is limited to fail-stop\nimplementation-defined determinism _**cannot read covert channels and\nside channels**_ that it was not explicitly enabled to read. Nothing\ncan practically prevent signaling on covert channels and side\nchannels, but approximations to determinism can practically prevent\nconfined computations from perceiving these signals.\n\n(TODO explain the anthropic side channel and how it differs from an\ninformation-flow termination channel.)\n\nFail-stop implementation-defined determinism is a **great boon to\ntesting and debugging**. All non-deterministic _dependencies_, like\nthe allegedly current time, can be mocked and _injected_ in a\nreproducible manner.\n\n\n## Annex B considerations\n\nAs of ES2016, the normative optionals of\n[Annex B](http://www.ecma-international.org/ecma-262/6.0/#sec-additional-ecmascript-features-for-web-browsers)\nare safe for inclusion as normative optionals of immutable root\nrealms. However, where Annex B states that these are normative\nmandatory in a web browser, there is no such requirement for immutable\nroot realms. Even when run in a web browser, an immutable root realm,\nhaving no host specific globals, must be considered a non-browser\nenvironment. Some post-ES2015 APIs proposed for Annex B, such as the\n[`RegExp` statics](https://github.com/claudepache/es-regexp-legacy-static-properties)\nand the\n[`Error.prototype.stack` accessor property](https://mail.mozilla.org/pipermail/es-discuss/2016-February/045579.html),\nare not safe for inclusion in immutable root realms and must be absent.\n\nAt this time, to maximize compatibility with normal ECMAScript, we do\nnot alter an immutable root realm's evaluators to evaluate code in\nstrict mode by default. However, we should consider doing so. Most of\nthe code, including legacy code, that one would wish to run under an\nimmutable root realm is probably already compatible with strict\nmode. Omitting sloppy mode from immutable root realms and their\nspawned descendants would also make sections\n[B.1.1](http://www.ecma-international.org/ecma-262/6.0/#sec-additional-syntax-numeric-literals),\n[B.1.2](http://www.ecma-international.org/ecma-262/6.0/#sec-additional-syntax-string-literals),\n[B.3.2](http://www.ecma-international.org/ecma-262/6.0/#sec-labelled-function-declarations),\n[B.3.3](http://www.ecma-international.org/ecma-262/6.0/#sec-block-level-function-declarations-web-legacy-compatibility-semantics),\nand\n[B.3.4](http://www.ecma-international.org/ecma-262/6.0/#sec-functiondeclarations-in-ifstatement-statement-clauses)\nnon issues. It is unclear what an immutable root realm's evaluators\nshould specify regarding the remaining normative optional syntax in\nsection B.1. But the syntax accepted by these evaluators, at least in\nstrict mode, should probably be pinned down precisely by the spec.\n\nSome of the elements of Annex B are safe and likely mandatory in\npractice, independent of host environment:\n\n  * `escape` and `unescape`\n  * `Object.prototype.__proto__`\n  * `String.prototype.substr`\n  * The `String.prototype` methods defined in terms of the internal\n    `CreateHTML`: `anchor`, `big`, ..., `sup`\n  * `Date.prototype.getYear` and `Date.prototype.setYear`\n  * `Date.prototype.toGMTString`\n  * [`__proto__` Property Names in Object\n     Initializers](http://www.ecma-international.org/ecma-262/6.0/#sec-__proto__-property-names-in-object-initializers)\n\nAll but the last of these have been\n[whitelisted in Caja's SES-shim](https://github.com/google/caja/blob/master/src/com/google/caja/ses/whitelist.js#L85)\nfor a long time without problem. (The last bullet above is syntax and\nso not subject to the SES-shim whitelisting mechanism.)\n\n\n## Discussion\n\nBecause an immutable root realm is transitively immutable, we can\nsafely share it between ECMAScript programs that are otherwise fully\nisolated. This sharing gives them access to shared objects and shared\nidentities, but no ability to communicate with each other or to affect\nany state outside themselves. We can even share immutable root realms\nbetween origins and between threads, since deep immutability at the\nspecification level should make thread safety at the implementation\nlevel straightforward.\n\nToday, to self-host builtins by writing them in ECMAScript, one must\npractice\n[safe meta programming](http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming)\ntechniques so that these builtins are properly defensive. This\ntechnique is difficult to get right, especially if such self hosting\nis\n[opened to ECMAScript embedders](https://docs.google.com/document/d/1AT5-T0aHGp7Lt29vPWFr2-qG8r3l9CByyvKwEuA8Ec0/edit#heading=h.ma18njbt74u3). Instead,\nthese builtins could be defined in a lightweight realm spawned from an\nimmutable root realm, making defensiveness easier to achieve with\nhigher confidence.\n\n\nBy the rules above, a spawned realm's `Function.prototype.constructor`\nwill be the parent realm's `Function` constructor, i.e., identical to\nthe spawned realm's `Function.__proto__`. In exchange for this odd\ntopology, we obtain the pleasant property that `instanceof` works\ntransparently between spawned realms by default -- unless overridden\nby a user's polyfill to the contrary.\n\nIn ES2016, the `GeneratorFunction` evaluator is not a named global,\nbut rather an unnamed intrinsic. Upcoming evaluators are likely to\ninclude `AsyncFunction` and `AsyncGeneratorFunction`. These are likely\nto be specified as unnamed intrinsics as well. For all of these, the\nabove name-based overriding of `spawn` is irrelevant and probably not\nneeded anyway.\n\nBecause code evaluated within an immutable root realm is unable to cause any\naffects outside itself it is not given explicit access to, the\nevaluators of an immutable root realm should continue to operate even in\nenvironments in which\n[CSP has forbidden normal evaluators](https://github.com/tc39/ecma262/issues/450). By\nanalogy, CSP evaluator suppression does not suppress\n`JSON.parse`. There are few ways in which evaluating code in\nan immutable root realm is more dangerous than JSON data.\n\nOther possible proposals, like\n[private state](https://zenparsing.github.io/es-private-fields/) and\n[defensible `const` classes](http://wiki.ecmascript.org/doku.php?id=harmony:classes#const),\nare likely to aid the defensive programming that is especially\npowerful in the context of this proposal. But because the utility of\nsuch defensive programming support is not limited to frozen realms,\nthey should remain independent proposals.\n\nFor each of the upcoming proposed standard APIs that are inherently\nnot immutable and powerless:\n\n  * [`defaultLoader`](https://github.com/whatwg/loader/issues/34)\n  * [`global`](https://github.com/tc39/proposal-global)\n  * [`makeWeakRef`](https://github.com/tc39/proposal-weakrefs/blob/master/specs/weakrefs.md)\n  * [`getStack`](https://mail.mozilla.org/pipermail/es-discuss/2016-February/045579.html)\n  * [`getStackString`](https://mail.mozilla.org/pipermail/es-discuss/2016-February/045579.html)\n\nthey must be absent from an immutable root realm, or have their\nbehavior grossly truncated into something safe. This spec will\nadditionally need to say how they initially appear, if at all, in each\nindividual spawned lightweight realm.  In particular, we expect a\npattern to emerge for creating a fresh loader instance to be the\ndefault loader of a fresh spawned realm. Once some proposed APIs are\nspecced as being provided by import from\n[builtin primordial modules](https://github.com/tc39/ecma262/issues/395),\nwe will need to explain how they appear in an immutable root realm\nand/or the realms it spawns.\n\n\n## Open Questions\n\n* Should `Realm.immutableRoot()` return a new fresh frozen realm each\n  time or should it always return the same one? Above we leave this\n  implementation-defined for now to encourage implementations to\n  experiment and see how efficient each can be made. If all can agree\n  on one of these options, we should codify that rather than continue\n  to leave this implementation-defined.\n\n* Although not officially a question within the jurisdiction of TC39,\n  we should discuss whether the existing CSP \"no script evaluation\"\n  settings should exempt an immutable root realm's evaluators, or whether CSP\n  should be extended in order to express this differential\n  prohibition.\n\n* Currently, if the value of `eval` is anything other than the\n  original value of `eval`, any use of it in the form of a direct-eval\n  expression will actually have the semantics of an indirect eval,\n  i.e., a simple function call to the current value of `eval`. If\n  an immutable root realm's builtin evaluators are not strict by default,\n  then any user customization that replaces a spawned realm's global\n  evaluators with strict-by-default wrappers will break their use for\n  direct-eval. Fortunately, this seems to be addressed by the rest of\n  the [old Realms API](https://gist.github.com/dherman/7568885).\n\n* The standard `Date` constructor reveals the current time either\n  * when called as a constructor with no arguments, or\n  * when called as a function (regardless of the arguments)\n\n  Above we propose to censor the current time by having the proto-Date\n  constructor throw a `TypeError` in those cases. Would another error type be\n  more appropriate? Instead of throwing an Error, should `new Date()` produce\n  an invalid date, equivalent to that produced by `new Date(NaN)`? If so,\n  calling the `Date` constructor as a function should produce the corresponding\n  string `\"Invalid Date\"`. If we go in this direction, conceivably we could\n  even have `Date.now()` return `NaN`. The advantage of removing `Date.now`\n  instead is to support the feature-testing style practiced by ECMAScript\n  programmers.\n\n* Of course, there is the perpetual bikeshedding of names. We are not\n  attached to the names we present here.\n\n## Spec Text\n\n### Updating the spec text for this proposal\n\nThe source for the spec text is located in [spec/index.emu](spec/index.emu) and it is written in [ecmarkup](https://github.com/bterlson/ecmarkup) language.\n\nWhen modifying the spec text, you should be able to build the HTML version in `index.html` by using the following command:\n\n```bash\nnpm install\nnpm run build\nopen index.html\n```\n\nAlternative, you can use `npm run watch`.\n\n## Acknowledgements\n\nThe Compartment API proposed here derives directly from [Moddable's earlier Compartment API](https://github.com/Moddable-OpenSource/moddable/tree/public/examples/js/compartments), in the XS implementation of standalone SES. We thank in particular Patrick Soquet and Peter Hoddlie for repeated sessions of brainstorming and refinement.\n\nThanks to the regular attendees at the recent SES meetings, especially Bradley Farias, Michael Fig, Saleh Motaal, and Chip Morningstar.\n\nMany thanks to E. Dean Tribble, Kevin Reid, Dave Herman, Michael Ficarra, Tom Van Cutsem, Kris Kowal, Kevin Smith, Terry Hayes, Daniel Ehrenberg, Ojan Vafai, Elliott Sprehn, and Alex Russell. Thanks to the entire Caja team (Jasvir Nagra, Ihab Awad, Mike Stay, Mike Samuel, Felix Lee, Kevin Reid, and Ben Laurie) for building a system in which all the hardest issues have already been worked out.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftc39%2Fproposal-ses","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftc39%2Fproposal-ses","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftc39%2Fproposal-ses/lists"}