From c1f40befca0f6e0f863e2ec81b2e595530f21875 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 24 Oct 2024 13:41:29 -0700 Subject: [PATCH] docs(ses): Finish embracing permits terminology --- packages/ses/docs/draft-standalone-spec.md | 2 +- packages/ses/docs/lockdown.md | 2 +- packages/ses/src/enable-property-overrides.js | 2 +- packages/ses/src/error/README.md | 4 ++-- packages/ses/src/error/console.js | 16 +++++++-------- packages/ses/src/error/internal-types.js | 2 +- .../src/error/tame-v8-error-constructor.js | 6 +++--- packages/ses/src/global-object.js | 2 +- packages/ses/src/intrinsics.js | 12 +++++------ packages/ses/src/lockdown.js | 8 ++++---- packages/ses/src/permits-intrinsics.js | 19 +++++++++--------- packages/ses/src/permits.js | 20 +++++++++---------- packages/ses/src/tame-symbol-constructor.js | 4 ++-- packages/ses/test/permits.test.js | 2 +- .../ses/test/tame-symbol-constructor.test.js | 2 +- 15 files changed, 51 insertions(+), 52 deletions(-) diff --git a/packages/ses/docs/draft-standalone-spec.md b/packages/ses/docs/draft-standalone-spec.md index 78404603df..e66a7056f7 100644 --- a/packages/ses/docs/draft-standalone-spec.md +++ b/packages/ses/docs/draft-standalone-spec.md @@ -23,7 +23,7 @@ the default configuration of SES are * Omit all support for sloppy mode * Aside from `BigInt`, omit everything else outside the EcmaScript 2018 spec. * In particular, omit the `import()` and `import.meta` expressions. - * Omit annex B (except those our whitelist allows) + * Omit annex B (except those `ses` explicitly permits) * In particular, omit the `RegExp` static properties that provide a global communications channel. * On the `Math` namespace object shared by constructed compartments: diff --git a/packages/ses/docs/lockdown.md b/packages/ses/docs/lockdown.md index 94a53b03b2..433d7fb8c1 100644 --- a/packages/ses/docs/lockdown.md +++ b/packages/ses/docs/lockdown.md @@ -770,7 +770,7 @@ we now step into every access to an enabled property. Every read steps into the enabling getter. This adds yet more noise to the debugging experience. The file [src/enablements.js](../src/enablements.js) exports three different -whitelists definining which data properties to convert to enable override by +lists definining which data properties to convert to enable override by assignment, `minEnablements`, `moderateEnablements`, and `severeEnablements`. ```js diff --git a/packages/ses/src/enable-property-overrides.js b/packages/ses/src/enable-property-overrides.js index 454c1f64df..c2e9c91cb1 100644 --- a/packages/ses/src/enable-property-overrides.js +++ b/packages/ses/src/enable-property-overrides.js @@ -26,7 +26,7 @@ import { /** @import {Reporter} from './reporting-types.js' */ /** - * For a special set of properties defined in the `enablement` whitelist, + * For a special set of properties defined in the `enablement` list, * `enablePropertyOverrides` ensures that the effect of freezing does not * suppress the ability to override these properties on derived objects by * simple assignment. diff --git a/packages/ses/src/error/README.md b/packages/ses/src/error/README.md index e67a659ca2..04baea775d 100644 --- a/packages/ses/src/error/README.md +++ b/packages/ses/src/error/README.md @@ -62,9 +62,9 @@ Before repair or `lockdown`, we assume there is some prior "system" console boun The default `{ consoleTaming: 'safe' }` setting replaces the system console with a root console that does use all these side tables to generate a more informative log. This root console wraps the prior system console. This root console outputs its log information only by invoking this wrapped system console, which therefore determines how this log information is made available to the external world. To support determinism, we will also need to support a no-op console setting, as explained at [deterministic handling of adversarial code calling console.log with a Proxy #1852](https://github.com/Agoric/agoric-sdk/issues/1852) and [Need no-op console setting for determinism #487](https://github.com/Agoric/SES-shim/issues/487). -SES considers both `assert` and `console` to be powerful objects, appearing initially in the start compartment, and not whitelisted for implicit propagation to created compartments. Rather, we recommend an endowment pattern where the global `assert` is passed forward as is, but only filtered forms of the `console` are. As compartments create each other in a tree, they create a corresponding filtering tree of consoles. Information sent to any compartment's console is then sent up the filtering tree. Only information that survives all the filters in its path arrive at the root console, producing log output. The others have no effect. Given the expected pattern of a compartment per package, the per-compartment console filter is effectly a topic filter, treating the package identity as a topic. We plan to also support coordinated stack-frame filters, as explained at [Need source-prefix-based stackframe filter #488](https://github.com/Agoric/SES-shim/issues/488). +SES considers both `assert` and `console` to be powerful objects, appearing initially in the start compartment, and not permitted for implicit propagation to created compartments. Rather, we recommend an endowment pattern where the global `assert` is passed forward as-is, but only filtered forms of the `console` are. As compartments create each other in a tree, they create a corresponding filtering tree of consoles. Information sent to any compartment's console is then sent up the filtering tree. Only information that survives all the filters in its path arrive at the root console, producing log output. The others have no effect. Given the expected pattern of a compartment per package, the per-compartment console filter is effectly a topic filter, treating the package identity as a topic. We plan to also support coordinated stack-frame filters, as explained at [Need source-prefix-based stackframe filter #488](https://github.com/Agoric/SES-shim/issues/488). -For security and determinism, we normally reason from the *in-band frame of reference* where the console logging output does not exist, is not an effect, and `console` operations are write-only. Within this frame of reference, the `assert` and `console` powers are not very powerful. They are almost as safe as the whitelisted powerless shared primordials, which is why we're willing to recommend this endowment pattern be habitual. +For security and determinism, we normally reason from the *in-band frame of reference* where the console logging output does not exist, is not an effect, and `console` operations are write-only. Within this frame of reference, the `assert` and `console` powers are not very powerful. They are almost as safe as the permitted, powerless, shared primordials, which is why we're willing to recommend this endowment pattern be habitual. ## Hiding and Revealing Distributed Diagnostic Information diff --git a/packages/ses/src/error/console.js b/packages/ses/src/error/console.js index 49b78f9f0a..1446ad56e9 100644 --- a/packages/ses/src/error/console.js +++ b/packages/ses/src/error/console.js @@ -31,7 +31,7 @@ import { // For our internal debugging purposes, uncomment // const internalDebugConsole = console; -// The whitelists of console methods, from: +// The permitted console methods, from: // Whatwg "living standard" https://console.spec.whatwg.org/ // Node https://nodejs.org/dist/latest-v14.x/docs/api/console.html // MDN https://developer.mozilla.org/en-US/docs/Web/API/Console_API @@ -110,7 +110,7 @@ export const consoleOtherMethods = freeze([ ]); /** @type {readonly [ConsoleProps, LogSeverity | undefined][]} */ -const consoleWhitelist = freeze([ +const consoleMethodPermits = freeze([ ...consoleLevelMethods, ...consoleOtherMethods, ]); @@ -118,8 +118,8 @@ const consoleWhitelist = freeze([ /** * consoleOmittedProperties is currently unused. I record and maintain it here * with the intention that it be treated like the `false` entries in the main - * SES whitelist: that seeing these on the original console is expected, but - * seeing anything else that's outside the whitelist is surprising and should + * SES permits: that seeing these on the original console is expected, but + * seeing anything else that's outside the permits is surprising and should * provide a diagnostic. * * const consoleOmittedProperties = freeze([ @@ -158,7 +158,7 @@ export const makeLoggingConsoleKit = ( let logArray = []; const loggingConsole = fromEntries( - arrayMap(consoleWhitelist, ([name, _]) => { + arrayMap(consoleMethodPermits, ([name, _]) => { // Use an arrow function so that it doesn't come with its own name in // its printed form. Instead, we're hoping that tooling uses only // the `.name` property set below. @@ -508,11 +508,11 @@ freeze(defineCausalConsoleFromLogger); /** @type {FilterConsole} */ export const filterConsole = (baseConsole, filter, _topic = undefined) => { // TODO do something with optional topic string - const whitelist = arrayFilter( - consoleWhitelist, + const methodPermits = arrayFilter( + consoleMethodPermits, ([name, _]) => name in baseConsole, ); - const methods = arrayMap(whitelist, ([name, severity]) => { + const methods = arrayMap(methodPermits, ([name, severity]) => { /** * @param {...any} args */ diff --git a/packages/ses/src/error/internal-types.js b/packages/ses/src/error/internal-types.js index 3e3f2b4a13..214ff08ab3 100644 --- a/packages/ses/src/error/internal-types.js +++ b/packages/ses/src/error/internal-types.js @@ -62,7 +62,7 @@ /** * @callback MakeLoggingConsoleKit * - * A logging console just accumulates the contents of all whitelisted calls, + * A logging console just accumulates the contents of all permitted calls, * making them available to callers of `takeLog()`. Calling `takeLog()` * consumes these, so later calls to `takeLog()` will only provide a log of * calls that have happened since then. diff --git a/packages/ses/src/error/tame-v8-error-constructor.js b/packages/ses/src/error/tame-v8-error-constructor.js index 7a5da8dd31..e5b4c5f201 100644 --- a/packages/ses/src/error/tame-v8-error-constructor.js +++ b/packages/ses/src/error/tame-v8-error-constructor.js @@ -19,8 +19,8 @@ import { TypeError, } from '../commons.js'; -// Whitelist names from https://v8.dev/docs/stack-trace-api -// Whitelisting only the names used by error-stack-shim/src/v8StackFrames +// Permit names from https://v8.dev/docs/stack-trace-api +// Permiting only the names used by error-stack-shim/src/v8StackFrames // callSiteToFrame to shim the error stack proposal. const safeV8CallSiteMethodNames = [ // suppress 'getThis' definitely @@ -45,7 +45,7 @@ const safeV8CallSiteMethodNames = [ 'getPosition', 'getScriptNameOrSourceURL', - 'toString', // TODO replace to use only whitelisted info + 'toString', // TODO replace to use only permitted info ]; // TODO this is a ridiculously expensive way to attenuate callsites. diff --git a/packages/ses/src/global-object.js b/packages/ses/src/global-object.js index 468d3296c5..007399fa5c 100644 --- a/packages/ses/src/global-object.js +++ b/packages/ses/src/global-object.js @@ -121,7 +121,7 @@ export const setGlobalObjectMutableProperties = ( ), ); - // TODO These should still be tamed according to the whitelist before + // TODO These should still be tamed according to the permits before // being made available. for (const [name, value] of entries(perCompartmentGlobals)) { defineProperty(globalObject, name, { diff --git a/packages/ses/src/intrinsics.js b/packages/ses/src/intrinsics.js index 4355d0c6ab..3393d64880 100644 --- a/packages/ses/src/intrinsics.js +++ b/packages/ses/src/intrinsics.js @@ -28,7 +28,7 @@ const isFunction = obj => typeof obj === 'function'; // Like defineProperty, but throws if it would modify an existing property. // We use this to ensure that two conflicting attempts to define the same // property throws, causing SES initialization to fail. Otherwise, a -// conflict between, for example, two of SES's internal whitelists might +// conflict between, for example, two of SES's internal permits might // get masked as one overwrites the other. Accordingly, the thrown error // complains of a "Conflicting definition". function initProperty(obj, name, desc) { @@ -82,7 +82,7 @@ export const makeIntrinsicsCollector = () => { freeze(addIntrinsics); // For each intrinsic, if it has a `.prototype` property, use the - // whitelist to find out the intrinsic name for that prototype and add it + // permits to find out the intrinsic name for that prototype and add it // to the intrinsics. const completePrototypes = () => { for (const [name, intrinsic] of entries(intrinsics)) { @@ -96,17 +96,17 @@ export const makeIntrinsicsCollector = () => { } const permit = permitted[name]; if (typeof permit !== 'object') { - throw TypeError(`Expected permit object at whitelist.${name}`); + throw TypeError(`Expected permit object at permits.${name}`); } const namePrototype = permit.prototype; if (!namePrototype) { - throw TypeError(`${name}.prototype property not whitelisted`); + throw TypeError(`${name}.prototype property not permitted`); } if ( typeof namePrototype !== 'string' || !objectHasOwnProperty(permitted, namePrototype) ) { - throw TypeError(`Unrecognized ${name}.prototype whitelist entry`); + throw TypeError(`Unrecognized ${name}.prototype permits entry`); } const intrinsicPrototype = intrinsic.prototype; if (objectHasOwnProperty(intrinsics, namePrototype)) { @@ -155,7 +155,7 @@ export const makeIntrinsicsCollector = () => { /** * getGlobalIntrinsics() * Doesn't tame, delete, or modify anything. Samples globalObject to create an - * intrinsics record containing only the whitelisted global variables, listed + * intrinsics record containing only the permitted global variables, listed * by the intrinsic names appropriate for new globals, i.e., the globals of * newly constructed compartments. * diff --git a/packages/ses/src/lockdown.js b/packages/ses/src/lockdown.js index 7c4a1b2d11..4150b3d544 100644 --- a/packages/ses/src/lockdown.js +++ b/packages/ses/src/lockdown.js @@ -30,7 +30,7 @@ import { } from './commons.js'; import { makeHardener } from './make-hardener.js'; import { makeIntrinsicsCollector } from './intrinsics.js'; -import whitelistIntrinsics from './permits-intrinsics.js'; +import removeUnpermittedIntrinsics from './permits-intrinsics.js'; import tameFunctionConstructors from './tame-function-constructors.js'; import tameDateConstructor from './tame-date-constructor.js'; import tameMathObject from './tame-math-object.js'; @@ -365,17 +365,17 @@ export const repairIntrinsics = (options = {}) => { tameFauxDataProperties(intrinsics); /** - * 2. WHITELIST to standardize the environment. + * 2. Enforce PERMITS on shared intrinsics */ // Remove non-standard properties. - // All remaining function encountered during whitelisting are + // All remaining functions encountered during whitelisting are // branded as honorary native functions. reportInGroup( 'SES Removing unpermitted intrinsics', reporter, groupReporter => - whitelistIntrinsics( + removeUnpermittedIntrinsics( intrinsics, markVirtualizedNativeFunction, groupReporter, diff --git a/packages/ses/src/permits-intrinsics.js b/packages/ses/src/permits-intrinsics.js index 2c55f2434f..a629dae8ea 100644 --- a/packages/ses/src/permits-intrinsics.js +++ b/packages/ses/src/permits-intrinsics.js @@ -24,7 +24,7 @@ // Typically, this module will not be used directly, but via the // [lockdown-shim] which handles all necessary repairs and taming in SES. // -// In the whitelist, the `prototype`, `__proto__`, and `constructor` must be +// In the permits, the `prototype`, `__proto__`, and `constructor` must be // specified and point to top level entries in the map. For example, // `Object.__proto__` leads to `FunctionPrototype` which is a top level entry // in the map. @@ -34,14 +34,14 @@ // `Error.stackTraceLimit` leads to 'number'), // * the name of an intrinsic, // * an internal constant(for example, `eval` leads to `fn` which -// is an alias for `FunctionInstance`, a record that whitelist all +// is an alias for `FunctionInstance`, a record that permits all // properties allowed on such instance). // * false, a property to be removed that we know about. // // All unlisted properties are also removed. But for the ones that are removed // because they are unlisted, as opposed to `false`, we also print their // name to the console as a useful diagnostic, possibly provoking an expansion -// of the whitelist. +// of the permits. import { permitted, FunctionInstance, isAccessorPermit } from './permits.js'; import { @@ -67,15 +67,14 @@ import { */ /** - * whitelistIntrinsics() * Removes all non-allowed properties found by recursively and * reflectively walking own property chains. * * @param {object} intrinsics - * @param {(object) => void} markVirtualizedNativeFunction + * @param {(virtualizedNativeFunction: object) => void} markVirtualizedNativeFunction * @param {Reporter} reporter */ -export default function whitelistIntrinsics( +export default function removeUnpermittedIntrinsics( intrinsics, markVirtualizedNativeFunction, { warn, error }, @@ -143,7 +142,7 @@ export default function whitelistIntrinsics( // Assert: protoName, if provided, is a string. if (protoName !== undefined && typeof protoName !== 'string') { - throw TypeError(`Malformed whitelist permit ${path}.__proto__`); + throw TypeError(`Malformed permit ${path}.__proto__`); } // If permit not specified, default to Object.prototype. @@ -159,7 +158,7 @@ export default function whitelistIntrinsics( /* * isAllowedPropertyValue() - * Whitelist a single property value against a permit. + * enforce permit for a single property value. */ function isAllowedPropertyValue(path, value, prop, permit) { if (typeof permit === 'object') { @@ -187,7 +186,7 @@ export default function whitelistIntrinsics( if (objectHasOwnProperty(intrinsics, permit)) { if (value !== intrinsics[permit]) { - throw TypeError(`Does not match whitelist ${path}`); + throw TypeError(`Does not match permit for ${path}`); } return true; } @@ -314,6 +313,6 @@ export default function whitelistIntrinsics( } // Start path with 'intrinsics' to clarify that properties are not - // removed from the global object by the whitelisting operation. + // removed from the global object by the permitting operation. visitProperties('intrinsics', intrinsics, permitted); } diff --git a/packages/ses/src/permits.js b/packages/ses/src/permits.js index fa62aaa7ea..d1ac6de7fb 100644 --- a/packages/ses/src/permits.js +++ b/packages/ses/src/permits.js @@ -6,7 +6,7 @@ import { arrayPush } from './commons.js'; /** @import {GenericErrorConstructor} from '../types.js' */ /** - * @file Exports {@code whitelist}, a recursively defined + * @file Exports {@code permits}, a recursively defined * JSON record enumerating all intrinsics and their properties * according to ECMA specs. * @@ -32,7 +32,7 @@ export const constantProperties = { * universalPropertyNames * Properties of all global objects. * Must be powerless. - * Maps from property name to the intrinsic name in the whitelist. + * Maps from property name to the intrinsic name in the permits. */ export const universalPropertyNames = { // *** Function Properties of the Global Object @@ -115,7 +115,7 @@ export const universalPropertyNames = { * Those found only on the initial global, i.e., the global of the * start compartment, as well as any compartments created before lockdown. * These may provide much of the power provided by the original. - * Maps from property name to the intrinsic name in the whitelist. + * Maps from property name to the intrinsic name in the permits. */ export const initialGlobalPropertyNames = { // *** Constructor Properties of the Global Object @@ -125,7 +125,7 @@ export const initialGlobalPropertyNames = { RegExp: '%InitialRegExp%', // Omit `Symbol`, because we want the original to appear on the - // start compartment without passing through the whitelist mechanism, since + // start compartment without passing through the permits mechanism, since // we want to preserve all its properties, even if we never heard of them. // Symbol: '%InitialSymbol%', @@ -149,7 +149,7 @@ export const initialGlobalPropertyNames = { * sharedGlobalPropertyNames * Those found only on the globals of new compartments created after lockdown, * which must therefore be powerless. - * Maps from property name to the intrinsic name in the whitelist. + * Maps from property name to the intrinsic name in the permits. */ export const sharedGlobalPropertyNames = { // *** Constructor Properties of the Global Object @@ -168,7 +168,7 @@ export const sharedGlobalPropertyNames = { * uniqueGlobalPropertyNames * Those made separately for each global, including the initial global * of the start compartment. - * Maps from property name to the intrinsic name in the whitelist + * Maps from property name to the intrinsic name in the permits * (which is currently always the same). */ export const uniqueGlobalPropertyNames = { @@ -228,18 +228,18 @@ export { NativeErrors }; * blacklisted and simply removed. Properties not mentioned * are also considered blacklisted and are removed. *
  • A string value equal to a primitive ("number", "string", etc), - * in which case the property is whitelisted if its value property + * in which case the property is permitted if its value property * is typeof the given type. For example, {@code "Infinity"} leads to * "number" and property values that fail {@code typeof "number"}. * are removed. *
  • A string value equal to an intinsic name ("ObjectPrototype", - * "Array", etc), in which case the property whitelisted if its + * "Array", etc), in which case the property permitted if its * value property is equal to the value of the corresponfing * intrinsics. For example, {@code Map.prototype} leads to * "MapPrototype" and the property is removed if its value is * not equal to %MapPrototype% *
  • Another record, in which case this property is simply - * whitelisted and that next record represents the disposition of + * permitted and that next record represents the disposition of * the object which is its value. For example, {@code "Object"} * leads to another record explaining what properties {@code * "Object"} may have and how each such property should be treated. @@ -1496,7 +1496,7 @@ export const permitted = { // // We will likely change this to add a property to Promise called // Promise.delegate and put static methods on it, which will necessitate - // another whitelist change to update to the current proposed standard. + // another permits change to update to the current proposed standard. HandledPromise: { '[[Proto]]': 'Promise', applyFunction: fn, diff --git a/packages/ses/src/tame-symbol-constructor.js b/packages/ses/src/tame-symbol-constructor.js index 50fe532f3c..becc49fd99 100644 --- a/packages/ses/src/tame-symbol-constructor.js +++ b/packages/ses/src/tame-symbol-constructor.js @@ -16,8 +16,8 @@ import { * `Symbol` constructor on constructed compartments. * * Starting these properties as configurable assumes two succeeding phases of - * processing: A whitelisting phase, that - * removes all properties not on the whitelist (which requires them to be + * processing: A permit enforcement phase, that + * removes all properties not on the permits (which requires them to be * configurable) and a global hardening step that freezes all primordials, * returning these properties to their expected non-configurable status. * diff --git a/packages/ses/test/permits.test.js b/packages/ses/test/permits.test.js index 3a1ef1b5d0..1777552bcf 100644 --- a/packages/ses/test/permits.test.js +++ b/packages/ses/test/permits.test.js @@ -18,7 +18,7 @@ test('SharedArrayBuffer should be removed because it is not permitted', t => { if (have) { // we ideally want both of these, but the realms magic can only // manage one at a time (for properties that previously existed but - // which were removed by the whitelist check) + // which were removed by the permits check) // t.throws(() => c.evaluate('SharedArrayBuffer'), ReferenceError); t.is(c.evaluate('typeof SharedArrayBuffer'), 'undefined'); } diff --git a/packages/ses/test/tame-symbol-constructor.test.js b/packages/ses/test/tame-symbol-constructor.test.js index 52ba606a3e..56e4aeee62 100644 --- a/packages/ses/test/tame-symbol-constructor.test.js +++ b/packages/ses/test/tame-symbol-constructor.test.js @@ -15,7 +15,7 @@ defineProperty(Symbol, 'dummy', { configurable: false, }); -// Since %SharedSymbol%.dummy is not even mentioned on the whitelist, +// Since %SharedSymbol%.dummy is not even mentioned on the permits, // this test should also print on the console: // > Removing intrinsics.%SharedSymbol%.dummy lockdown();