Comparing version 0.18.4 to 0.18.5
@@ -30,3 +30,3 @@ // Copyright (C) 2018 Agoric | ||
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_NO_SLOPPY.md | ||
throw new TypeError(`SES failed to initialize, sloppy mode (SES_NO_SLOPPY)`); | ||
throw TypeError(`SES failed to initialize, sloppy mode (SES_NO_SLOPPY)`); | ||
} | ||
@@ -33,0 +33,0 @@ |
12
NEWS.md
User-visible changes in SES: | ||
# v0.18.5 (2023-07-14) | ||
- Adds `assert.bare` for embedding unquoted strings in details. | ||
- Permits new `dispose` symbols, other recent additions to JavaScript, and for | ||
the anticipated iterator helpers. | ||
- Tames `Symbol` so symbols can be preserved in the start compartment and | ||
denied to shared compartments. | ||
- Improves debugging in Safari. | ||
- Adds missing native function markers. | ||
All native functions should have a `toString` that says `[native code]`, even | ||
if emulated. | ||
# v0.18.4 (2023-04-20) | ||
@@ -4,0 +16,0 @@ |
{ | ||
"name": "ses", | ||
"version": "0.18.4", | ||
"version": "0.18.5", | ||
"description": "Hardened JavaScript for Fearless Cooperation", | ||
@@ -62,19 +62,23 @@ "keywords": [ | ||
}, | ||
"dependencies": { | ||
"@endo/env-options": "^0.1.1" | ||
}, | ||
"devDependencies": { | ||
"@endo/compartment-mapper": "^0.8.4", | ||
"@endo/static-module-record": "^0.7.19", | ||
"@endo/test262-runner": "^0.1.31", | ||
"ava": "^5.2.0", | ||
"@endo/compartment-mapper": "^0.8.5", | ||
"@endo/static-module-record": "^0.7.20", | ||
"@endo/test262-runner": "^0.1.32", | ||
"ava": "^5.3.0", | ||
"babel-eslint": "^10.0.3", | ||
"c8": "^7.7.3", | ||
"eslint": "^7.32.0", | ||
"eslint-config-airbnb-base": "^14.0.0", | ||
"c8": "^7.14.0", | ||
"core-js": "^3.31.0", | ||
"eslint": "^8.44.0", | ||
"eslint-config-airbnb-base": "^15.0.0", | ||
"eslint-config-prettier": "^8.8.0", | ||
"eslint-plugin-eslint-comments": "^3.1.2", | ||
"eslint-plugin-import": "^2.27.5", | ||
"prettier": "^2.8.5", | ||
"sinon": "8.0.4", | ||
"prettier": "^3.0.0", | ||
"sinon": "^15.1.0", | ||
"terser": "^5.16.6", | ||
"tsd": "^0.24.1", | ||
"typescript": "~4.9.5" | ||
"tsd": "^0.28.1", | ||
"typescript": "~5.1.3" | ||
}, | ||
@@ -179,3 +183,3 @@ "files": [ | ||
}, | ||
"gitHead": "38c2c59d6ae8c53f84cd333e6c7828e2d37604e2" | ||
"gitHead": "106da55b8bcea3067f70c29c357806f3f2e55c52" | ||
} |
152
README.md
@@ -5,3 +5,3 @@ # SES | ||
This package is a SES [shim][define shim] for JavaScript features | ||
[proposed][SES proposal] to ECMA TC-39. | ||
[proposed][SES proposal] to ECMA TC39. | ||
Hardened JavaScript is highly compatible with ordinary JavaScript. | ||
@@ -74,3 +74,4 @@ Most existing JavaScript libraries can run on hardened JavaScript. | ||
**realm**, such that no two programs running in the same realm can observe or | ||
interfere with each other until they have been introduced. | ||
affect each other until they have been introduced, and even then can only | ||
interact through their own exposed interfaces. | ||
@@ -85,6 +86,6 @@ To do this, `lockdown()` tamper-proofs all of the JavaScript intrinsics, to | ||
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. | ||
Lockdown freezes all objects that are effectively undeniable to programs in the | ||
realm. The set of such objects includes but is not limited to: `globalThis`, | ||
prototype objects of Array, Function, GeneratorFunction, and Object, and objects | ||
accessible from those objects (such as `Object.prototype.toString`). | ||
@@ -97,3 +98,3 @@ The `lockdown()` function also **tames** some objects including regular | ||
Lockdown replaces locale methods like `String.prototype.localeCompare` with | ||
lexical versions that do not reveal the user locale. | ||
generic versions that do not reveal the host locale. | ||
@@ -147,4 +148,4 @@ ```js | ||
still closes over the mutable counter. | ||
Hardening an object graph makes the surface immutable, but does not make | ||
methods pure. | ||
Hardening an object graph makes the surface immutable, but does not guarantee | ||
that methods are free of side effects. | ||
@@ -530,3 +531,3 @@ | ||
taming hides error stacks, accumulating them in side tables. The `assert` | ||
system generated other diagnostic information hidden in side tables. The tamed | ||
system generates other diagnostic information hidden in side tables. The tamed | ||
console uses these side tables to output more informative diagnostics. | ||
@@ -551,6 +552,9 @@ [Logging Errors](./src/error/README.md) explains the design. | ||
Provided that the `ses` implementation and its trusted compute base are | ||
correct, we claim that a host program (Figure 1) can evaluate a guest program | ||
(`program`) in a compartment after `lockdown` and that the program: | ||
### Single-guest Compartment Isolation | ||
Provided that the `ses` implementation and its | ||
[trusted compute base](#trusted-compute-base) are correct, we claim that a host | ||
program can evaluate a guest program (`program`) in a compartment after | ||
`lockdown` and that the guest program: | ||
- will initially only have access to one mutable object, the compartment's | ||
@@ -577,3 +581,2 @@ `globalThis`, | ||
```js | ||
// Claims, Figure 1 | ||
lockdown(); | ||
@@ -584,5 +587,8 @@ const compartment = new Compartment(); | ||
If the host program (Figure 2) arranges for the compartment's `globalThis` to | ||
### Multi-guest Compartment Isolation | ||
If the host program arranges for the compartment's `globalThis` to | ||
be frozen, we additionally claim that the host can evaluate any two guest | ||
programs (`program1` and `program2`) such that neither program will: | ||
programs (`program1` and `program2`) in that compartment such that neither | ||
guest program will: | ||
@@ -597,3 +603,2 @@ - initially share *any* mutable objects. | ||
```js | ||
// Claims, Figure 2 | ||
lockdown(); | ||
@@ -606,27 +611,26 @@ const compartment = new Compartment(); | ||
However such programs (`program`, `program1`, or `program2`) are only | ||
as useful as a calculator. | ||
A host program is therefore responsible for maintaining any of the desired | ||
invariants above when "endowing" a compartment with any of its own objects. | ||
### Endowment Protection | ||
For example, a host program (Figure 3) may run two programs in separate | ||
compartments, giving one program the ability to resolve a promise and the other | ||
program the ability to observe the settlement (fulfillment or rejection) of | ||
The above `program`, `program1`, and `program2` guest programs are only useful | ||
as glorified calculators. | ||
When going beyond that by "endowing" a compartment with extra objects, a host | ||
program is responsible for maintaining any of the invariants above that it | ||
considers necessary. | ||
For example, a host program may run two guest programs in separate | ||
compartments, giving one the ability to resolve a promise and the other | ||
the ability to observe the settlement (fulfillment or rejection) of | ||
that promise. | ||
The host program is responsible for hardening these objects. | ||
The host program is responsible for hardening the objects implementing such | ||
abilities. | ||
```js | ||
// Claims, Figure 2 | ||
lockdown(); | ||
const promise = new Promise(resolve => { | ||
const compartmentA = new Compartment(harden({ | ||
resolve, | ||
})); | ||
compartment.evaluate(programA); | ||
const compartmentA = new Compartment(harden({ resolve })); | ||
compartmentA.evaluate(programA); | ||
}); | ||
const compartmentB = new Compartment(harden({ | ||
promise, | ||
})); | ||
const compartmentB = new Compartment(harden({ promise })); | ||
compartmentB.evaluate(programB); | ||
@@ -639,9 +643,10 @@ ``` | ||
### Caveats | ||
Host programs must maintain the `ses` boundary with care in what they present | ||
as endowments. | ||
A host program should take care not to share mutable state with guests, | ||
or distribute mutable state to multiple guests, such as an unfrozen object (use | ||
`harden`), direct read and write access to a collection, like a `Map` or `Set`, | ||
even if hardened. | ||
Furthermore, typed arrays are collections and cannot be hardened. | ||
or distribute mutable state to multiple guests, such as an object that has not | ||
been frozen with `harden` or a collection like a `Map` or `Set` or typed array | ||
(collections retain some mutability even if hardened). | ||
@@ -662,4 +667,3 @@ For the purposes of sharing state, pseudo-random number generators (PRNG) like | ||
when evaluated in separate compartments. | ||
This is relevant when program created objects are shared between guest | ||
programs. | ||
This is relevant when objects are shared between guest programs. | ||
@@ -672,4 +676,4 @@ When a program interacts with an object introduced by another program (as | ||
another program, like property accessors or proxy traps, to execute on the same | ||
stack, which may be able to call back into the program (reentrancy), throw, or | ||
detect the current stack height. | ||
stack, which may be able to detect the stack height, throw an exception, or call | ||
back into the program in pursuit of a reentrancy attack. | ||
@@ -683,2 +687,4 @@ A host object can defend itself from reentrancy attacks by ensuring that it | ||
### Trusted Compute Base | ||
The trusted compute base (TCB) for `ses` includes: | ||
@@ -715,6 +721,5 @@ | ||
No critical flaws in the code surfaced during the review. | ||
As a result of the search for flaws, deficiencies, and weaknesses in the code | ||
(which is currently a Stage 1 proposal with the ECMA TC39 committee), a series | ||
of small code changes and documentation improvements were made. There is a | ||
report available on the | ||
As a result of the search for flaws, deficiencies, and weaknesses in the code, | ||
a series of small code changes and documentation improvements were made. There | ||
is a report available on the | ||
[Agoric blog](https://agoric.com/blog/technology/purple-teaming-how-metamask-and-agoric-hunted-bugs-to-harden-javascript) | ||
@@ -745,9 +750,62 @@ that includes links to recordings of code walk-throughs and technical | ||
## Ecosystem Compatibility | ||
Most ordinary JavaScript can run without issues in a realm locked down by SES. | ||
Exceptions are tracked at [issue #576][incompatibility tracking], and almost | ||
always take the form of assignments that fail because the | ||
"[override mistake][override mistake]" prevents overriding properties inherited | ||
from a frozen intrinsic object in the prototype chain. When that is the case, | ||
the code is often incompatible with *all* environments in which intrinsic | ||
objects are frozen (such as in Node.js with the | ||
[`--frozen-intrinsics`][Node frozen intrinsics] option) and can be fixed by | ||
replacing `<lhs>.<propertyKey> = <rhs>;` or `<lhs>[<propertyKey>] = <rhs>;` with | ||
```js | ||
Object.defineProperties(<lhs>, { | ||
[<propertyKey>]: { | ||
value: <rhs>, | ||
writable: true, | ||
enumerable: true, | ||
configurable: true, | ||
}, | ||
}); | ||
``` | ||
Upon encountering an incompatibility, we recommend that you add a comment to | ||
[issue #576][incompatibility tracking] and file an issue with the external | ||
project referencing this section. | ||
Projects often have their own unique issue reporting templates, but generally | ||
provide some place to include text like | ||
> ``` | ||
> This project has some assignments that break in an environment with frozen | ||
> intrinsic objects, such as | ||
> [Hardened JS (a.k.a. SES)](https://github.com/endojs/endo/blob/master/packages/ses#ecosystem-compatibility) | ||
> or Node.js with the | ||
> [`--frozen-intrinsics`](https://nodejs.org/docs/latest/api/cli.html#--frozen-intrinsics) | ||
> option. | ||
> Specifically, [link to source in the project] does not work correctly in such | ||
> an environment. | ||
> | ||
> Please consider increasing support by replacing assignments to object | ||
> properties inherited from intrinsics with use of `Object.defineProperties` | ||
> (thereby working around the JavaScript "override mistake"), and if applicable | ||
> also by avoiding mutation of intrinsic objects. | ||
> If you don't have the capacity but would accept a PR, please comment to that | ||
> effect so that a volunteer knows their efforts would be welcomed. | ||
> ``` | ||
We find that library authors are generally amenable to making these small changes to increase | ||
compatibility with any environment that protects itself from prototype pollution attacks by freezing | ||
intrinsics, including `ses`. | ||
[define shim]: https://en.wikipedia.org/wiki/Shim_(computing) | ||
[SES proposal]: https://github.com/tc39/proposal-ses | ||
[Endo Matrix]: https://matrix.to/#/#endojs:matrix.org | ||
[incompatibility tracking]: https://github.com/endojs/endo/issues/576 | ||
[Node frozen intrinsics]: https://nodejs.org/docs/latest/api/cli.html#--frozen-intrinsics | ||
[override mistake]: https://web.archive.org/web/20141230041441/http://wiki.ecmascript.org/doku.php?id=strawman:fixing_override_mistake | ||
[SECURITY.md]: https://github.com/endojs/endo/blob/master/packages/ses/SECURITY.md | ||
[SES Issues]: https://github.com/endojs/endo/issues | ||
[SES proposal]: https://github.com/tc39/proposal-ses | ||
[SES Strategy Group]: https://groups.google.com/g/ses-strategy | ||
[SES Strategy Recordings]: https://www.youtube.com/playlist?list=PLzDw4TTug5O1jzKodRDp3qec8zl88oxGd | ||
[Endo Matrix]: https://matrix.to/#/#endojs:matrix.org | ||
@@ -36,2 +36,3 @@ /* global globalThis */ | ||
String, | ||
Symbol, | ||
WeakMap, | ||
@@ -291,5 +292,3 @@ WeakSet, | ||
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_NO_EVAL.md | ||
throw new TypeError( | ||
'Cannot eval with evalTaming set to "noEval" (SES_NO_EVAL)', | ||
); | ||
throw TypeError('Cannot eval with evalTaming set to "noEval" (SES_NO_EVAL)'); | ||
}; |
@@ -64,3 +64,3 @@ /// <reference types="ses"> | ||
if (typeof source !== 'string') { | ||
throw new TypeError('first argument of evaluate() must be a string'); | ||
throw TypeError('first argument of evaluate() must be a string'); | ||
} | ||
@@ -67,0 +67,0 @@ |
@@ -23,3 +23,3 @@ // @ts-check | ||
} from './global-object.js'; | ||
import { sharedGlobalPropertyNames } from './whitelist.js'; | ||
import { sharedGlobalPropertyNames } from './permits.js'; | ||
import { load } from './module-load.js'; | ||
@@ -52,3 +52,3 @@ import { link } from './module-link.js'; | ||
if (typeof importHook !== 'function' || typeof resolveHook !== 'function') { | ||
throw new TypeError( | ||
throw TypeError( | ||
'Compartment must be constructed with an importHook and a resolveHook for it to be able to load modules', | ||
@@ -64,3 +64,3 @@ ); | ||
) { | ||
throw new TypeError( | ||
throw TypeError( | ||
'Compartment.prototype.constructor is not a valid constructor.', | ||
@@ -117,3 +117,3 @@ ); | ||
if (typeof specifier !== 'string') { | ||
throw new TypeError('first argument of module() must be a string'); | ||
throw TypeError('first argument of module() must be a string'); | ||
} | ||
@@ -135,3 +135,3 @@ | ||
if (typeof specifier !== 'string') { | ||
throw new TypeError('first argument of import() must be a string'); | ||
throw TypeError('first argument of import() must be a string'); | ||
} | ||
@@ -157,3 +157,3 @@ | ||
if (typeof specifier !== 'string') { | ||
throw new TypeError('first argument of load() must be a string'); | ||
throw TypeError('first argument of load() must be a string'); | ||
} | ||
@@ -168,3 +168,3 @@ | ||
if (typeof specifier !== 'string') { | ||
throw new TypeError('first argument of importNow() must be a string'); | ||
throw TypeError('first argument of importNow() must be a string'); | ||
} | ||
@@ -198,3 +198,3 @@ | ||
if (new.target === undefined) { | ||
throw new TypeError( | ||
throw TypeError( | ||
"Class constructor Compartment cannot be invoked without 'new'", | ||
@@ -231,3 +231,3 @@ ); | ||
// TODO implement parent module record retrieval. | ||
throw new TypeError( | ||
throw TypeError( | ||
`Cannot map module ${q(specifier)} to ${q( | ||
@@ -234,0 +234,0 @@ aliasNamespace, |
@@ -105,3 +105,3 @@ // Adapted from SES/Caja | ||
if (obj === this) { | ||
throw new TypeError( | ||
throw TypeError( | ||
`Cannot assign to read only property '${String( | ||
@@ -117,3 +117,3 @@ prop, | ||
// eslint-disable-next-line @endo/no-polymorphic-call | ||
console.error(new TypeError(`Override property ${prop}`)); | ||
console.error(TypeError(`Override property ${prop}`)); | ||
} | ||
@@ -180,3 +180,3 @@ defineProperty(this, prop, { | ||
} else { | ||
throw new TypeError(`Unexpected override enablement plan ${subPath}`); | ||
throw TypeError(`Unexpected override enablement plan ${subPath}`); | ||
} | ||
@@ -201,3 +201,3 @@ } | ||
default: { | ||
throw new TypeError(`unrecognized overrideTaming ${overrideTaming}`); | ||
throw TypeError(`unrecognized overrideTaming ${overrideTaming}`); | ||
} | ||
@@ -204,0 +204,0 @@ } |
@@ -27,2 +27,3 @@ // Copyright (C) 2019 Agoric, under Apache License 2.0 | ||
isError, | ||
regexpTest, | ||
stringIndexOf, | ||
@@ -60,2 +61,31 @@ stringReplace, | ||
const canBeBare = freeze(/^[\w:-]( ?[\w:-])*$/); | ||
/** | ||
* Embed a string directly into error details without wrapping punctuation. | ||
* To avoid injection attacks that exploit quoting confusion, this must NEVER | ||
* be used with data that is possibly attacker-controlled. | ||
* As a further safeguard, we fall back to quoting any input that is not a | ||
* string of sufficiently word-like parts separated by isolated spaces (rather | ||
* than throwing an exception, which could hide the original problem for which | ||
* explanatory details are being constructed---i.e., ``` assert.details`...` ``` | ||
* should never be the source of a new exception, nor should an attempt to | ||
* render its output, although we _could_ instead decide to handle the latter | ||
* by inline replacement similar to that of `bestEffortStringify` for producing | ||
* rendered messages like `(an object) was tagged "[Unsafe bare string]"`). | ||
* | ||
* @type {AssertQuote} | ||
*/ | ||
const bare = (payload, spaces = undefined) => { | ||
if (typeof payload !== 'string' || !regexpTest(canBeBare, payload)) { | ||
return quote(payload, spaces); | ||
} | ||
const result = freeze({ | ||
toString: freeze(() => payload), | ||
}); | ||
weakmapSet(declassifiers, result, payload); | ||
return result; | ||
}; | ||
freeze(bare); | ||
// ///////////////////////////////////////////////////////////////////////////// | ||
@@ -242,3 +272,3 @@ | ||
if (hiddenDetails === undefined) { | ||
throw new TypeError(`unrecognized details ${quote(optDetails)}`); | ||
throw TypeError(`unrecognized details ${quote(optDetails)}`); | ||
} | ||
@@ -283,3 +313,3 @@ const messageString = getMessageString(hiddenDetails); | ||
if (hiddenDetails === undefined) { | ||
throw new TypeError(`unrecognized details ${quote(detailsNote)}`); | ||
throw TypeError(`unrecognized details ${quote(detailsNote)}`); | ||
} | ||
@@ -410,9 +440,5 @@ const logArgs = getLogArgs(hiddenDetails); | ||
if (optDetails === undefined) { | ||
// Like | ||
// ```js | ||
// optDetails = details`${specimen} must be ${quote(an(typename))}`; | ||
// ``` | ||
// except it puts the typename into the literal part of the template | ||
// so it doesn't get quoted. | ||
optDetails = details(['', ` must be ${an(typename)}`], specimen); | ||
// Embed the type phrase without quotes. | ||
const typeWithDeterminer = an(typename); | ||
optDetails = details`${specimen} must be ${bare(typeWithDeterminer)}`; | ||
} | ||
@@ -439,2 +465,3 @@ fail(optDetails, TypeError); | ||
quote, | ||
bare, | ||
makeAssert, | ||
@@ -441,0 +468,0 @@ }); |
@@ -105,5 +105,3 @@ // @ts-check | ||
if (!isSafeInteger(keysBudget) || keysBudget < 0) { | ||
throw new TypeError( | ||
'keysBudget must be a safe non-negative integer number', | ||
); | ||
throw TypeError('keysBudget must be a safe non-negative integer number'); | ||
} | ||
@@ -231,3 +229,3 @@ /** @typedef {DoublyLinkedCell<WeakMap<K, V> | undefined>} LRUCacheCell */ | ||
if (!isSafeInteger(argsPerErrorBudget) || argsPerErrorBudget < 1) { | ||
throw new TypeError( | ||
throw TypeError( | ||
'argsPerErrorBudget must be a safe positive integer number', | ||
@@ -234,0 +232,0 @@ ); |
@@ -31,3 +31,3 @@ // @ts-check | ||
if (consoleTaming !== 'safe' && consoleTaming !== 'unsafe') { | ||
throw new TypeError(`unrecognized consoleTaming ${consoleTaming}`); | ||
throw TypeError(`unrecognized consoleTaming ${consoleTaming}`); | ||
} | ||
@@ -34,0 +34,0 @@ |
@@ -11,3 +11,3 @@ import { | ||
} from '../commons.js'; | ||
import { NativeErrors } from '../whitelist.js'; | ||
import { NativeErrors } from '../permits.js'; | ||
import { tameV8ErrorConstructor } from './tame-v8-error-constructor.js'; | ||
@@ -39,6 +39,6 @@ | ||
if (errorTaming !== 'safe' && errorTaming !== 'unsafe') { | ||
throw new TypeError(`unrecognized errorTaming ${errorTaming}`); | ||
throw TypeError(`unrecognized errorTaming ${errorTaming}`); | ||
} | ||
if (stackFiltering !== 'concise' && stackFiltering !== 'verbose') { | ||
throw new TypeError(`unrecognized stackFiltering ${stackFiltering}`); | ||
throw TypeError(`unrecognized stackFiltering ${stackFiltering}`); | ||
} | ||
@@ -45,0 +45,0 @@ const ErrorPrototype = FERAL_ERROR.prototype; |
@@ -344,2 +344,3 @@ // @ts-check | ||
* quote: AssertQuote, | ||
* bare: AssertQuote, | ||
* makeAssert: MakeAssert, | ||
@@ -346,0 +347,0 @@ * } } Assert |
@@ -16,2 +16,3 @@ import { | ||
regexpPrototype, | ||
globalThis, | ||
} from './commons.js'; | ||
@@ -138,3 +139,25 @@ import { InertCompartment } from './compartment-shim.js'; | ||
if (globalThis.Iterator) { | ||
intrinsics['%IteratorHelperPrototype%'] = getPrototypeOf( | ||
// eslint-disable-next-line @endo/no-polymorphic-call | ||
globalThis.Iterator.from([]).take(0), | ||
); | ||
intrinsics['%WrapForValidIteratorPrototype%'] = getPrototypeOf( | ||
// eslint-disable-next-line @endo/no-polymorphic-call | ||
globalThis.Iterator.from({ next() {} }), | ||
); | ||
} | ||
if (globalThis.AsyncIterator) { | ||
intrinsics['%AsyncIteratorHelperPrototype%'] = getPrototypeOf( | ||
// eslint-disable-next-line @endo/no-polymorphic-call | ||
globalThis.AsyncIterator.from([]).take(0), | ||
); | ||
intrinsics['%WrapForValidAsyncIteratorPrototype%'] = getPrototypeOf( | ||
// eslint-disable-next-line @endo/no-polymorphic-call | ||
globalThis.AsyncIterator.from({ next() {} }), | ||
); | ||
} | ||
return intrinsics; | ||
}; |
@@ -13,3 +13,3 @@ import { | ||
import { makeFunctionConstructor } from './make-function-constructor.js'; | ||
import { constantProperties, universalPropertyNames } from './whitelist.js'; | ||
import { constantProperties, universalPropertyNames } from './permits.js'; | ||
@@ -35,3 +35,3 @@ /** | ||
set: freeze(() => { | ||
throw new TypeError( | ||
throw TypeError( | ||
`Cannot set Symbol.unscopables of a Compartment's globalThis`, | ||
@@ -38,0 +38,0 @@ ); |
@@ -23,4 +23,4 @@ import { | ||
universalPropertyNames, | ||
whitelist, | ||
} from './whitelist.js'; | ||
permitted, | ||
} from './permits.js'; | ||
@@ -47,3 +47,3 @@ const isFunction = obj => typeof obj === 'function'; | ||
) { | ||
throw new TypeError(`Conflicting definitions of ${name}`); | ||
throw TypeError(`Conflicting definitions of ${name}`); | ||
} | ||
@@ -99,15 +99,15 @@ } | ||
} | ||
const permit = whitelist[name]; | ||
const permit = permitted[name]; | ||
if (typeof permit !== 'object') { | ||
throw new TypeError(`Expected permit object at whitelist.${name}`); | ||
throw TypeError(`Expected permit object at whitelist.${name}`); | ||
} | ||
const namePrototype = permit.prototype; | ||
if (!namePrototype) { | ||
throw new TypeError(`${name}.prototype property not whitelisted`); | ||
throw TypeError(`${name}.prototype property not whitelisted`); | ||
} | ||
if ( | ||
typeof namePrototype !== 'string' || | ||
!objectHasOwnProperty(whitelist, namePrototype) | ||
!objectHasOwnProperty(permitted, namePrototype) | ||
) { | ||
throw new TypeError(`Unrecognized ${name}.prototype whitelist entry`); | ||
throw TypeError(`Unrecognized ${name}.prototype whitelist entry`); | ||
} | ||
@@ -117,3 +117,3 @@ const intrinsicPrototype = intrinsic.prototype; | ||
if (intrinsics[namePrototype] !== intrinsicPrototype) { | ||
throw new TypeError(`Conflicting bindings of ${namePrototype}`); | ||
throw TypeError(`Conflicting bindings of ${namePrototype}`); | ||
} | ||
@@ -137,3 +137,3 @@ // eslint-disable-next-line no-continue | ||
if (!pseudoNatives) { | ||
throw new TypeError( | ||
throw TypeError( | ||
'isPseudoNative can only be called after finalIntrinsics', | ||
@@ -140,0 +140,0 @@ ); |
@@ -17,2 +17,3 @@ // Copyright (C) 2018 Agoric | ||
import { makeEnvironmentCaptor } from '@endo/env-options'; | ||
import { | ||
@@ -23,3 +24,2 @@ FERAL_FUNCTION, | ||
arrayFilter, | ||
arrayMap, | ||
globalThis, | ||
@@ -31,6 +31,5 @@ is, | ||
} from './commons.js'; | ||
import { enJoin } from './error/stringify-utils.js'; | ||
import { makeHardener } from './make-hardener.js'; | ||
import { makeIntrinsicsCollector } from './intrinsics.js'; | ||
import whitelistIntrinsics from './whitelist-intrinsics.js'; | ||
import whitelistIntrinsics from './permits-intrinsics.js'; | ||
import tameFunctionConstructors from './tame-function-constructors.js'; | ||
@@ -48,3 +47,3 @@ import tameDateConstructor from './tame-date-constructor.js'; | ||
import { makeSafeEvaluator } from './make-safe-evaluator.js'; | ||
import { initialGlobalPropertyNames } from './whitelist.js'; | ||
import { initialGlobalPropertyNames } from './permits.js'; | ||
import { tameFunctionToString } from './tame-function-tostring.js'; | ||
@@ -56,6 +55,6 @@ import { tameDomains } from './tame-domains.js'; | ||
import { assert, makeAssert } from './error/assert.js'; | ||
import { makeEnvironmentCaptor } from './environment-options.js'; | ||
import { getAnonymousIntrinsics } from './get-anonymous-intrinsics.js'; | ||
import { makeCompartmentConstructor } from './compartment-shim.js'; | ||
import { tameHarden } from './tame-harden.js'; | ||
import { tameSymbolConstructor } from './tame-symbol-constructor.js'; | ||
@@ -125,3 +124,3 @@ /** @typedef {import('../types.js').LockdownOptions} LockdownOptions */ | ||
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_DIRECT_EVAL.md | ||
throw new TypeError( | ||
throw TypeError( | ||
`SES cannot initialize unless 'eval' is the original intrinsic 'eval', suitable for direct-eval (dynamically scoped eval) (SES_DIRECT_EVAL)`, | ||
@@ -162,4 +161,3 @@ ); | ||
const { getEnvironmentOption: getenv, getCapturedEnvironmentOptionNames } = | ||
makeEnvironmentCaptor(globalThis); | ||
const { getEnvironmentOption: getenv } = makeEnvironmentCaptor(globalThis); | ||
@@ -191,13 +189,2 @@ const { | ||
const capturedEnvironmentOptionNames = getCapturedEnvironmentOptionNames(); | ||
if (capturedEnvironmentOptionNames.length > 0) { | ||
// eslint-disable-next-line @endo/no-polymorphic-call | ||
console.warn( | ||
`SES Lockdown using options from environment variables ${enJoin( | ||
arrayMap(capturedEnvironmentOptionNames, q), | ||
'and', | ||
)}`, | ||
); | ||
} | ||
evalTaming === 'unsafeEval' || | ||
@@ -221,3 +208,3 @@ evalTaming === 'safeEval' || | ||
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_ALREADY_LOCKED_DOWN.md | ||
priorLockdown = new TypeError('Prior lockdown (SES_ALREADY_LOCKED_DOWN)'); | ||
priorLockdown = TypeError('Prior lockdown (SES_ALREADY_LOCKED_DOWN)'); | ||
// Tease V8 to generate the stack string and release the closures the stack | ||
@@ -264,3 +251,3 @@ // trace retained: | ||
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_MULTIPLE_INSTANCES.md | ||
throw new TypeError( | ||
throw TypeError( | ||
`Already locked down but not by this SES instance (SES_MULTIPLE_INSTANCES)`, | ||
@@ -276,2 +263,6 @@ ); | ||
// Replace Function.prototype.toString with one that recognizes | ||
// shimmed functions as honorary native functions. | ||
const markVirtualizedNativeFunction = tameFunctionToString(); | ||
const { addIntrinsics, completePrototypes, finalIntrinsics } = | ||
@@ -289,2 +280,3 @@ makeIntrinsicsCollector(); | ||
addIntrinsics(tameRegExpConstructor(regExpTaming)); | ||
addIntrinsics(tameSymbolConstructor()); | ||
@@ -310,3 +302,2 @@ addIntrinsics(getAnonymousIntrinsics()); | ||
const consoleRecord = tameConsole( | ||
// @ts-expect-error tameConsole does its own input validation | ||
consoleTaming, | ||
@@ -333,6 +324,2 @@ errorTrapping, | ||
// Replace Function.prototype.toString with one that recognizes | ||
// shimmed functions as honorary native functions. | ||
const markVirtualizedNativeFunction = tameFunctionToString(); | ||
/** | ||
@@ -339,0 +326,0 @@ * 2. WHITELIST to standardize the environment. |
@@ -163,3 +163,3 @@ // Adapted from SES/Caja - Copyright (C) 2011 Google Inc. | ||
// future proof: break until someone figures out what it should do | ||
throw new TypeError(`Unexpected typeof: ${type}`); | ||
throw TypeError(`Unexpected typeof: ${type}`); | ||
} | ||
@@ -166,0 +166,0 @@ if (weaksetHas(hardened, val) || setHas(toFreeze, val)) { |
@@ -49,3 +49,3 @@ import { assert } from './error/assert.js'; | ||
) { | ||
throw new TypeError( | ||
throw TypeError( | ||
`SES third-party static module record "exports" property must be an array of strings for module ${moduleSpecifier}`, | ||
@@ -199,5 +199,3 @@ ); | ||
if (tdz) { | ||
throw new ReferenceError( | ||
`binding ${q(localName)} not yet initialized`, | ||
); | ||
throw ReferenceError(`binding ${q(localName)} not yet initialized`); | ||
} | ||
@@ -212,3 +210,3 @@ return value; | ||
if (!tdz) { | ||
throw new TypeError( | ||
throw TypeError( | ||
`Internal: binding ${q(localName)} already initialized`, | ||
@@ -273,3 +271,3 @@ ); | ||
if (tdz) { | ||
throw new ReferenceError( | ||
throw ReferenceError( | ||
`binding ${q(liveExportName)} not yet initialized`, | ||
@@ -301,5 +299,3 @@ ); | ||
if (tdz) { | ||
throw new ReferenceError( | ||
`binding ${q(localName)} not yet initialized`, | ||
); | ||
throw ReferenceError(`binding ${q(localName)} not yet initialized`); | ||
} | ||
@@ -306,0 +302,0 @@ value = newValue; |
@@ -50,3 +50,3 @@ /* eslint-disable no-underscore-dangle */ | ||
if (moduleRecord === undefined) { | ||
throw new ReferenceError( | ||
throw ReferenceError( | ||
`Missing link to module ${q(moduleSpecifier)} from compartment ${q( | ||
@@ -156,3 +156,3 @@ compartmentName, | ||
} else { | ||
throw new TypeError( | ||
throw TypeError( | ||
`importHook must return a static module record, got ${q( | ||
@@ -159,0 +159,0 @@ staticModuleRecord, |
@@ -184,3 +184,3 @@ // For brevity, in this file, as in module-link.js, the term "moduleRecord" | ||
if (staticModuleRecord.compartment !== undefined) { | ||
throw new TypeError( | ||
throw TypeError( | ||
'Cannot redirect to an explicit record with a specified compartment', | ||
@@ -215,3 +215,3 @@ ); | ||
if (staticModuleRecord.importMeta !== undefined) { | ||
throw new TypeError( | ||
throw TypeError( | ||
'Cannot redirect to an implicit record with a specified importMeta', | ||
@@ -235,5 +235,3 @@ ); | ||
throw new TypeError( | ||
'Unnexpected RedirectStaticModuleInterface record shape', | ||
); | ||
throw TypeError('Unnexpected RedirectStaticModuleInterface record shape'); | ||
} | ||
@@ -357,3 +355,3 @@ | ||
if (errors.length > 0) { | ||
throw new TypeError( | ||
throw TypeError( | ||
`Failed to load module ${q(moduleSpecifier)} in package ${q( | ||
@@ -360,0 +358,0 @@ compartmentName, |
@@ -63,3 +63,3 @@ // Compartments need a mechanism to link a module from one compartment | ||
if (!active) { | ||
throw new TypeError( | ||
throw TypeError( | ||
`Cannot get property ${q( | ||
@@ -73,3 +73,3 @@ name, | ||
set(_target, name, _value) { | ||
throw new TypeError( | ||
throw TypeError( | ||
`Cannot set property ${q(name)} of module exports namespace`, | ||
@@ -80,3 +80,3 @@ ); | ||
if (!active) { | ||
throw new TypeError( | ||
throw TypeError( | ||
`Cannot check property ${q( | ||
@@ -90,3 +90,3 @@ name, | ||
deleteProperty(_target, name) { | ||
throw new TypeError( | ||
throw TypeError( | ||
`Cannot delete property ${q(name)}s of module exports namespace`, | ||
@@ -97,3 +97,3 @@ ); | ||
if (!active) { | ||
throw new TypeError( | ||
throw TypeError( | ||
'Cannot enumerate keys, the module has not yet begun to execute', | ||
@@ -106,3 +106,3 @@ ); | ||
if (!active) { | ||
throw new TypeError( | ||
throw TypeError( | ||
`Cannot get own property descriptor ${q( | ||
@@ -117,3 +117,3 @@ name, | ||
if (!active) { | ||
throw new TypeError( | ||
throw TypeError( | ||
'Cannot prevent extensions of module exports namespace, the module has not yet begun to execute', | ||
@@ -126,3 +126,3 @@ ); | ||
if (!active) { | ||
throw new TypeError( | ||
throw TypeError( | ||
'Cannot check extensibility of module exports namespace, the module has not yet begun to execute', | ||
@@ -137,6 +137,6 @@ ); | ||
setPrototypeOf(_target, _proto) { | ||
throw new TypeError('Cannot set prototype of module exports namespace'); | ||
throw TypeError('Cannot set prototype of module exports namespace'); | ||
}, | ||
defineProperty(_target, name, _descriptor) { | ||
throw new TypeError( | ||
throw TypeError( | ||
`Cannot define property ${q(name)} of module exports namespace`, | ||
@@ -146,3 +146,3 @@ ); | ||
apply(_target, _thisArg, _args) { | ||
throw new TypeError( | ||
throw TypeError( | ||
'Cannot call module exports namespace, it is not a function', | ||
@@ -152,3 +152,3 @@ ); | ||
construct(_target, _args) { | ||
throw new TypeError( | ||
throw TypeError( | ||
'Cannot construct module exports namespace, it is not a constructor', | ||
@@ -155,0 +155,0 @@ ); |
@@ -46,3 +46,3 @@ import { | ||
// ReferenceError message "Uncaught ReferenceError: xyz is not defined" | ||
throw new ReferenceError(`${String(prop)} is not defined`); | ||
throw ReferenceError(`${String(prop)} is not defined`); | ||
}, | ||
@@ -57,3 +57,3 @@ | ||
// https://bugs.webkit.org/show_bug.cgi?id=195534 | ||
getPrototypeOf() { | ||
getPrototypeOf(_shadow) { | ||
return null; | ||
@@ -64,3 +64,3 @@ }, | ||
// TODO: report as bug to v8 or Chrome, and record issue link here. | ||
getOwnPropertyDescriptor(_target, prop) { | ||
getOwnPropertyDescriptor(_shadow, prop) { | ||
// Coerce with `String` in case prop is a symbol. | ||
@@ -71,6 +71,12 @@ const quotedProp = q(String(prop)); | ||
`getOwnPropertyDescriptor trap on scopeTerminatorHandler for ${quotedProp}`, | ||
new TypeError().stack, | ||
TypeError().stack, | ||
); | ||
return undefined; | ||
}, | ||
// See https://github.com/endojs/endo/issues/1490 | ||
// TODO Report bug to JSC or Safari | ||
ownKeys(_shadow) { | ||
return []; | ||
}, | ||
}; | ||
@@ -77,0 +83,0 @@ |
@@ -13,3 +13,3 @@ // @ts-check | ||
if (dateTaming !== 'safe' && dateTaming !== 'unsafe') { | ||
throw new TypeError(`unrecognized dateTaming ${dateTaming}`); | ||
throw TypeError(`unrecognized dateTaming ${dateTaming}`); | ||
} | ||
@@ -16,0 +16,0 @@ const OriginalDate = Date; |
@@ -12,3 +12,3 @@ // @ts-check | ||
if (domainTaming !== 'safe' && domainTaming !== 'unsafe') { | ||
throw new TypeError(`unrecognized domainTaming ${domainTaming}`); | ||
throw TypeError(`unrecognized domainTaming ${domainTaming}`); | ||
} | ||
@@ -31,3 +31,3 @@ | ||
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_NO_DOMAINS.md | ||
throw new TypeError( | ||
throw TypeError( | ||
`SES failed to lockdown, Node.js domains have been initialized (SES_NO_DOMAINS)`, | ||
@@ -34,0 +34,0 @@ ); |
@@ -33,3 +33,3 @@ import { | ||
// In addition, the standard needs to define four new intrinsics for the safe | ||
// replacement functions. See [./whitelist intrinsics]. | ||
// replacement functions. See [./permits-intrinsics.js]. | ||
// | ||
@@ -88,3 +88,3 @@ // Adapted from SES/Caja | ||
const InertConstructor = function () { | ||
throw new TypeError( | ||
throw TypeError( | ||
'Function.prototype.constructor is not a valid constructor.', | ||
@@ -91,0 +91,0 @@ ); |
@@ -6,3 +6,3 @@ /* eslint-disable no-restricted-globals */ | ||
if (hardenTaming !== 'safe' && hardenTaming !== 'unsafe') { | ||
throw new TypeError(`unrecognized fakeHardenOption ${hardenTaming}`); | ||
throw TypeError(`unrecognized fakeHardenOption ${hardenTaming}`); | ||
} | ||
@@ -9,0 +9,0 @@ |
@@ -22,3 +22,3 @@ import { | ||
if (this === null || this === undefined) { | ||
throw new TypeError( | ||
throw TypeError( | ||
'Cannot localeCompare with null or undefined "this" value', | ||
@@ -49,3 +49,3 @@ ); | ||
if (localeTaming !== 'safe' && localeTaming !== 'unsafe') { | ||
throw new TypeError(`unrecognized localeTaming ${localeTaming}`); | ||
throw TypeError(`unrecognized localeTaming ${localeTaming}`); | ||
} | ||
@@ -52,0 +52,0 @@ if (localeTaming === 'unsafe') { |
@@ -11,3 +11,3 @@ import { | ||
if (mathTaming !== 'safe' && mathTaming !== 'unsafe') { | ||
throw new TypeError(`unrecognized mathTaming ${mathTaming}`); | ||
throw TypeError(`unrecognized mathTaming ${mathTaming}`); | ||
} | ||
@@ -14,0 +14,0 @@ const originalMath = Math; |
@@ -12,3 +12,3 @@ import { | ||
if (regExpTaming !== 'safe' && regExpTaming !== 'unsafe') { | ||
throw new TypeError(`unrecognized regExpTaming ${regExpTaming}`); | ||
throw TypeError(`unrecognized regExpTaming ${regExpTaming}`); | ||
} | ||
@@ -31,3 +31,3 @@ const RegExpPrototype = FERAL_REG_EXP.prototype; | ||
if (!speciesDesc) { | ||
throw new TypeError('no RegExp[Symbol.species] descriptor'); | ||
throw TypeError('no RegExp[Symbol.species] descriptor'); | ||
} | ||
@@ -34,0 +34,0 @@ |
@@ -74,3 +74,3 @@ // @ts-check | ||
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_HTML_COMMENT_REJECTED.md | ||
throw new SyntaxError( | ||
throw SyntaxError( | ||
`Possible HTML comment rejected at ${name}:${lineNumber}. (SES_HTML_COMMENT_REJECTED)`, | ||
@@ -151,3 +151,3 @@ ); | ||
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_IMPORT_REJECTED.md | ||
throw new SyntaxError( | ||
throw SyntaxError( | ||
`Possible import expression rejected at ${name}:${lineNumber}. (SES_IMPORT_REJECTED)`, | ||
@@ -224,3 +224,3 @@ ); | ||
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_EVAL_REJECTED.md | ||
throw new SyntaxError( | ||
throw SyntaxError( | ||
`Possible direct eval expression rejected at ${name}:${lineNumber}. (SES_EVAL_REJECTED)`, | ||
@@ -227,0 +227,0 @@ ); |
@@ -202,2 +202,3 @@ /** | ||
quote(payload: any, spaces?: string | number): ToStringable; | ||
bare(payload: any, spaces?: string | number): ToStringable; | ||
makeAssert: MakeAssert; | ||
@@ -204,0 +205,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3014113
64652
795
1
17
1
+ Added@endo/env-options@^0.1.1
+ Added@endo/env-options@0.1.4(transitive)