Socket
Socket
Sign inDemoInstall

ses

Package Overview
Dependencies
Maintainers
6
Versions
102
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ses - npm Package Compare versions

Comparing version 1.4.1 to 1.5.0

dist/types.d.cts

5

NEWS.md
User-visible changes in SES:
# v1.5.0 (2024-05-06)
- Adds `importNowHook` to the `Compartment` options. The compartment will invoke the hook whenever it encounters a missing dependency while running `compartmentInstance.importNow(specifier)`, which cannot use an asynchronous `importHook`.
- To support TypeScript v5.5, a CommonJS-specific type declaration file is now explicitly exported.
# v1.3.0 (2024-02-22)

@@ -4,0 +9,0 @@

39

package.json
{
"name": "ses",
"version": "1.4.1",
"version": "1.5.0",
"description": "Hardened JavaScript for Fearless Cooperation",

@@ -39,10 +39,20 @@ "keywords": [

".": {
"types": "./types.d.ts",
"import": "./index.js",
"require": "./dist/ses.cjs"
"import": {
"types": "./types.d.ts",
"default": "./index.js"
},
"require": {
"types": "./dist/types.d.cts",
"default": "./dist/ses.cjs"
}
},
"./lockdown": {
"types": "./types.d.ts",
"import": "./index.js",
"require": "./dist/ses.cjs"
"import": {
"types": "./types.d.ts",
"default": "./index.js"
},
"require": {
"types": "./dist/types.d.cts",
"default": "./dist/ses.cjs"
}
},

@@ -64,3 +74,3 @@ "./tools.js": "./tools.js",

"lint:types": "tsc",
"prepare": "yarn run clean && yarn build",
"prepare": "npm run clean && npm run build",
"qt": "ava",

@@ -71,8 +81,8 @@ "test": "tsd && ava",

"dependencies": {
"@endo/env-options": "^1.1.3"
"@endo/env-options": "^1.1.4"
},
"devDependencies": {
"@endo/compartment-mapper": "^1.1.4",
"@endo/static-module-record": "^1.1.1",
"@endo/test262-runner": "^0.1.36",
"@endo/compartment-mapper": "^1.1.5",
"@endo/static-module-record": "^1.1.2",
"@endo/test262-runner": "^0.1.37",
"ava": "^6.1.2",

@@ -87,3 +97,3 @@ "babel-eslint": "^10.0.3",

"eslint-plugin-import": "^2.29.0",
"prettier": "^3.0.0",
"prettier": "^3.2.5",
"sinon": "^15.1.0",

@@ -104,3 +114,2 @@ "terser": "^5.16.6",

],
"private": false,
"publishConfig": {

@@ -197,3 +206,3 @@ "access": "public"

},
"gitHead": "25229bdcc26babb3afe0c229df4283a0f3c105f3"
"gitHead": "08e59bc0d262565165636c2e3875bbe3dcb91cf8"
}

@@ -387,2 +387,37 @@ # SES

### importNowHook
Additionally, an `importNowHook` may be provided that the compartment will use as means to synchronously load modules not seen before in situations where calling out to asynchronous `importHook` is not possible.
Specifically, when `compartmentInstance.importNow('specifier')` is called, the compartment will first look up module records it's already aware of and call `moduleMapHook` and if none of that is successful in finding a module record matching the specifier, it will call `importNowHook` expecting to synchronously receive the same record type as from `importHook` or throw if it cannot.
```js
import 'ses';
import { StaticModuleRecord } from '@endo/static-module-record';
const c1 = new Compartment({}, {
'c2': c2.module('./main.js'),
}, {
name: "first compartment",
resolveHook: (moduleSpecifier, moduleReferrer) => {
return resolve(moduleSpecifier, moduleReferrer);
},
importHook: async moduleSpecifier => {
const moduleLocation = locate(moduleSpecifier);
const moduleText = await retrieve(moduleLocation);
return new StaticModuleRecord(moduleText, moduleLocation);
},
importNowHook: moduleSpecifier => {
const moduleLocation = locate(moduleSpecifier);
// Platform specific synchronous read API can be used
const moduleText = fs.readFileSync(moduleLocation);
return new StaticModuleRecord(moduleText, moduleLocation);
},
});
//... | importHook | importNowHook
await c1.import('a'); //| called | not called
c1.importNow('b'); //| not called | called
c1.importNow('a'); //| not called | not called
c1.importNow('c2'); //| not called | not called
```
### Third-party modules

@@ -389,0 +424,0 @@

@@ -136,2 +136,6 @@ /* global globalThis */

export const { prototype: promisePrototype } = Promise;
export const { prototype: generatorPrototype } = getPrototypeOf(
// eslint-disable-next-line no-empty-function, func-names
function* () {},
);

@@ -195,2 +199,5 @@ export const typedArrayPrototype = getPrototypeOf(Uint8Array.prototype);

export const stringMatch = uncurryThis(stringPrototype.match);
export const generatorNext = uncurryThis(generatorPrototype.next);
export const generatorThrow = uncurryThis(generatorPrototype.throw);
/**

@@ -222,2 +229,3 @@ * @type { &

export const functionToString = uncurryThis(functionPrototype.toString);
export const functionBind = uncurryThis(bind);
//

@@ -300,1 +308,59 @@ const { all } = Promise;

};
// ////////////////// FERAL_STACK_GETTER FERAL_STACK_SETTER ////////////////////
const er1StackDesc = getOwnPropertyDescriptor(Error('er1'), 'stack');
const er2StackDesc = getOwnPropertyDescriptor(TypeError('er2'), 'stack');
let feralStackGetter;
let feralStackSetter;
if (er1StackDesc && er2StackDesc && er1StackDesc.get) {
// We should only encounter this case on v8 because of its problematic
// error own stack accessor behavior.
// Note that FF/SpiderMonkey, Moddable/XS, and the error stack proposal
// all inherit a stack accessor property from Error.prototype, which is
// great. That case needs no heroics to secure.
if (
// In the v8 case as we understand it, all errors have an own stack
// accessor property, but within the same realm, all these accessor
// properties have the same getter and have the same setter.
// This is therefore the case that we repair.
typeof er1StackDesc.get === 'function' &&
er1StackDesc.get === er2StackDesc.get &&
typeof er1StackDesc.set === 'function' &&
er1StackDesc.set === er2StackDesc.set
) {
// Otherwise, we have own stack accessor properties that are outside
// our expectations, that therefore need to be understood better
// before we know how to repair them.
feralStackGetter = freeze(er1StackDesc.get);
feralStackSetter = freeze(er1StackDesc.set);
} else {
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_UNEXPECTED_ERROR_OWN_STACK_ACCESSOR.md
throw TypeError(
'Unexpected Error own stack accessor functions (SES_UNEXPECTED_ERROR_OWN_STACK_ACCESSOR)',
);
}
}
/**
* If on a v8 with the problematic error own stack accessor behavior,
* `FERAL_STACK_GETTER` will be the shared getter of all those accessors
* and `FERAL_STACK_SETTER` will be the shared setter. On any platform
* without this problem, `FERAL_STACK_GETTER` and `FERAL_STACK_SETTER` are
* both `undefined`.
*
* @type {(() => any) | undefined}
*/
export const FERAL_STACK_GETTER = feralStackGetter;
/**
* If on a v8 with the problematic error own stack accessor behavior,
* `FERAL_STACK_GETTER` will be the shared getter of all those accessors
* and `FERAL_STACK_SETTER` will be the shared setter. On any platform
* without this problem, `FERAL_STACK_GETTER` and `FERAL_STACK_SETTER` are
* both `undefined`.
*
* @type {((newValue: any) => void) | undefined}
*/
export const FERAL_STACK_SETTER = feralStackSetter;

@@ -25,3 +25,3 @@ // @ts-check

import { sharedGlobalPropertyNames } from './permits.js';
import { load } from './module-load.js';
import { load, loadNow } from './module-load.js';
import { link } from './module-link.js';

@@ -164,3 +164,3 @@ import { getDeferredExports } from './module-proxy.js';

assertModuleHooks(this);
loadNow(privateFields, moduleAliases, this, specifier);
return compartmentImportNow(/** @type {Compartment} */ (this), specifier);

@@ -214,2 +214,3 @@ },

importHook,
importNowHook,
moduleMapHook,

@@ -291,2 +292,3 @@ importMetaHook,

importHook,
importNowHook,
moduleMap,

@@ -293,0 +295,0 @@ moduleMapHook,

@@ -38,2 +38,7 @@ // Copyright (C) 2019 Agoric, under Apache License 2.0

AggregateError,
getOwnPropertyDescriptors,
ownKeys,
create,
objectPrototype,
objectHasOwnProperty,
} from '../commons.js';

@@ -45,2 +50,6 @@ import { an, bestEffortStringify } from './stringify-utils.js';

/**
* @import {BaseAssert, Assert, AssertionFunctions, AssertionUtilities, StringablePayload, DetailsToken, MakeAssert} from '../../types.js'
*/
// For our internal debugging purposes, uncomment

@@ -54,3 +63,3 @@ // const internalDebugConsole = console;

/** @type {AssertQuote} */
/** @type {AssertionUtilities['quote']} */
const quote = (payload, spaces = undefined) => {

@@ -68,15 +77,3 @@ const result = freeze({

/**
* 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}
* @type {AssertionUtilities['bare']}
*/

@@ -167,3 +164,3 @@ const bare = (payload, spaces = undefined) => {

*
* @type {DetailsTag}
* @type {AssertionUtilities['details']}
*/

@@ -177,3 +174,3 @@ const redactedDetails = (template, ...args) => {

weakmapSet(hiddenDetailsMap, detailsToken, { template, args });
return detailsToken;
return /** @type {DetailsToken} */ (/** @type {unknown} */ (detailsToken));
};

@@ -193,3 +190,3 @@ freeze(redactedDetails);

*
* @type {DetailsTag}
* @type {AssertionUtilities['details']}
*/

@@ -265,8 +262,75 @@ const unredactedDetails = (template, ...args) => {

/**
* @type {AssertMakeError}
* Make reasonable best efforts to make a `Passable` error.
* - `sanitizeError` will remove any "extraneous" own properties already added
* by the host,
* such as `fileName`,`lineNumber` on FireFox or `line` on Safari.
* - If any such "extraneous" properties were removed, `sanitizeError` will
* annotate
* the error with them, so they still appear on the causal console
* log output for diagnostic purposes, but not be otherwise visible.
* - `sanitizeError` will ensure that any expected properties already
* added by the host are data
* properties, converting accessor properties to data properties as needed,
* such as `stack` on v8 (Chrome, Brave, Edge?)
* - `sanitizeError` will freeze the error, preventing any correct engine from
* adding or
* altering any of the error's own properties `sanitizeError` is done.
*
* However, `sanitizeError` will not, for example, `harden`
* (i.e., deeply freeze)
* or ensure that the `cause` or `errors` property satisfy the `Passable`
* constraints. The purpose of `sanitizeError` is only to protect against
* mischief the host may have already added to the error as created,
* not to ensure that the error is actually Passable. For that,
* see `toPassableError` in `@endo/pass-style`.
*
* @param {Error} error
*/
export const sanitizeError = error => {
const descs = getOwnPropertyDescriptors(error);
const {
name: _nameDesc,
message: _messageDesc,
errors: _errorsDesc = undefined,
cause: _causeDesc = undefined,
stack: _stackDesc = undefined,
...restDescs
} = descs;
const restNames = ownKeys(restDescs);
if (restNames.length >= 1) {
for (const name of restNames) {
delete error[name];
}
const droppedNote = create(objectPrototype, restDescs);
// eslint-disable-next-line no-use-before-define
note(
error,
redactedDetails`originally with properties ${quote(droppedNote)}`,
);
}
for (const name of ownKeys(error)) {
// @ts-expect-error TS still confused by symbols as property names
const desc = descs[name];
if (desc && objectHasOwnProperty(desc, 'get')) {
defineProperty(error, name, {
value: error[name], // invoke the getter to convert to data property
});
}
}
freeze(error);
};
/**
* @type {AssertionUtilities['error']}
*/
const makeError = (
optDetails = redactedDetails`Assert failed`,
errConstructor = globalThis.Error,
{ errorName = undefined, cause = undefined, errors = undefined } = {},
{
errorName = undefined,
cause = undefined,
errors = undefined,
sanitize = true,
} = {},
) => {

@@ -310,2 +374,5 @@ if (typeof optDetails === 'string') {

}
if (sanitize) {
sanitizeError(error);
}
// The next line is a particularly fruitful place to put a breakpoint.

@@ -334,3 +401,3 @@ return error;

/** @type {AssertNote} */
/** @type {AssertionUtilities['note']} */
const note = (error, detailsNote) => {

@@ -416,3 +483,3 @@ if (typeof detailsNote === 'string') {

/** @type {AssertFail} */
/** @type {AssertionFunctions['fail']} */
const fail = (

@@ -425,2 +492,3 @@ optDetails = assertFailedDetails,

if (optRaise !== undefined) {
// @ts-ignore returns `never` doesn't mean it isn't callable
optRaise(reason);

@@ -432,3 +500,3 @@ }

/** @type {FailTag} */
/** @type {AssertionUtilities['Fail']} */
const Fail = (template, ...args) => fail(details(template, ...args));

@@ -449,3 +517,3 @@

/** @type {AssertEqual} */
/** @type {AssertionFunctions['equal']} */
const equal = (

@@ -467,3 +535,3 @@ actual,

/** @type {AssertTypeof} */
/** @type {AssertionFunctions['typeof']} */
const assertTypeof = (specimen, typename, optDetails) => {

@@ -487,3 +555,3 @@ // This will safely fall through if typename is not a string,

/** @type {AssertString} */
/** @type {AssertionFunctions['string']} */
const assertString = (specimen, optDetails = undefined) =>

@@ -490,0 +558,0 @@ assertTypeof(specimen, 'string', optDetails);

@@ -8,2 +8,4 @@ // @ts-check

/** @import {Assert} from '../../types.js' */
let abandon;

@@ -10,0 +12,0 @@ // Sniff for host-provided functions for terminating the enclosing UOPT (see

@@ -24,2 +24,4 @@ // @ts-check

/** @import {StringablePayload} from '../../types.js' */
/**

@@ -26,0 +28,0 @@ * Joins English terms with commas and an optional conjunction.

@@ -61,10 +61,10 @@ // @ts-check

: typeof globalThis.print === 'function'
? // Make a good-enough console for eshost (including only functions that
// log at a specific level with no special argument interpretation).
// https://console.spec.whatwg.org/#logging
(p => freeze({ debug: p, log: p, info: p, warn: p, error: p }))(
// eslint-disable-next-line no-undef
wrapLogger(globalThis.print),
)
: undefined
? // Make a good-enough console for eshost (including only functions that
// log at a specific level with no special argument interpretation).
// https://console.spec.whatwg.org/#logging
(p => freeze({ debug: p, log: p, info: p, warn: p, error: p }))(
// eslint-disable-next-line no-undef
wrapLogger(globalThis.print),
)
: undefined
);

@@ -71,0 +71,0 @@

// @ts-check
/**
* TypeScript does not treat `AggregateErrorConstructor` as a subtype of
* `ErrorConstructor`, which makes sense because their constructors
* have incompatible signatures. However, we want to parameterize some
* operations by any error constructor, including possible `AggregateError`.
* So we introduce `GenericErrorConstructor` as a common supertype. Any call
* to it to make an instance must therefore first case split on whether the
* constructor is an AggregateErrorConstructor or a normal ErrorConstructor.
*
* @typedef {ErrorConstructor | AggregateErrorConstructor} GenericErrorConstructor
*/
/** @import {GenericErrorConstructor, AssertMakeErrorOptions, DetailsToken, StringablePayload} from '../../types.js' */
/**
* @callback BaseAssert
* The `assert` function itself.
*
* @param {any} flag The truthy/falsy value
* @param {Details} [optDetails] The details to throw
* @param {GenericErrorConstructor} [errConstructor]
* An optional alternate error constructor to use.
* @param {AssertMakeErrorOptions} [options]
* @returns {asserts flag}
*/
/**
* @typedef {object} AssertMakeErrorOptions
* @property {string} [errorName]
* @property {Error} [cause]
* @property {Error[]} [errors]
* Normally only used when the ErrorConstuctor is `AggregateError`
*/
/**
* @callback AssertMakeError
*
* The `assert.error` method, recording details for the console.
*
* The optional `optDetails` can be a string.
* @param {Details} [optDetails] The details of what was asserted
* @param {GenericErrorConstructor} [errConstructor]
* An optional alternate error constructor to use.
* @param {AssertMakeErrorOptions} [options]
* @returns {Error}
*/
/**
* @callback AssertFail
*
* The `assert.fail` method.
*
* Fail an assertion, recording full details to the console and
* raising an exception with a message in which `details` substitution values
* have been redacted.
*
* The optional `optDetails` can be a string for backwards compatibility
* with the nodejs assertion library.
* @param {Details} [optDetails] The details of what was asserted
* @param {GenericErrorConstructor} [errConstructor]
* An optional alternate error constructor to use.
* @param {AssertMakeErrorOptions} [options]
* @returns {never}
*/
/**
* @callback AssertEqual
* The `assert.equal` method
*
* Assert that two values must be `Object.is`.
* @param {any} actual The value we received
* @param {any} expected What we wanted
* @param {Details} [optDetails] The details to throw
* @param {GenericErrorConstructor} [errConstructor]
* An optional alternate error constructor to use.
* @param {AssertMakeErrorOptions} [options]
* @returns {void}
*/
// Type all the overloads of the assertTypeof function.
// There may eventually be a better way to do this, but
// thems the breaks with Typescript 4.0.
/**
* @callback AssertTypeofBigint
* @param {any} specimen
* @param {'bigint'} typename
* @param {Details} [optDetails]
* @returns {asserts specimen is bigint}
*/
/**
* @callback AssertTypeofBoolean
* @param {any} specimen
* @param {'boolean'} typename
* @param {Details} [optDetails]
* @returns {asserts specimen is boolean}
*/
/**
* @callback AssertTypeofFunction
* @param {any} specimen
* @param {'function'} typename
* @param {Details} [optDetails]
* @returns {asserts specimen is Function}
*/
/**
* @callback AssertTypeofNumber
* @param {any} specimen
* @param {'number'} typename
* @param {Details} [optDetails]
* @returns {asserts specimen is number}
*/
/**
* @callback AssertTypeofObject
* @param {any} specimen
* @param {'object'} typename
* @param {Details} [optDetails]
* @returns {asserts specimen is Record<any, any> | null}
*/
/**
* @callback AssertTypeofString
* @param {any} specimen
* @param {'string'} typename
* @param {Details} [optDetails]
* @returns {asserts specimen is string}
*/
/**
* @callback AssertTypeofSymbol
* @param {any} specimen
* @param {'symbol'} typename
* @param {Details} [optDetails]
* @returns {asserts specimen is symbol}
*/
/**
* @callback AssertTypeofUndefined
* @param {any} specimen
* @param {'undefined'} typename
* @param {Details} [optDetails]
* @returns {asserts specimen is undefined}
*/
/**
* The `assert.typeof` method
*
* @typedef {AssertTypeofBigint & AssertTypeofBoolean & AssertTypeofFunction & AssertTypeofNumber & AssertTypeofObject & AssertTypeofString & AssertTypeofSymbol & AssertTypeofUndefined} AssertTypeof
*/
/**
* @callback AssertString
* The `assert.string` method.
*
* `assert.string(v)` is equivalent to `assert.typeof(v, 'string')`. We
* special case this one because it is the most frequently used.
*
* Assert an expected typeof result.
* @param {any} specimen The value to get the typeof
* @param {Details} [optDetails] The details to throw
* @returns {asserts specimen is string}
*/
/**
* @callback AssertNote
* The `assert.note` method.
*
* Annotate an error with details, potentially to be used by an
* augmented console such as the causal console of `console.js`, to
* provide extra information associated with logged errors.
*
* @param {Error} error
* @param {Details} detailsNote
* @returns {void}
*/
// /////////////////////////////////////////////////////////////////////////////
/**
* @typedef {{}} DetailsToken
* A call to the `details` template literal makes and returns a fresh details
* token, which is a frozen empty object associated with the arguments of that
* `details` template literal expression.
*/
/**
* @typedef {string | DetailsToken} Details
* Either a plain string, or made by the `details` template literal tag.
*/
/**
* @typedef {object} StringablePayload
* Holds the payload passed to quote so that its printed form is visible.
* @property {() => string} toString How to print the payload
*/
/**
* To "declassify" and quote a substitution value used in a
* ``` details`...` ``` template literal, enclose that substitution expression
* in a call to `quote`. This makes the value appear quoted
* (as if with `JSON.stringify`) in the message of the thrown error. The
* payload itself is still passed unquoted to the console as it would be
* without `quote`.
*
* For example, the following will reveal the expected sky color, but not the
* actual incorrect sky color, in the thrown error's message:
* ```js
* sky.color === expectedColor || Fail`${sky.color} should be ${quote(expectedColor)}`;
* ```
*
* // TODO Update SES-shim to new convention, where `details` is
* // renamed to `X` rather than `d`.
* The normal convention is to locally rename `details` to `d` and `quote` to `q`
* like `const { details: d, quote: q } = assert;`, so the above example would then be
* ```js
* sky.color === expectedColor || Fail`${sky.color} should be ${q(expectedColor)}`;
* ```
*
* @callback AssertQuote
* @param {any} payload What to declassify
* @param {(string|number)} [spaces]
* @returns {StringablePayload} The declassified payload
*/
/**
* @callback Raise
*
* To make an `assert` which terminates some larger unit of computation
* like a transaction, vat, or process, call `makeAssert` with a `Raise`
* callback, where that callback actually performs that larger termination.
* If possible, the callback should also report its `reason` parameter as
* the alleged reason for the termination.
*
* @param {Error} reason
*/
/**
* @callback MakeAssert
*
* Makes and returns an `assert` function object that shares the bookkeeping
* state defined by this module with other `assert` function objects made by
* `makeAssert`. This state is per-module-instance and is exposed by the
* `loggedErrorHandler` above. We refer to `assert` as a "function object"
* because it can be called directly as a function, but also has methods that
* can be called.
*
* If `optRaise` is provided, the returned `assert` function object will call
* `optRaise(reason)` before throwing the error. This enables `optRaise` to
* engage in even more violent termination behavior, like terminating the vat,
* that prevents execution from reaching the following throw. However, if
* `optRaise` returns normally, which would be unusual, the throw following
* `optRaise(reason)` would still happen.
*
* @param {Raise} [optRaise]
* @param {boolean} [unredacted]
* @returns {Assert}
*/
/**
* @typedef {(template: TemplateStringsArray | string[], ...args: any) => DetailsToken} DetailsTag
*
* Use the `details` function as a template literal tag to create
* informative error messages. The assertion functions take such messages
* as optional arguments:
* ```js
* assert(sky.isBlue(), details`${sky.color} should be "blue"`);
* ```
* // TODO Update SES-shim to new convention, where `details` is
* // renamed to `X` rather than `d`.
* or following the normal convention to locally rename `details` to `d`
* and `quote` to `q` like `const { details: d, quote: q } = assert;`:
* ```js
* assert(sky.isBlue(), d`${sky.color} should be "blue"`);
* ```
* However, note that in most cases it is preferable to instead use the `Fail`
* template literal tag (which has the same input signature as `details`
* but automatically creates and throws an error):
* ```js
* sky.isBlue() || Fail`${sky.color} should be "blue"`;
* ```
*
* The details template tag returns a `DetailsToken` object that can print
* itself with the formatted message in two ways.
* It will report full details to the console, but
* mask embedded substitution values with their typeof information in the thrown error
* to prevent revealing secrets up the exceptional path. In the example
* above, the thrown error may reveal only that `sky.color` is a string,
* whereas the same diagnostic printed to the console reveals that the
* sky was green. This masking can be disabled for an individual substitution value
* using `quote`.
*
* The `raw` property of an input template array is ignored, so a simple
* array of strings may be provided directly.
*/
/**
* @typedef {(template: TemplateStringsArray | string[], ...args: any) => never} FailTag
*
* Use the `Fail` function as a template literal tag to efficiently
* create and throw a `details`-style error only when a condition is not satisfied.
* ```js
* condition || Fail`...complaint...`;
* ```
* This avoids the overhead of creating usually-unnecessary errors like
* ```js
* assert(condition, details`...complaint...`);
* ```
* while improving readability over alternatives like
* ```js
* condition || assert.fail(details`...complaint...`);
* ```
*
* However, due to current weakness in TypeScript, static reasoning
* is less powerful with the `||` patterns than with an `assert` call.
* Until/unless https://github.com/microsoft/TypeScript/issues/51426 is fixed,
* for `||`-style assertions where this loss of static reasoning is a problem,
* instead express the assertion as
* ```js
* if (!condition) {
* Fail`...complaint...`;
* }
* ```
* or, if needed,
* ```js
* if (!condition) {
* // `throw` is noop since `Fail` throws, but it improves static analysis
* throw Fail`...complaint...`;
* }
* ```
*/
/**
* assert that expr is truthy, with an optional details to describe
* the assertion. It is a tagged template literal like
* ```js
* assert(expr, details`....`);`
* ```
*
* The literal portions of the template are assumed non-sensitive, as
* are the `typeof` types of the substitution values. These are
* assembled into the thrown error message. The actual contents of the
* substitution values are assumed sensitive, to be revealed to
* the console only. We assume only the virtual platform's owner can read
* what is written to the console, where the owner is in a privileged
* position over computation running on that platform.
*
* The optional `optDetails` can be a string for backwards compatibility
* with the nodejs assertion library.
*
* @typedef { BaseAssert & {
* typeof: AssertTypeof,
* error: AssertMakeError,
* fail: AssertFail,
* equal: AssertEqual,
* string: AssertString,
* note: AssertNote,
* details: DetailsTag,
* Fail: FailTag,
* quote: AssertQuote,
* bare: AssertQuote,
* makeAssert: MakeAssert,
* } } Assert
*/
// /////////////////////////////////////////////////////////////////////////////
/**
* @typedef {object} VirtualConsole

@@ -371,0 +7,0 @@ * @property {Console['debug']} debug

@@ -28,3 +28,2 @@ // Adapted from SES/Caja - Copyright (C) 2011 Google Inc.

TypeError,
WeakMap,
WeakSet,

@@ -49,6 +48,7 @@ globalThis,

typedArrayPrototype,
weakmapGet,
weakmapSet,
weaksetAdd,
weaksetHas,
FERAL_STACK_GETTER,
FERAL_STACK_SETTER,
isError,
} from './commons.js';

@@ -149,3 +149,2 @@ import { assert } from './error/assert.js';

const toFreeze = new Set();
const paths = new WeakMap();

@@ -156,5 +155,4 @@ // If val is something we should be freezing but aren't yet,

* @param {any} val
* @param {string} [path]
*/
function enqueue(val, path = undefined) {
function enqueue(val) {
if (!isObject(val)) {

@@ -175,3 +173,2 @@ // ignore primitives

setAdd(toFreeze, val);
weakmapSet(paths, val, path);
}

@@ -182,3 +179,3 @@

*/
function freezeAndTraverse(obj) {
const baseFreezeAndTraverse = obj => {
// Now freeze the object to ensure reactive

@@ -202,9 +199,7 @@ // objects such as proxies won't add properties

// something sneaky.
const path = weakmapGet(paths, obj) || 'unknown';
const descs = getOwnPropertyDescriptors(obj);
const proto = getPrototypeOf(obj);
enqueue(proto, `${path}.__proto__`);
enqueue(proto);
arrayForEach(ownKeys(descs), (/** @type {string | symbol} */ name) => {
const pathname = `${path}.${String(name)}`;
// The 'name' may be a symbol, and TypeScript doesn't like us to

@@ -222,23 +217,56 @@ // index arbitrary symbols on objects, so we pretend they're just

if (objectHasOwnProperty(desc, 'value')) {
enqueue(desc.value, `${pathname}`);
enqueue(desc.value);
} else {
enqueue(desc.get, `${pathname}(get)`);
enqueue(desc.set, `${pathname}(set)`);
enqueue(desc.get);
enqueue(desc.set);
}
});
}
};
function dequeue() {
const freezeAndTraverse =
FERAL_STACK_GETTER === undefined && FERAL_STACK_SETTER === undefined
? // On platforms without v8's error own stack accessor problem,
// don't pay for any extra overhead.
baseFreezeAndTraverse
: obj => {
if (isError(obj)) {
// Only pay the overhead if it first passes this cheap isError
// check. Otherwise, it will be unrepaired, but won't be judged
// to be a passable error anyway, so will not be unsafe.
const stackDesc = getOwnPropertyDescriptor(obj, 'stack');
if (
stackDesc &&
stackDesc.get === FERAL_STACK_GETTER &&
stackDesc.configurable
) {
// Can only repair if it is configurable. Otherwise, leave
// unrepaired, in which case it will not be judged passable,
// avoiding a safety problem.
defineProperty(obj, 'stack', {
// NOTE: Calls getter during harden, which seems dangerous.
// But we're only calling the problematic getter whose
// hazards we think we understand.
// @ts-expect-error TS should know FERAL_STACK_GETTER
// cannot be `undefined` here.
// See https://github.com/endojs/endo/pull/2232#discussion_r1575179471
value: apply(FERAL_STACK_GETTER, obj, []),
});
}
}
return baseFreezeAndTraverse(obj);
};
const dequeue = () => {
// New values added before forEach() has finished will be visited.
setForEach(toFreeze, freezeAndTraverse);
}
};
/** @param {any} value */
function markHardened(value) {
const markHardened = value => {
weaksetAdd(hardened, value);
}
};
function commit() {
const commit = () => {
setForEach(toFreeze, markHardened);
}
};

@@ -245,0 +273,0 @@ enqueue(root);

@@ -9,2 +9,3 @@ // For brevity, in this file, as in module-link.js, the term "moduleRecord"

import { getEnvironmentOption as getenv } from '@endo/env-options';
import {

@@ -24,6 +25,7 @@ ReferenceError,

setAdd,
promiseCatch,
promiseThen,
values,
weakmapGet,
generatorNext,
generatorThrow,
} from './commons.js';

@@ -36,2 +38,29 @@ import { assert } from './error/assert.js';

async function asyncTrampoline(generatorFunc, args, errorWrapper) {
const iterator = generatorFunc(...args);
let result = generatorNext(iterator);
while (!result.done) {
try {
// eslint-disable-next-line no-await-in-loop
const val = await result.value;
result = generatorNext(iterator, val);
} catch (error) {
result = generatorThrow(iterator, errorWrapper(error));
}
}
return result.value;
}
function syncTrampoline(generatorFunc, args) {
const iterator = generatorFunc(...args);
let result = generatorNext(iterator);
while (!result.done) {
try {
result = generatorNext(iterator, result.value);
} catch (error) {
result = generatorThrow(iterator, error);
}
}
return result.value;
}
// `makeAlias` constructs compartment specifier tuples for the `aliases`

@@ -66,5 +95,5 @@ // private field of compartments.

staticModuleRecord,
pendingJobs,
enqueueJob,
selectImplementation,
moduleLoads,
errors,
importMeta,

@@ -95,3 +124,3 @@ ) => {

// eslint-disable-next-line no-use-before-define
const dependencyLoaded = memoizedLoadWithErrorAnnotation(
enqueueJob(memoizedLoadWithErrorAnnotation, [
compartmentPrivateFields,

@@ -101,12 +130,6 @@ moduleAliases,

fullSpecifier,
pendingJobs,
enqueueJob,
selectImplementation,
moduleLoads,
errors,
);
setAdd(
pendingJobs,
promiseThen(dependencyLoaded, noop, error => {
arrayPush(errors, error);
}),
);
]);
}

@@ -119,3 +142,3 @@

const loadWithoutErrorAnnotation = async (
function* loadWithoutErrorAnnotation(
compartmentPrivateFields,

@@ -125,10 +148,8 @@ moduleAliases,

moduleSpecifier,
pendingJobs,
enqueueJob,
selectImplementation,
moduleLoads,
errors,
) => {
const { importHook, moduleMap, moduleMapHook, moduleRecords } = weakmapGet(
compartmentPrivateFields,
compartment,
);
) {
const { importHook, importNowHook, moduleMap, moduleMapHook, moduleRecords } =
weakmapGet(compartmentPrivateFields, compartment);

@@ -161,3 +182,3 @@ // Follow moduleMap, or moduleMapHook if present.

// eslint-disable-next-line no-use-before-define
const aliasRecord = await memoizedLoadWithErrorAnnotation(
const aliasRecord = yield memoizedLoadWithErrorAnnotation(
compartmentPrivateFields,

@@ -167,5 +188,5 @@ moduleAliases,

alias.specifier,
pendingJobs,
enqueueJob,
selectImplementation,
moduleLoads,
errors,
);

@@ -180,3 +201,6 @@ mapSet(moduleRecords, moduleSpecifier, aliasRecord);

const staticModuleRecord = await importHook(moduleSpecifier);
const staticModuleRecord = yield selectImplementation(
importHook,
importNowHook,
)(moduleSpecifier);

@@ -212,5 +236,5 @@ if (staticModuleRecord === null || typeof staticModuleRecord !== 'object') {

aliasModuleRecord,
pendingJobs,
enqueueJob,
selectImplementation,
moduleLoads,
errors,
importMeta,

@@ -232,3 +256,3 @@ );

// eslint-disable-next-line no-use-before-define
const aliasRecord = await memoizedLoadWithErrorAnnotation(
const aliasRecord = yield memoizedLoadWithErrorAnnotation(
compartmentPrivateFields,

@@ -238,5 +262,5 @@ moduleAliases,

staticModuleRecord.specifier,
pendingJobs,
enqueueJob,
selectImplementation,
moduleLoads,
errors,
);

@@ -256,9 +280,9 @@ mapSet(moduleRecords, moduleSpecifier, aliasRecord);

staticModuleRecord,
pendingJobs,
enqueueJob,
selectImplementation,
moduleLoads,
errors,
);
};
}
const memoizedLoadWithErrorAnnotation = async (
const memoizedLoadWithErrorAnnotation = (
compartmentPrivateFields,

@@ -268,5 +292,5 @@ moduleAliases,

moduleSpecifier,
pendingJobs,
enqueueJob,
selectImplementation,
moduleLoads,
errors,
) => {

@@ -289,4 +313,5 @@ const { name: compartmentName } = weakmapGet(

moduleLoading = promiseCatch(
loadWithoutErrorAnnotation(
moduleLoading = selectImplementation(asyncTrampoline, syncTrampoline)(
loadWithoutErrorAnnotation,
[
compartmentPrivateFields,

@@ -296,6 +321,6 @@ moduleAliases,

moduleSpecifier,
pendingJobs,
enqueueJob,
selectImplementation,
moduleLoads,
errors,
),
],
error => {

@@ -318,2 +343,60 @@ // eslint-disable-next-line @endo/no-polymorphic-call

function asyncJobQueue() {
/** @type {Set<Promise<undefined>>} */
const pendingJobs = new Set();
/** @type {Array<Error>} */
const errors = [];
/**
* Enqueues a job that starts immediately but won't be awaited until drainQueue is called.
*
* @template {any[]} T
* @param {(...args: T)=>Promise<*>} func
* @param {T} args
*/
const enqueueJob = (func, args) => {
setAdd(
pendingJobs,
promiseThen(func(...args), noop, error => {
arrayPush(errors, error);
}),
);
};
/**
* Sequentially awaits pending jobs and returns an array of errors
*
* @returns {Promise<Array<Error>>}
*/
const drainQueue = async () => {
for (const job of pendingJobs) {
// eslint-disable-next-line no-await-in-loop
await job;
}
return errors;
};
return { enqueueJob, drainQueue };
}
/**
* @param {object} options
* @param {Array<Error>} options.errors
* @param {string} options.errorPrefix
*/
function throwAggregateError({ errors, errorPrefix }) {
// Throw an aggregate error if there were any errors.
if (errors.length > 0) {
const verbose =
getenv('COMPARTMENT_LOAD_ERRORS', '', ['verbose']) === 'verbose';
throw TypeError(
`${errorPrefix} (${errors.length} underlying failures: ${arrayJoin(
arrayMap(errors, error => error.message + (verbose ? error.stack : '')),
', ',
)}`,
);
}
}
const preferSync = (_asyncImpl, syncImpl) => syncImpl;
const preferAsync = (asyncImpl, _syncImpl) => asyncImpl;
/*

@@ -337,10 +420,8 @@ * `load` asynchronously gathers the `StaticModuleRecord`s for a module and its

/** @type {Set<Promise<undefined>>} */
const pendingJobs = new Set();
/** @type {Map<object, Map<string, Promise<Record<any, any>>>>} */
const moduleLoads = new Map();
/** @type {Array<Error>} */
const errors = [];
const dependencyLoaded = memoizedLoadWithErrorAnnotation(
const { enqueueJob, drainQueue } = asyncJobQueue();
enqueueJob(memoizedLoadWithErrorAnnotation, [
compartmentPrivateFields,

@@ -350,33 +431,66 @@ moduleAliases,

moduleSpecifier,
pendingJobs,
enqueueJob,
preferAsync,
moduleLoads,
]);
// Drain pending jobs queue and throw an aggregate error
const errors = await drainQueue();
throwAggregateError({
errors,
errorPrefix: `Failed to load module ${q(moduleSpecifier)} in package ${q(
compartmentName,
)}`,
});
};
/*
* `loadNow` synchronously gathers the `StaticModuleRecord`s for a module and its
* transitive dependencies.
* The module records refer to each other by a reference to the dependency's
* compartment and the specifier of the module within its own compartment.
* This graph is then ready to be synchronously linked and executed.
*/
export const loadNow = (
compartmentPrivateFields,
moduleAliases,
compartment,
moduleSpecifier,
) => {
const { name: compartmentName } = weakmapGet(
compartmentPrivateFields,
compartment,
);
setAdd(
pendingJobs,
promiseThen(dependencyLoaded, noop, error => {
/** @type {Map<object, Map<string, Promise<Record<any, any>>>>} */
const moduleLoads = new Map();
/** @type {Array<Error>} */
const errors = [];
const enqueueJob = (func, args) => {
try {
func(...args);
} catch (error) {
arrayPush(errors, error);
}),
);
}
};
// Drain pending jobs queue.
// Each job is a promise for undefined, regardless of success or failure.
// Before we add a job to the queue, we catch any error and push it into the
// `errors` accumulator.
for (const job of pendingJobs) {
// eslint-disable-next-line no-await-in-loop
await job;
}
enqueueJob(memoizedLoadWithErrorAnnotation, [
compartmentPrivateFields,
moduleAliases,
compartment,
moduleSpecifier,
enqueueJob,
preferSync,
moduleLoads,
]);
// Throw an aggregate error if there were any errors.
if (errors.length > 0) {
throw TypeError(
`Failed to load module ${q(moduleSpecifier)} in package ${q(
compartmentName,
)} (${errors.length} underlying failures: ${arrayJoin(
arrayMap(errors, error => error.message),
', ',
)}`,
);
}
throwAggregateError({
errors,
errorPrefix: `Failed to load module ${q(moduleSpecifier)} in package ${q(
compartmentName,
)}`,
});
};

@@ -6,2 +6,4 @@ /* eslint-disable no-restricted-globals */

/** @import {GenericErrorConstructor} from '../types.js' */
/**

@@ -8,0 +10,0 @@ * @file Exports {@code whitelist}, a recursively defined

@@ -8,2 +8,3 @@ import {

arrayMap,
functionBind,
} from './commons.js';

@@ -41,7 +42,5 @@

const SharedSymbol = {
Symbol(description) {
return OriginalSymbol(description);
},
}.Symbol;
// Bypass Hermes bug, fixed in: https://github.com/facebook/hermes/commit/00f18c89c720e1c34592bb85a1a8d311e6e99599
// Make a "copy" of the primordial [Symbol "constructor"](https://tc39.es/ecma262/#sec-symbol-description) which maintains all observable behavior. The primordial explicitly throws on `[[Construct]]` and has a `[[Call]]` which ignores the receiver. Binding also maintains the `toString` source as a native function. The `name` is restored below when copying own properties.
const SharedSymbol = functionBind(Symbol, undefined);

@@ -48,0 +47,0 @@ defineProperties(SymbolPrototype, {

@@ -98,2 +98,3 @@ /**

export type ImportHook = (moduleSpecifier: string) => Promise<StaticModuleType>;
export type ImportNowHook = (moduleSpecifier: string) => StaticModuleType;

@@ -105,2 +106,3 @@ export interface CompartmentOptions {

importHook?: ImportHook;
importNowHook?: ImportNowHook;
resolveHook?: ResolveHook;

@@ -118,12 +120,45 @@ __shimTransforms__?: Array<Transform>;

// The DetailsToken is an empty object literal.
/**
* A call to the `details` template literal makes and returns a fresh details
* token, which is a frozen empty object associated with the arguments of that
* `details` template literal expression.
*/
export type DetailsToken = Record<any, never>;
/** Either a plain string, or made by the `details` template literal tag. */
export type Details = string | DetailsToken;
export interface AssertMakeErrorOptions {
/**
* Does not affect the error.name property. That remains determined by
* the constructor. Rather, the `errorName` determines how this error is
* identified in the causal console log's output.
*/
errorName?: string;
/**
* Discloses the error that caused this one, typically from a lower
* layer of abstraction. This is represented by a public `cause` data property
* on the error, not a hidden annotation.
*/
cause?: Error;
/**
* Normally only used when the ErrorConstuctor is `AggregateError`, to
* represent the set of prior errors aggregated together in this error,
* typically by `Promise.any`. But `makeError` allows it on any error.
* This is represented by a public `errors` data property on the error,
* not a hidden annotation.
*/
errors?: Error[];
/**
* Defaults to true. If true, `makeError` will apply `sanitizeError`
* to the error before returning it. See the comments on
* {@link sanitizeError}.
*/
sanitize?: boolean;
}
// TODO inline overloading
type AssertTypeofBigint = (

@@ -179,6 +214,15 @@ specimen: any,

interface ToStringable {
interface StringablePayload {
toString(): string;
}
/**
* TypeScript does not treat `AggregateErrorConstructor` as a subtype of
* `ErrorConstructor`, which makes sense because their constructors
* have incompatible signatures. However, we want to parameterize some
* operations by any error constructor, including possible `AggregateError`.
* So we introduce `GenericErrorConstructor` as a common supertype. Any call
* to it to make an instance must therefore first case split on whether the
* constructor is an AggregateErrorConstructor or a normal ErrorConstructor.
*/
export type GenericErrorConstructor =

@@ -188,3 +232,26 @@ | ErrorConstructor

/**
* To make an `assert` which terminates some larger unit of computation
* like a transaction, vat, or process, call `makeAssert` with a `Raise`
* callback, where that callback actually performs that larger termination.
* If possible, the callback should also report its `reason` parameter as
* the alleged reason for the termination.
*/
export type Raise = (reason: Error) => void;
/**
* Makes and returns an `assert` function object that shares the bookkeeping
* state defined by this module with other `assert` function objects made by
* `makeAssert`. This state is per-module-instance and is exposed by the
* `loggedErrorHandler` above. We refer to `assert` as a "function object"
* because it can be called directly as a function, but also has methods that
* can be called.
*
* If `optRaise` is provided, the returned `assert` function object will call
* `optRaise(reason)` before throwing the error. This enables `optRaise` to
* engage in even more violent termination behavior, like terminating the vat,
* that prevents execution from reaching the following throw. However, if
* `optRaise` returns normally, which would be unusual, the throw following
* `optRaise(reason)` would still happen.
*/
// Behold: recursion.

@@ -194,20 +261,60 @@ // eslint-disable-next-line no-use-before-define

export interface AssertionFunctions {
(
value: any,
details?: Details,
errConstructor?: GenericErrorConstructor,
options?: AssertMakeErrorOptions,
): asserts value;
export type BaseAssert = (
/** The truthy/falsy value we're testing */
flag: any,
/** The details of what was asserted */
details?: Details,
/** An optional alternate error constructor to use */
errConstructor?: GenericErrorConstructor,
options?: AssertMakeErrorOptions,
) => asserts flag;
export interface AssertionFunctions extends BaseAssert {
typeof: AssertTypeof;
/**
* The `assert.equal` method
*
* Assert that two values must be `Object.is`.
*/
equal(
left: any,
right: any,
/** What we received */
actual: any,
/** What we wanted */
expected: any,
/** The details of what was asserted */
details?: Details,
/** An optional alternate error constructor to use */
errConstructor?: GenericErrorConstructor,
options?: AssertMakeErrorOptions,
): void;
string(specimen: any, details?: Details): asserts specimen is string;
/**
* The `assert.string` method.
*
* `assert.string(v)` is equivalent to `assert.typeof(v, 'string')`. We
* special case this one because it is the most frequently used.
*
* Assert an expected typeof result.
*/
string(
specimen: any,
/** The details of what was asserted */
details?: Details,
): asserts specimen is string;
/**
* The `assert.fail` method.
*
* Fail an assertion, recording full details to the console and
* raising an exception with a message in which `details` substitution values
* have been redacted.
*
* The optional `optDetails` can be a string for backwards compatibility
* with the nodejs assertion library.
*/
fail(
/** The details of what was asserted */
details?: Details,
/** An optional alternate error constructor to use */
errConstructor?: GenericErrorConstructor,

@@ -219,8 +326,58 @@ options?: AssertMakeErrorOptions,

export interface AssertionUtilities {
/**
* Aka the `makeError` function as imported from `@endo/errors`
*
* Recording unredacted details for the console.
*/
error(
/** The details of what was asserted */
details?: Details,
/** An optional alternate error constructor to use */
errConstructor?: GenericErrorConstructor,
options?: AssertMakeErrorOptions,
): Error;
/**
* Aka the `annotateError` function as imported from `@endo/errors`
*
* Annotate an error with details, potentially to be used by an
* augmented console such as the causal console of `console.js`, to
* provide extra information associated with logged errors.
*/
note(error: Error, details: Details): void;
/**
* Use the `details` function as a template literal tag to create
* informative error messages. The assertion functions take such messages
* as optional arguments:
* ```js
* assert(sky.isBlue(), details`${sky.color} should be "blue"`);
* ```
* // TODO Update SES-shim to new convention, where `details` is
* // renamed to `X` rather than `d`.
* or following the normal convention to locally rename `details` to `d`
* and `quote` to `q` like `const { details: d, quote: q } = assert;`:
* ```js
* assert(sky.isBlue(), d`${sky.color} should be "blue"`);
* ```
* However, note that in most cases it is preferable to instead use the `Fail`
* template literal tag (which has the same input signature as `details`
* but automatically creates and throws an error):
* ```js
* sky.isBlue() || Fail`${sky.color} should be "blue"`;
* ```
*
* The details template tag returns a `DetailsToken` object that can print
* itself with the formatted message in two ways.
* It will report full details to the console, but
* mask embedded substitution values with their typeof information in the thrown error
* to prevent revealing secrets up the exceptional path. In the example
* above, the thrown error may reveal only that `sky.color` is a string,
* whereas the same diagnostic printed to the console reveals that the
* sky was green. This masking can be disabled for an individual substitution value
* using `quote`.
*
* The `raw` property of an input template array is ignored, so a simple
* array of strings may be provided directly.
*/
details(

@@ -230,5 +387,85 @@ template: TemplateStringsArray | string[],

): DetailsToken;
/**
* Use the `Fail` function as a template literal tag to efficiently
* create and throw a `details`-style error only when a condition is not satisfied.
* ```js
* condition || Fail`...complaint...`;
* ```
* This avoids the overhead of creating usually-unnecessary errors like
* ```js
* assert(condition, details`...complaint...`);
* ```
* while improving readability over alternatives like
* ```js
* condition || assert.fail(details`...complaint...`);
* ```
*
* However, due to current weakness in TypeScript, static reasoning
* is less powerful with the `||` patterns than with an `assert` call.
* Until/unless https://github.com/microsoft/TypeScript/issues/51426 is fixed,
* for `||`-style assertions where this loss of static reasoning is a problem,
* instead express the assertion as
* ```js
* if (!condition) {
* Fail`...complaint...`;
* }
* ```
* or, if needed,
* ```js
* if (!condition) {
* // `throw` is noop since `Fail` throws, but it improves static analysis
* throw Fail`...complaint...`;
* }
* ```
*/
Fail(template: TemplateStringsArray | string[], ...args: any): never;
quote(payload: any, spaces?: string | number): ToStringable;
bare(payload: any, spaces?: string | number): ToStringable;
/**
* To "declassify" and quote a substitution value used in a
* ``` details`...` ``` template literal, enclose that substitution expression
* in a call to `quote`. This makes the value appear quoted
* (as if with `JSON.stringify`) in the message of the thrown error. The
* payload itself is still passed unquoted to the console as it would be
* without `quote`.
*
* For example, the following will reveal the expected sky color, but not the
* actual incorrect sky color, in the thrown error's message:
* ```js
* sky.color === expectedColor || Fail`${sky.color} should be ${quote(expectedColor)}`;
* ```
*
* // TODO Update SES-shim to new convention, where `details` is
* // renamed to `X` rather than `d`.
* The normal convention is to locally rename `details` to `d` and `quote` to `q`
* like `const { details: d, quote: q } = assert;`, so the above example would then be
* ```js
* sky.color === expectedColor || Fail`${sky.color} should be ${q(expectedColor)}`;
* ```
*/
quote(
/** What to declassify */
payload: any,
spaces?: string | number,
): /** The declassified and quoted payload */ StringablePayload;
/**
* 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]"`).
*/
bare(
/** What to declassify */
payload: any,
spaces?: string | number,
): /** The declassified payload without quotes (beware confusion hazard) */
StringablePayload;
}

@@ -240,2 +477,20 @@

/**
* assert that expr is truthy, with an optional details to describe
* the assertion. It is a tagged template literal like
* ```js
* assert(expr, details`....`);`
* ```
*
* The literal portions of the template are assumed non-sensitive, as
* are the `typeof` types of the substitution values. These are
* assembled into the thrown error message. The actual contents of the
* substitution values are assumed sensitive, to be revealed to
* the console only. We assume only the virtual platform's owner can read
* what is written to the console, where the owner is in a privileged
* position over computation running on that platform.
*
* The optional `optDetails` can be a string for backwards compatibility
* with the nodejs assertion library.
*/
export type Assert = AssertionFunctions &

@@ -242,0 +497,0 @@ AssertionUtilities &

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc