New:Socket for Asana Is Now Available.Learn more
Sign In

ses

Package Overview
Dependencies
Maintainers
8
Versions
116
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
2.0.0
to
2.1.0
+29
src-xs/commons.js
/**
* @module In the spirit of ../src/commons.js, this module captures native
* functions specific to the XS engine during initialization, so vetted shims
* are free to modify any intrinsic without risking the integrity of SES.
*/
/// <reference types="ses"/>
import {
getOwnPropertyDescriptor,
globalThis,
uncurryThis,
} from '../src/commons.js';
/** @type {typeof Compartment} */
export const NativeStartCompartment = /** @type {any} */ (globalThis)
.Compartment;
export const nativeCompartmentPrototype = NativeStartCompartment.prototype;
export const nativeImport = uncurryThis(nativeCompartmentPrototype.import);
export const nativeImportNow = uncurryThis(
nativeCompartmentPrototype.importNow,
);
/** @type {(compartment: any, source: string) => unknown} */
export const nativeEvaluate = uncurryThis(nativeCompartmentPrototype.evaluate);
/** @type {(compartment: typeof Compartment) => typeof globalThis} */
export const nativeGetGlobalThis = uncurryThis(
// @ts-expect-error we know it is there on XS
getOwnPropertyDescriptor(nativeCompartmentPrototype, 'globalThis').get,
);
/**
* @module Provides a XS-specific variation on the behavior of
* ../compartment-shim.js, completing the story that begins in
* ./compartment.js, adding a Compartment constructor adapter to the global
* scope and transforming all of the methods of a native compartment into
* thunks that will alternately delegate to its native or shim behaviors
* depending on the __native__ Compartment constructor option.
*/
/// <reference types="ses"/>
import { defineProperty, globalThis, weakmapGet } from '../src/commons.js';
import {
NativeStartCompartment,
nativeCompartmentPrototype,
nativeImport,
nativeImportNow,
nativeEvaluate,
nativeGetGlobalThis,
} from './commons.js';
import {
ShimStartCompartment,
adaptCompartmentConstructors,
privateFields,
shimEvaluate,
shimGetGlobalThis,
shimImport,
shimImportNow,
} from './compartment.js';
const adapterFunctions = {
evaluate(source, options) {
const fields = weakmapGet(privateFields, this);
if (fields === undefined) {
return nativeEvaluate(this, source);
}
const { delegateNative } = fields;
if (delegateNative) {
const { transforms, nativeEval } = fields;
for (let i = 0; i < transforms.length; i += 1) {
const transform = transforms[i];
source = transform(source);
}
return nativeEval(source);
} else {
return shimEvaluate(this, source, options);
}
},
async import(specifier) {
await null;
const fields = weakmapGet(privateFields, this);
if (fields === undefined) {
return nativeImport(this, specifier);
}
const { noNamespaceBox, delegateNative } = fields;
const delegateImport = delegateNative ? nativeImport : shimImport;
const namespace = delegateImport(this, specifier);
return noNamespaceBox ? namespace : { namespace: await namespace };
},
importNow(specifier) {
const fields = weakmapGet(privateFields, this);
if (fields === undefined) {
return nativeImportNow(this, specifier);
}
const { delegateNative } = fields;
const delegateImportNow = delegateNative ? nativeImportNow : shimImportNow;
return delegateImportNow(this, specifier);
},
};
defineProperty(nativeCompartmentPrototype, 'evaluate', {
value: adapterFunctions.evaluate,
writable: true,
configurable: true,
enumerable: false,
});
defineProperty(nativeCompartmentPrototype, 'import', {
value: adapterFunctions.import,
writable: true,
configurable: true,
enumerable: false,
});
defineProperty(nativeCompartmentPrototype, 'importNow', {
value: adapterFunctions.importNow,
writable: true,
configurable: true,
enumerable: false,
});
defineProperty(nativeCompartmentPrototype, 'globalThis', {
get() {
const fields = weakmapGet(privateFields, this);
if (fields === undefined) {
return nativeGetGlobalThis(this);
}
const { delegateNative } = fields;
const delegateGetGlobalThis = delegateNative
? nativeGetGlobalThis
: shimGetGlobalThis;
return delegateGetGlobalThis(this);
},
configurable: true,
enumerable: false,
});
defineProperty(nativeCompartmentPrototype, 'name', {
get() {
const fields = weakmapGet(privateFields, this);
if (fields === undefined) {
return undefined;
}
const { name } = fields;
return name;
},
configurable: true,
enumerable: false,
});
// Adapt the start compartment's native Compartment to the SES-compatibility
// adapter.
// Before Lockdown, the Compartment constructor in transitive child
// Compartments is not (and cannot be) hardened.
const noHarden = object => object;
// @ts-expect-error TypeScript is not inferring from the types above that
// Compartment is on globalThis.
globalThis.Compartment = adaptCompartmentConstructors(
NativeStartCompartment,
ShimStartCompartment,
noHarden,
);
/** @import {ModuleDescriptor} from '../types.js' */
import {
Map,
Object,
SyntaxError,
TypeError,
WeakMap,
arrayMap,
create,
defineProperty,
entries,
fromEntries,
getOwnPropertyDescriptor,
globalThis,
mapDelete,
mapGet,
mapSet,
uncurryThis,
weakmapGet,
weakmapSet,
} from '../src/commons.js';
import { nativeGetGlobalThis } from './commons.js';
import {
makeCompartmentConstructor,
compartmentOptions,
} from '../src/compartment.js';
import { getGlobalIntrinsics } from '../src/intrinsics.js';
import { tameFunctionToString } from '../src/tame-function-tostring.js';
import { chooseReporter } from '../src/reporting.js';
import { makeError } from '../src/error/assert.js';
/**
* @import {CompartmentOptionsArgs, LegacyCompartmentOptionsArgs} from '../src/compartment.js'
*/
const muteReporter = chooseReporter('none');
export const ShimStartCompartment = makeCompartmentConstructor(
makeCompartmentConstructor,
getGlobalIntrinsics(globalThis, muteReporter),
tameFunctionToString(),
);
export const shimCompartmentPrototype = ShimStartCompartment.prototype;
export const shimEvaluate = uncurryThis(shimCompartmentPrototype.evaluate);
export const shimImport = uncurryThis(shimCompartmentPrototype.import);
export const shimImportNow = uncurryThis(shimCompartmentPrototype.importNow);
/** @type {(compartment: typeof Compartment) => typeof globalThis} */
export const shimGetGlobalThis = uncurryThis(
// @ts-expect-error The descriptor will never be undefined.
getOwnPropertyDescriptor(ShimStartCompartment.prototype, 'globalThis').get,
);
/**
* @typedef {{
* name: string,
* transforms: Array<(source: string) => string>,
* delegateNative: boolean,
* noNamespaceBox: boolean,
* nativeEval: typeof eval,
* descriptors: Map<string, ModuleDescriptor>,
* }} PrivateFields
*/
/** @type {WeakMap<object, PrivateFields>} */
export const privateFields = new WeakMap();
const adaptVirtualModuleSource = ({
execute,
imports = [],
exports = [],
reexports = [],
}) => {
const resolutions = create(null);
let i = 0;
return {
execute(environment) {
const fakeCompartment = {
importNow(specifier) {
return environment[specifier];
},
};
execute(environment, fakeCompartment, resolutions);
},
bindings: [
...arrayMap(imports, specifier => {
const resolved = `_${i}`;
i += 1;
resolutions[specifier] = resolved;
return { importAllFrom: specifier, as: resolved };
}),
...arrayMap(reexports, specifier => ({ exportAllFrom: specifier })),
...arrayMap(exports, name => ({ export: name })),
],
};
};
const adaptModuleSource = source => {
if (source.execute) {
return adaptVirtualModuleSource(source);
}
// eslint-disable-next-line no-underscore-dangle
if (source.__syncModuleProgram__) {
throw makeError(
'XS native compartments do not support precompiled module sources',
SyntaxError,
);
}
return source;
};
const adaptModuleDescriptor = (
descriptor,
specifier,
compartment = undefined,
) => {
if (Object(descriptor) !== descriptor) {
throw makeError('Module descriptor must be an object', TypeError);
}
if (descriptor.namespace !== undefined) {
return descriptor;
}
if (descriptor.source !== undefined) {
return {
source: adaptModuleSource(descriptor.source),
importMeta: descriptor.importMeta,
specifier: descriptor.specifier,
};
}
// Legacy support for record descriptors.
if (descriptor.record !== undefined) {
if (
descriptor.specifier === specifier ||
descriptor.specifier === undefined
) {
return {
source: adaptModuleSource(descriptor.record),
specifier,
importMeta: descriptor.importMeta,
};
} else {
if (compartment === undefined) {
throw makeError(
'Cannot construct forward reference module descriptor in module map',
TypeError,
);
}
const compartmentPrivateFields = weakmapGet(privateFields, compartment);
if (compartmentPrivateFields === undefined) {
throw makeError(
'Module descriptor compartment is not a recognizable compartment',
TypeError,
);
}
const { descriptors } = compartmentPrivateFields;
mapSet(descriptors, descriptor.specifier, {
compartment,
namespace: specifier,
});
return {
source: adaptModuleSource(descriptor.record),
specifier: descriptor.specifier,
importMeta: descriptor.importMeta,
};
}
}
if (descriptor.specifier !== undefined) {
return {
namespace: descriptor.specifier,
compartment: descriptor.compartment,
};
}
// Legacy support for a source in the place of a descriptor.
return { source: adaptModuleSource(descriptor) };
};
export const adaptCompartmentConstructors = (
NativeCompartment,
ShimCompartment,
maybeHarden,
) => {
/** @param {CompartmentOptionsArgs|LegacyCompartmentOptionsArgs} args */
function Compartment(...args) {
const options = compartmentOptions(...args);
const {
name = undefined,
globals = {},
transforms = [],
resolveHook = () => {
throw makeError('Compartment requires a resolveHook', TypeError);
},
loadHook = undefined,
loadNowHook = undefined,
importHook = loadHook,
importNowHook = loadNowHook,
moduleMapHook = () => {},
__native__: delegateNative = false,
__noNamespaceBox__: noNamespaceBox = false,
} = options;
const modules = delegateNative
? fromEntries(
arrayMap(
entries(options.modules ?? {}),
// Uses desctructuring to avoid invoking iterator protocol and deny
// vetted shims the opportunity to interfere.
({ 0: specifier, 1: descriptor }) => [
specifier,
adaptModuleDescriptor(descriptor, specifier, undefined),
],
),
)
: {};
// side table for references from one descriptor to another
const descriptors = new Map();
let nativeOptions = { globals, modules };
if (importHook) {
/** @param {string} specifier */
const nativeImportHook = async specifier => {
await null;
let descriptor =
mapGet(descriptors, specifier) ??
moduleMapHook(specifier) ??
(await importHook(specifier));
mapDelete(descriptors, specifier);
// eslint-disable-next-line no-use-before-define
descriptor = adaptModuleDescriptor(descriptor, specifier, compartment);
return descriptor;
};
nativeOptions = {
...nativeOptions,
resolveHook,
importHook: nativeImportHook,
loadHook: nativeImportHook,
};
}
if (importNowHook) {
/** @param {string} specifier */
const nativeImportNowHook = specifier => {
let descriptor =
mapGet(descriptors, specifier) ??
moduleMapHook(specifier) ??
importNowHook(specifier);
mapDelete(descriptors, specifier);
// eslint-disable-next-line no-use-before-define
descriptor = adaptModuleDescriptor(descriptor, specifier, compartment);
return descriptor;
};
nativeOptions = {
...nativeOptions,
resolveHook,
importNowHook: nativeImportNowHook,
loadNowHook: nativeImportNowHook,
};
}
const compartment = new NativeCompartment(nativeOptions);
const nativeGlobalThis = nativeGetGlobalThis(compartment);
const nativeEval = nativeGlobalThis.eval;
weakmapSet(privateFields, compartment, {
name,
transforms,
delegateNative,
noNamespaceBox,
nativeEval,
descriptors,
});
const shimOptions = {
...options,
__noNamespaceBox__: true,
__options__: true,
};
uncurryThis(ShimCompartment)(compartment, shimOptions);
const shimGlobalThis = shimGetGlobalThis(compartment);
const ChildCompartment = adaptCompartmentConstructors(
// @ts-expect-error incomplete type information for XS
nativeGlobalThis.Compartment,
// @ts-expect-error incomplete type information for XS
shimGlobalThis.Compartment,
maybeHarden,
);
defineProperty(nativeGlobalThis, 'Compartment', {
value: ChildCompartment,
writable: true,
configurable: true,
enumerable: false,
});
defineProperty(shimGlobalThis, 'Compartment', {
value: ChildCompartment,
writable: true,
configurable: true,
enumerable: false,
});
maybeHarden(compartment);
return compartment;
}
maybeHarden(Compartment);
return Compartment;
};
/**
* @module This is an alternate implementation of ../index.js to provide
* access to the native implementation of Hardened JavaScript on the XS
* engine, but adapted for backward compatibility with SES.
* This module can only be reached in the presence of the package export/import
* condition "xs", and should only be used to bundle an XS-specific version of
* SES.
*/
// @ts-nocheck
/// <refs types="../types.js"/>
import { Object, freeze } from '../src/commons.js';
// These are the constituent shims in an arbitrary order, but matched
// to ../index.js to remove doubt.
import './lockdown-shim.js';
import './compartment-shim.js';
import '../src/assert-shim.js';
import '../src/console-shim.js';
// XS Object.freeze takes a second argument to apply freeze transitively, but
// with slightly different effects than `harden`.
// We disable this behavior to encourage use of `harden` for portable Hardened
// JavaScript.
// The pattern of creating and extracting preserves Object.freeze.name.
/** @param {object} object */
Object.freeze = { freeze: object => freeze(object) }.freeze;
/**
* @module Alters the XS implementation of Lockdown to be backward compatible
* with SES, providing Compartment constructors in every Compartment that can
* be used with either native ModuleSources or module sources pre-compiled for
* the SES Compartment, depending on the __native__ Compartment constructor
* option.
*/
import { globalThis } from '../src/commons.js';
import { NativeStartCompartment } from './commons.js';
import { repairIntrinsics } from '../src/lockdown.js';
import {
ShimStartCompartment,
adaptCompartmentConstructors,
} from './compartment.js';
const lockdown = options => {
const hardenIntrinsics = repairIntrinsics(options);
hardenIntrinsics();
// Replace global Compartment with a version that is hardened and hardens
// transitive child Compartment.
// @ts-expect-error Incomplete global type on XS.
globalThis.Compartment = adaptCompartmentConstructors(
NativeStartCompartment,
ShimStartCompartment,
harden,
);
};
globalThis.lockdown = lockdown;
+5
-39
{
"name": "ses",
"version": "2.0.0",
"version": "2.1.0",
"description": "Hardened JavaScript for Fearless Cooperation",

@@ -21,3 +21,3 @@ "keywords": [

],
"author": "Agoric",
"author": "Endo contributors",
"license": "Apache-2.0",

@@ -79,20 +79,2 @@ "homepage": "https://github.com/Agoric/SES-shim/tree/master/packages/ses#readme",

},
"scripts": {
"build:vanilla": "node scripts/bundle.js",
"build:hermes": "node scripts/bundle.js hermes",
"build": "yarn build:vanilla && yarn build:hermes",
"clean": "rm -rf dist",
"cover": "c8 ava --config test/_ava-ses.config.js",
"demo": "python3 -m http.server",
"lint": "yarn lint:types && yarn lint:eslint",
"lint-fix": "eslint --fix .",
"lint:eslint": "eslint .",
"lint:types": "tsc",
"prepare": "npm run clean && npm run build",
"qt": "ava",
"test": "tsd && ava",
"test:hermes": "./scripts/hermes-test.sh",
"test:xs": "xst dist/ses.umd.js test/_lockdown-safe.js && node scripts/generate-test-xs.js && xst tmp/test-xs.js && rm -rf tmp",
"postpack": "git clean -fX -e node_modules/"
},
"dependencies": {

@@ -103,20 +85,4 @@ "@endo/cache-map": "^1.1.0",

},
"devDependencies": {
"@babel/generator": "^7.28.3",
"@babel/parser": "~7.28.3",
"@babel/traverse": "~7.28.3",
"@babel/types": "~7.28.2",
"@endo/compartment-mapper": "^2.1.0",
"@endo/module-source": "^1.4.1",
"@endo/test262-runner": "^0.1.50",
"ava": "catalog:dev",
"c8": "catalog:dev",
"core-js": "^3.31.0",
"eslint": "catalog:dev",
"hermes-engine-cli": "^0.12.0",
"terser": "^5.16.6",
"tsd": "catalog:dev",
"typescript": "catalog:dev"
},
"files": [
"./*.d.js",
"./*.d.ts",

@@ -130,2 +96,3 @@ "./*.js",

"src",
"src-xs",
"tools"

@@ -149,4 +116,3 @@ ],

"atLeast": 81.17
},
"gitHead": "c3616c39c35f2d052f7083aba31054910951beb4"
}
}
import { TypeError } from './commons.js';
/** getThis returns globalThis in sloppy mode or undefined in strict mode. */
/** @this {unknown} */
// getThis returns globalThis in sloppy mode or undefined in strict mode.
function getThis() {

@@ -5,0 +6,0 @@ return this;

@@ -55,2 +55,3 @@ import { hasOwn } from './commons.js';

} catch (err) {
const reason = /** @type {string | object} */ (err);
if (hasOwn(obj, prop)) {

@@ -64,5 +65,5 @@ if (typeof obj === 'function' && prop === 'prototype') {

}
error(`failed to delete ${subPath}`, err);
error(`failed to delete ${subPath}`, reason);
} else {
error(`deleting ${subPath} threw`, err);
error(`deleting ${subPath} threw`, reason);
}

@@ -69,0 +70,0 @@ throw err;

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

entries,
freeze,
getOwnPropertyDescriptor,

@@ -86,2 +85,13 @@ getOwnPropertyDescriptors,

/**
* For use with any value except a regular expression (@see {@link sealRegexp}
* and {@link freezeRegexp}).
*
* @type { &
* ((obj: RegExp) => void) &
* typeof Object.freeze
* }
*/
export const freeze = /** @type {any} */ (Object.freeze);
export const {

@@ -93,2 +103,3 @@ species: speciesSymbol,

replace: replaceSymbol,
search: searchSymbol,
unscopables: unscopablesSymbol,

@@ -99,4 +110,6 @@ keyFor: symbolKeyFor,

export const { isInteger } = Number;
export const { max, min, trunc } = Math;
export const { MAX_SAFE_INTEGER, isInteger } = Number;
export const { stringify: stringifyJson } = JSON;

@@ -142,2 +155,3 @@

export const { prototype: arrayBufferPrototype } = ArrayBuffer;
export const { prototype: dataViewPrototype } = DataView;
export const { prototype: mapPrototype } = Map;

@@ -244,2 +258,3 @@ export const { revocable: proxyRevocable } = Proxy;

export const regexpExec = uncurryThis(regexpPrototype.exec);
/**

@@ -254,5 +269,18 @@ * @type { &

);
/**
* `regexpSearch` can be used with a frozen non-global non-sticky RegExp.
*
* @type {(thisArg: RegExp, string: string) => number}
*/
export const regexpSearch = uncurryThis(regexpPrototype[searchSymbol]);
/**
* `matchAll` internally creates a new RegExp vulnerable to prototype poisoning,
* but this function is still needed to reach %RegExpStringIteratorPrototype%.
* @private
*/
export const matchAllRegExp = uncurryThis(regexpPrototype[matchAllSymbol]);
const { _regexpConstructor, ...regexpDescriptors } =
getOwnPropertyDescriptors(regexpPrototype);
const regexpDescriptors = getOwnPropertyDescriptors(regexpPrototype);
arrayForEach(ownKeys(regexpDescriptors), key => {

@@ -263,5 +291,17 @@ const desc = regexpDescriptors[/** @type {any} */ (key)];

});
// Don't follow Symbol.species
// https://tc39.es/ecma262/multipage/abstract-operations.html#sec-speciesconstructor
defineProperty(regexpDescriptors, 'constructor', {
value: undefined,
enumerable: false,
configurable: false,
writable: false,
});
/**
* Protect a RegExp instance against RegExp.prototype poisoning ("exec",
* "flags", Symbol.replace, etc.).
* "flags", Symbol.replace, etc.) while still maintaining mutability of its
* "lastIndex" property (which is necessary with flags "g" and/or "y" for some
* operations, most notably {@link regexpReplace}).
*
* @type {<T extends RegExp>(regexp: T) => T}

@@ -271,2 +311,13 @@ */

seal(defineProperties(regexp, regexpDescriptors));
/**
* Protect a RegExp instance against RegExp.prototype poisoning ("exec",
* "flags", Symbol.replace, etc.) and freeze its "lastIndex" property, rendering
* it fully inert but (absent flags "g" and/or "y") still usable by
* {@link regexpExec} and {@link regexpSearch}.
*
* @type {<T extends RegExp>(regexp: T) => T}
*/
export const freezeRegexp = regexp =>
freeze(/** @type {any} */ (defineProperties(regexp, regexpDescriptors)));
//

@@ -398,3 +449,3 @@ export const stringEndsWith = uncurryThis(stringPrototype.endsWith);

} catch (error) {
return error;
return /** @type {TypeError} */ (error);
}

@@ -472,5 +523,6 @@ };

} catch (error) {
const err = /** @type {Error} */ (error);
// Note: `Error.prototype.jsEngine` is only set by React Native runtime, not Hermes:
// https://github.com/facebook/react-native/blob/main/packages/react-native/ReactCommon/hermes/executor/HermesExecutorFactory.cpp#L224-L230
if (error.name === 'SyntaxError') {
if (err.name === 'SyntaxError') {
// Swallows Hermes error `async generators are unsupported` at runtime.

@@ -483,7 +535,7 @@ // Note: `console` is not a JS built-in, so Hermes engine throws:

return undefined;
} else if (error.name === 'EvalError') {
} else if (err.name === 'EvalError') {
// eslint-disable-next-line no-empty-function
return async function* AsyncGeneratorFunctionInstance() {};
} else {
throw error;
throw err;
}

@@ -490,0 +542,0 @@ }

@@ -338,5 +338,5 @@ /**

/**
*
* @param {CompartmentOptionsArgs|LegacyCompartmentOptionsArgs} args
* @param {...CompartmentOptionsArgs|LegacyCompartmentOptionsArgs} args
*/
/** @this {Compartment} */
function Compartment(...args) {

@@ -363,3 +363,5 @@ if (enforceNew && new.target === undefined) {

noAggregateLoadErrors = false,
} = compartmentOptions(...args);
} = compartmentOptions(
.../** @type {Parameters<typeof compartmentOptions>} */ (args),
);
const globalTransforms = arrayFlatMap(

@@ -381,3 +383,3 @@ [transforms, __shimTransforms__],

const compartment = this;
const compartment = /** @type {Compartment} */ (this);

@@ -403,3 +405,3 @@ setGlobalObjectSymbolUnscopables(globalObject);

makeCompartmentConstructor: targetMakeCompartmentConstructor,
parentCompartment: this,
parentCompartment: compartment,
markVirtualizedNativeFunction,

@@ -456,3 +458,3 @@ });

weakmapSet(privateFields, this, {
weakmapSet(privateFields, compartment, {
name: `${name}`,

@@ -459,0 +461,0 @@ globalTransforms,

@@ -25,8 +25,8 @@ // Copyright (C) 2019 Agoric, under Apache License 2.0

defineProperty,
freezeRegexp,
globalThis,
is,
isError,
regexpExec,
regexpReplace,
sealRegexp,
regexpSearch,
stringEndsWith,
stringIndexOf,

@@ -78,3 +78,3 @@ stringSlice,

const canBeBare = freeze(/^[\w:-]( ?[\w:-])*$/);
const canBeBare = freezeRegexp(/^[\w:-]( ?[\w:-])*$/);

@@ -85,3 +85,3 @@ /**

const bare = (text, spaces = undefined) => {
if (typeof text !== 'string' || !regexpExec(canBeBare, text)) {
if (typeof text !== 'string' || regexpSearch(canBeBare, text) === -1) {
return quote(text, spaces);

@@ -207,5 +207,2 @@ }

const leadingSpacePattern = sealRegexp(/^ /);
const trailingSpacePattern = sealRegexp(/ $/);
/**

@@ -228,16 +225,14 @@ * Get arguments suitable for a console logger function (e.g., `console.error`)

// (since console logging inserts its own argument-separating spaces).
const prevLiteralPart = regexpReplace(
trailingSpacePattern,
arrayPop(logArgs) || '',
'',
);
if (prevLiteralPart !== '') {
arrayPush(logArgs, prevLiteralPart);
const prevLiteralPart = arrayPop(logArgs) || '';
const trimmedPrev = stringEndsWith(prevLiteralPart, ' ')
? stringSlice(prevLiteralPart, 0, -1)
: prevLiteralPart;
if (trimmedPrev !== '') {
arrayPush(logArgs, trimmedPrev);
}
const nextLiteralPart = regexpReplace(
leadingSpacePattern,
template[i + 1],
'',
);
arrayPush(logArgs, arg, nextLiteralPart);
const nextLiteralPart = template[i + 1];
const trimmedNext = stringStartsWith(nextLiteralPart, ' ')
? stringSlice(nextLiteralPart, 1)
: nextLiteralPart;
arrayPush(logArgs, arg, trimmedNext);
}

@@ -244,0 +239,0 @@ if (logArgs[logArgs.length - 1] === '') {

@@ -45,2 +45,6 @@ import {

const makeErrorConstructor = (_ = {}) => {
/**
* @this {ErrorConstructor}
* @param {...any} rest
*/
// eslint-disable-next-line no-shadow

@@ -50,2 +54,4 @@ const ResultError = function Error(...rest) {

if (new.target === undefined) {
// Forward caller's `this` to FERAL_ERROR to keep the shim transparent.
// Error() as a function ignores `this` per spec, but do not narrow it.
error = apply(FERAL_ERROR, this, rest);

@@ -52,0 +58,0 @@ } else {

@@ -11,5 +11,7 @@ import {

defineProperties,
freezeRegexp,
fromEntries,
reflectSet,
regexpExec,
regexpSearch,
weakmapGet,

@@ -67,3 +69,3 @@ weakmapSet,

// be an infrastructure frame to be dropped from concise stack traces.
const FILENAME_NODE_DEPENDENTS_CENSOR = /\/node_modules\//;
const FILENAME_NODE_DEPENDENTS_CENSOR = freezeRegexp(/\/node_modules\//);

@@ -73,7 +75,9 @@ // If it begins with `internal/` or `node:internal` then it is likely

// stack traces.
const FILENAME_NODE_INTERNALS_CENSOR = /^(?:node:)?internal\//;
const FILENAME_NODE_INTERNALS_CENSOR = freezeRegexp(/^(?:node:)?internal\//);
// Frames within SES `assert.js` should be dropped from concise stack traces, as
// these are just steps towards creating the error object in question.
const FILENAME_ASSERT_CENSOR = /\/packages\/ses\/src\/error\/assert\.js$/;
const FILENAME_ASSERT_CENSOR = freezeRegexp(
/\/packages\/ses\/src\/error\/assert\.js$/,
);

@@ -85,7 +89,11 @@ // Frames within the `eventual-send` shim should be dropped so that concise

// Endo, so this rule will be of general interest.
const FILENAME_EVENTUAL_SEND_CENSOR = /\/packages\/eventual-send\/src\//;
const FILENAME_EVENTUAL_SEND_CENSOR = freezeRegexp(
/\/packages\/eventual-send\/src\//,
);
// Frames within the `ses-ava` package should be dropped from concise stack
// traces, as they just support exposing error details to AVA.
const FILENAME_SES_AVA_CENSOR = /\/packages\/ses-ava\/src\/ses-ava-test\.js$/;
const FILENAME_SES_AVA_CENSOR = freezeRegexp(
/\/packages\/ses-ava\/src\/ses-ava-test\.js$/,
);

@@ -113,3 +121,3 @@ // Any stack frame whose `fileName` matches any of these censor patterns

for (const filter of FILENAME_CENSORS) {
if (regexpExec(filter, fileName)) {
if (regexpSearch(filter, fileName) !== -1) {
return false;

@@ -131,3 +139,5 @@ }

// https://github.com/Agoric/agoric-sdk/issues/2326#issuecomment-773020389
const CALLSITE_ELLIPSIS_PATTERN1 = /^((?:.*[( ])?)[:/\w_-]*\/\.\.\.\/(.+)$/;
const CALLSITE_ELLIPSIS_PATTERN1 = freezeRegexp(
/^((?:.*[( ])?)[:/\w_-]*\/\.\.\.\/(.+)$/,
);

@@ -144,3 +154,3 @@ // The ad-hoc rule of the current pattern is that any likely-file-path or

// https://github.com/Agoric/agoric-sdk/issues/2326#issuecomment-773020389
const CALLSITE_ELLIPSIS_PATTERN2 = /^((?:.*[( ])?)\.\.\.\/(.+)$/;
const CALLSITE_ELLIPSIS_PATTERN2 = freezeRegexp(/^((?:.*[( ])?)\.\.\.\/(.+)$/);

@@ -157,3 +167,5 @@ // The ad-hoc rule of the current pattern is that any likely-file-path or

// lerna.
const CALLSITE_PACKAGES_PATTERN = /^((?:.*[( ])?)[:/\w_-]*\/(packages\/.+)$/;
const CALLSITE_PACKAGES_PATTERN = freezeRegexp(
/^((?:.*[( ])?)[:/\w_-]*\/(packages\/.+)$/,
);

@@ -174,3 +186,5 @@ // The ad-hoc rule of the current pattern is that any likely-file-path or

// `file://` is removed.
const CALLSITE_FILE_2SLASH_PATTERN = /^((?:.*[( ])?)file:\/\/([^/].*)$/;
const CALLSITE_FILE_2SLASH_PATTERN = freezeRegexp(
/^((?:.*[( ])?)file:\/\/([^/].*)$/,
);

@@ -177,0 +191,0 @@ // The use of these callSite patterns below assumes that any match will bind

@@ -63,3 +63,3 @@ import {

const RegExpStringIterator =
regexpPrototype[matchAllSymbol] && matchAllRegExp(/./);
regexpPrototype[matchAllSymbol] && matchAllRegExp(/./, '');
const RegExpStringIteratorPrototype =

@@ -66,0 +66,0 @@ RegExpStringIterator && getPrototypeOf(RegExpStringIterator);

@@ -1,2 +0,7 @@

import { FERAL_REG_EXP, regexpExec, stringSlice } from './commons.js';
import {
FERAL_REG_EXP,
freezeRegexp,
regexpExec,
stringSlice,
} from './commons.js';

@@ -12,4 +17,6 @@ // Captures a key and value of the form #key=value or @key=value

// trim until there are no matching comments.
const sourceMetaEntriesRegExp = new FERAL_REG_EXP(
`(?:\\s*//${sourceMetaEntryRegExp}|/\\*${sourceMetaEntryRegExp}\\s*\\*/)\\s*$`,
const sourceMetaEntriesRegExp = freezeRegexp(
new FERAL_REG_EXP(
`(?:\\s*//${sourceMetaEntryRegExp}|/\\*${sourceMetaEntryRegExp}\\s*\\*/)\\s*$`,
),
);

@@ -35,3 +42,3 @@

}
src = stringSlice(src, 0, src.length - match[0].length);
src = stringSlice(src, 0, -match[0].length);

@@ -38,0 +45,0 @@ // We skip $0 since it contains the entire match.

@@ -341,3 +341,3 @@ /* eslint-disable no-restricted-globals */

// https://github.com/facebook/hermes/blob/main/test/hermes/function-non-strict.js
if (e.message === 'Restricted in strict mode') {
if (/** @type {Error} */ (e).message === 'Restricted in strict mode') {
// Fixed in Static Hermes: https://github.com/facebook/hermes/issues/1582

@@ -869,2 +869,4 @@ FunctionInstance[prop] = accessor;

toGMTString: fn,
toTemporalInstant: false,
},

@@ -1258,2 +1260,4 @@

get: fn,
getOrInsert: fn,
getOrInsertComputed: fn,
has: fn,

@@ -1330,2 +1334,4 @@ keys: fn,

get: fn,
getOrInsert: fn,
getOrInsertComputed: fn,
has: fn,

@@ -1332,0 +1338,0 @@ set: fn,

import {
arrayFilter,
arrayIncludes,
freezeRegexp,
getOwnPropertyDescriptor,
getOwnPropertyNames,
hasOwn,
regexpExec,
regexpSearch,
Set,

@@ -94,3 +95,3 @@ setHas,

*/
const identifierPattern = /^[a-zA-Z_$][\w$]*$/;
const identifierPattern = freezeRegexp(/^[a-zA-Z_$][\w$]*$/);

@@ -104,3 +105,3 @@ /**

export const isValidIdentifierName = name =>
!setHas(reservedNames, name) && !!regexpExec(identifierPattern, name);
!setHas(reservedNames, name) && regexpSearch(identifierPattern, name) !== -1;

@@ -107,0 +108,0 @@ /*

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

defineProperty,
freezeRegexp,
getOwnPropertyNames,

@@ -15,3 +16,3 @@ isPrimitive,

const localePattern = /^(\w*[a-z])Locale([A-Z]\w*)$/;
const localePattern = freezeRegexp(/^(\w*[a-z])Locale([A-Z]\w*)$/);

@@ -18,0 +19,0 @@ // Use concise methods to obtain named functions without constructor

@@ -1,12 +0,20 @@

import { Object, DataView, Reflect, Number } from './commons.js';
import {
RangeError,
dataViewPrototype,
MAX_SAFE_INTEGER,
apply,
defineProperty,
entries,
getOwnPropertyDescriptor,
hasOwn,
is,
max,
trunc,
uncurryThis,
} from './commons.js';
const { is, defineProperty, entries } = Object;
const { apply } = Reflect;
const { prototype: dataViewPrototype } = DataView;
/**
* These `FERAL*` methods open up the NaN side-channel on some platforms,
* like v8. Thus, we need to encapsulate them and replace them with wrappers
* that canonicaize NaNs.
* that canonicalize NaNs.
*/

@@ -23,10 +31,127 @@ const {

// See https://webidl.spec.whatwg.org/#js-unrestricted-double which implies
// that this is the canonical NaN for web standards.
// Casual googling stongly suggests that this is also the cosmWasm
// canonical NaN. But I have not yet found an authoritative page stating this.
const canonicalNaN = 0x7ff8000000000000n;
const dataViewGetBuffer = uncurryThis(
// @ts-expect-error we know it is there on all conforming platforms
getOwnPropertyDescriptor(dataViewPrototype, 'buffer').get,
);
// Use method shorthand syntax to be this-sensitive but now be constructable
// nor have a `prototype` property.
/**
* See https://webidl.spec.whatwg.org/#js-unrestricted-double which implies
* that this is the canonical NaN for web standards.
* Casual googling strongly suggests that this is also the cosmWasm
* canonical NaN. But I have not yet found an authoritative page stating this.
*
* As noted there, WebIDL choses this value because
* > The `NaN` value ... is chosen simply because it is the quiet NaN
* > with the lowest value when its bit pattern is interpreted as an 64-bit
* > unsigned integer.
*
* See https://github.com/endojs/endo/pull/3214#discussion_r3155852021
*/
const canonicalNaN64Encoding = 0x7ff8000000000000n;
/**
* As of this writing (April 29, 2026) all implementations agree that a
* ***literal*** `NaN` seems to encode in 32 bits as `0x7fc00000`.
* See https://github.com/endojs/endo/pull/3214#discussion_r3162974396
*
* Applying WebIDL's rationale for the 64 bit float
* > The `NaN` value ... is chosen simply because it is the quiet NaN
* > with the lowest value when its bit pattern is interpreted as an 64-bit
* > unsigned integer.
*
* to the 32 bit case agrees with the choice the implementations make
* for the 32 bit encoding of a ***literal*** `NaN`.
*
* See https://github.com/endojs/endo/pull/3214#discussion_r3155852021
*/
const canonicalNaN32Encoding = 0x7fc00000;
/**
* As of this writing (April 29, 2026) all implementations that implement
* float16 (with the almost surely unintentional exception of QuickJS),
* agree that a ***literal*** `NaN` seems to encode in 16 bits as `0x7e00`.
* See https://github.com/endojs/endo/pull/3214#discussion_r3162974396
*
* Applying WebIDL's rationale for the 64 bit float
* > The `NaN` value ... is chosen simply because it is the quiet NaN
* > with the lowest value when its bit pattern is interpreted as an 64-bit
* > unsigned integer.
*
* to the 16 bit case agrees with the choice those implementations make
* for the 16 bit encoding of a ***literal*** `NaN`.
*
* See https://github.com/endojs/endo/pull/3214#discussion_r3155852021
*/
const canonicalNaN16Encoding = 0x7e00;
/**
* Perform `RequireInternalSlot(obj, [[DataView]])` as needed in
* SetViewValue.
* https://tc39.es/ecma262/multipage/structured-data.html#sec-setviewvalue
* https://tc39.es/ecma262/#sec-get-dataview.prototype.buffer
*
* @param {unknown} obj
* @returns {void}
*/
const requireDataView = obj => {
dataViewGetBuffer(obj);
};
/**
* Emulate the internal `ToIndex` from
* https://tc39.es/ecma262/multipage/abstract-operations.html#sec-toindex
*
* @param {unknown} v
* @returns {number}
*/
const toIndex = v => {
// @ts-expect-error Math.trunc uses the internal `ToNumber` to coerce its
// argument, whatever it is, to an integer number.
const n = trunc(v);
if (n === 0 || is(n, NaN)) {
return 0;
}
if (n < 0 || n > MAX_SAFE_INTEGER) {
throw RangeError('Invalid offset');
}
return n;
};
/**
* Expose the internal `ToNumber` from
* https://tc39.es/ecma262/multipage/abstract-operations.html#sec-tonumber
* We can't just use `Number(value)` because that is backed by `ToNumeric`,
* which coerces bigint output from `ToPrimitive` rather than throwing a
* TypeError as required of `ToNumber` (e.g., `Number({ valueOf: () => 42n })`
* returns 42).
*
* @param {unknown} v
* @returns {number}
*/
const toNumber = v =>
// @ts-expect-error Math.max uses the internal `ToNumber` to coerce its
// argument, whatever it is, to a number.
max(v);
/**
* Correctly guarding the `setFloat*` built-ins requires performing
* `ToNumber(value)` ourselves, because a non-NaN value such as
* `{ valueOf: () => {} }` gets coerced **to** NaN. But, according to
* [SetViewValue](https://tc39.es/ecma262/multipage/structured-data.html#sec-setviewvalue),
* we must first perform the observable
* `RequireInternalSlot(view, [[DataView]])` and `ToIndex(requestIndex)` steps
* (in that order). If those steps complete successfully, we have number-coerced
* `byteOffset` and `value` values that will pass those same steps internal to
* the implementation without incurring further observable interactions.
*
* We added these coercions to defend against an attack noticed by
* https://github.com/deepview-autofix .
* We were vulnerable to `value` being an
* object with a `valueOf` method that returns a (bad) NaN, since it
* would bypass the is NaN check. See where the
* `tame-nan*-sidechannel.test.js test cases mention "coercion attack".
*
* Uses method shorthand syntax to be `this`-sensitive but not be constructable
* nor have a `prototype` property.
*/
const methods = {

@@ -36,45 +161,68 @@ /**

* @param {number} value
* @param {boolean} [littleEndian]
* @param {boolean} [isLittleEndian]
*/
setFloat16(byteOffset, value, littleEndian = undefined) {
setFloat16(byteOffset, value, isLittleEndian = undefined) {
requireDataView(this);
byteOffset = toIndex(byteOffset);
value = toNumber(value);
if (is(value, NaN)) {
return apply(setUint16, this, [
byteOffset,
Number(canonicalNaN),
littleEndian,
canonicalNaN16Encoding,
isLittleEndian,
]);
} else {
return apply(FERAL_SET_FLOAT16, this, [byteOffset, value, littleEndian]);
return apply(FERAL_SET_FLOAT16, this, [
byteOffset,
value,
isLittleEndian,
]);
}
},
/**
* @param {number} byteOffset
* @param {number} value
* @param {boolean} [littleEndian]
* @param {boolean} [isLittleEndian]
*/
setFloat32(byteOffset, value, littleEndian = undefined) {
setFloat32(byteOffset, value, isLittleEndian = undefined) {
requireDataView(this);
byteOffset = toIndex(byteOffset);
value = toNumber(value);
if (is(value, NaN)) {
return apply(setUint32, this, [
byteOffset,
Number(canonicalNaN),
littleEndian,
canonicalNaN32Encoding,
isLittleEndian,
]);
} else {
return apply(FERAL_SET_FLOAT32, this, [byteOffset, value, littleEndian]);
return apply(FERAL_SET_FLOAT32, this, [
byteOffset,
value,
isLittleEndian,
]);
}
},
/**
* @param {number} byteOffset
* @param {number} value
* @param {boolean} [littleEndian]
* @param {boolean} [isLittleEndian]
*/
setFloat64(byteOffset, value, littleEndian = undefined) {
setFloat64(byteOffset, value, isLittleEndian = undefined) {
requireDataView(this);
byteOffset = toIndex(byteOffset);
value = toNumber(value);
if (is(value, NaN)) {
return apply(setBigUint64, this, [
byteOffset,
canonicalNaN,
littleEndian,
canonicalNaN64Encoding,
isLittleEndian,
]);
} else {
return apply(FERAL_SET_FLOAT64, this, [byteOffset, value, littleEndian]);
return apply(FERAL_SET_FLOAT64, this, [
byteOffset,
value,
isLittleEndian,
]);
}

@@ -99,8 +247,10 @@ },

for (const [name, method] of entries(methods)) {
defineProperty(dataViewPrototype, name, {
// Since we're redefining properties that already exist, by omitting the
// other descriptor attributes here, they are unchanged.
value: method,
});
if (hasOwn(dataViewPrototype, name)) {
defineProperty(dataViewPrototype, name, {
// Since we're redefining properties that already exist, by omitting the
// other descriptor attributes here, they are unchanged.
value: method,
});
}
}
};

@@ -6,5 +6,6 @@ // @ts-check

SyntaxError,
freezeRegexp,
regexpReplace,
regexpSearch,
sealRegexp,
stringSearch,
stringSlice,

@@ -25,3 +26,3 @@ stringSplit,

function getLineNumber(src, pattern) {
const index = stringSearch(src, pattern);
const index = regexpSearch(pattern, src);
if (index < 0) {

@@ -182,5 +183,4 @@ return -1;

const someDirectEvalPattern = new FERAL_REG_EXP(
'(^|[^.])\\beval(\\s*\\()',
'g',
const someDirectEvalPattern = freezeRegexp(
new FERAL_REG_EXP('(^|[^.])\\beval(\\s*\\()'),
);

@@ -187,0 +187,0 @@

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 too big to display

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 too big to display

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 too big to display