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 0.18.7 to 0.18.8

src/assert-shim.js

34

index.js

@@ -15,32 +15,4 @@ // Copyright (C) 2018 Agoric

import { globalThis, TypeError, assign } from './src/commons.js';
import { tameFunctionToString } from './src/tame-function-tostring.js';
import { getGlobalIntrinsics } from './src/intrinsics.js';
import { lockdown } from './src/lockdown-shim.js';
import { makeCompartmentConstructor } from './src/compartment-shim.js';
import { assert } from './src/error/assert.js';
/** getThis returns globalThis in sloppy mode or undefined in strict mode. */
function getThis() {
return this;
}
if (getThis()) {
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_NO_SLOPPY.md
throw TypeError(`SES failed to initialize, sloppy mode (SES_NO_SLOPPY)`);
}
const markVirtualizedNativeFunction = tameFunctionToString();
const Compartment = makeCompartmentConstructor(
makeCompartmentConstructor,
getGlobalIntrinsics(globalThis),
markVirtualizedNativeFunction,
);
assign(globalThis, {
lockdown,
Compartment,
assert,
});
import './src/lockdown-shim.js';
import './src/compartment-shim.js';
import './src/assert-shim.js';
User-visible changes in SES:
# v0.18.8 (2023-09-11)
- Extracts `repairIntrinsics(options)` and `hardenIntrinsics()` from the
behavior of `lockdown(options)` so vetted shims can run between these
calls.
Any modifications to shared intrinsics survive if applied after
`repairIntrinsics()`.
- In the SES-shim implementation of HardenedJS, all constructed compartments
get the same safe `Date` constructor, that does not provide the ability to
measure duration.
It used to do this by having `Date.now()` return `NaN`, and to have calls on
the constructor that would normally have returned an indication of the
current date, instead return the corresponding invalid date indication.
Now, all of these throw a `TypeError` whose message begins with `'secure
mode'`.
This aligns with the XS implementation of HardenedJS.
- Similarly, In the SES-shim implementation of HardenedJS, all constructed
compartments get the same safe `Math` namespace object that does not provide
a working `random()` function.
It used to do that by omitting the `random` property from the safe `Math`
namespace object.
Now, the safe shared `Math` namespace object has a `Math.random()` function
that throws a `TypeError whose message begins with `'secure mode'`.
This again aligns with the XS implementation of HardenedJS.
# v0.18.6 (2023-08-07)

@@ -47,3 +72,3 @@

- Fixes a bug where reexport of multiple named exports of the same name was
causing them to be overridden by the last value. Now named exports are
causing them to be overridden by the last value. Now named exports are
handled in the same manner as `export *`.

@@ -50,0 +75,0 @@ - Allows Compatment `importHook` implementations to return aliases: module

{
"name": "ses",
"version": "0.18.7",
"version": "0.18.8",
"description": "Hardened JavaScript for Fearless Cooperation",

@@ -63,7 +63,7 @@ "keywords": [

"dependencies": {
"@endo/env-options": "^0.1.3"
"@endo/env-options": "^0.1.4"
},
"devDependencies": {
"@endo/compartment-mapper": "^0.9.1",
"@endo/static-module-record": "^0.8.1",
"@endo/compartment-mapper": "^0.9.2",
"@endo/static-module-record": "^0.8.2",
"@endo/test262-runner": "^0.1.32",

@@ -83,3 +83,3 @@ "ava": "^5.3.0",

"tsd": "^0.28.1",
"typescript": "~5.1.3"
"typescript": "~5.2.2"
},

@@ -184,3 +184,3 @@ "files": [

},
"gitHead": "b38361616f968415291b089dcca75cc4a2672a35"
"gitHead": "9c779d317c4b02133172dbe142c5b2d1727efc49"
}

@@ -197,3 +197,4 @@ # SES

The global scope of every compartment includes a shallow, specialized copy of
the JavaScript intrinsics, omitting `Date.now` and `Math.random`.
the JavaScript intrinsics, disabling `Math.random`, `Date.now` and the
behaviors of the `Date` constructor which would reveal the current time.
Comaprtments leave these out since they can be used as covert communication

@@ -726,3 +727,3 @@ channels between programs.

bugs in our code. We welcome the opportunity to cooperate with researchers,
whose efforts will undoubtedly yield stronger, more resilient code.
whose efforts will undoubtedly yield stronger, more resilient code.

@@ -773,3 +774,3 @@ ## Bug Disclosure

> an environment.
>
>
> Please consider increasing support by replacing assignments to object

@@ -797,2 +798,1 @@ > properties inherited from intrinsics with use of `Object.defineProperties`

[SES Strategy Recordings]: https://www.youtube.com/playlist?list=PLzDw4TTug5O1jzKodRDp3qec8zl88oxGd

@@ -1,292 +0,15 @@

// @ts-check
/* eslint-disable no-underscore-dangle */
/// <reference types="ses">
/// <reference types="ses"/>
import {
Map,
ReferenceError,
TypeError,
WeakMap,
assign,
defineProperties,
entries,
promiseThen,
weakmapGet,
weakmapSet,
} from './commons.js';
import {
setGlobalObjectSymbolUnscopables,
setGlobalObjectConstantProperties,
setGlobalObjectMutableProperties,
setGlobalObjectEvaluators,
} from './global-object.js';
import { sharedGlobalPropertyNames } from './permits.js';
import { load } from './module-load.js';
import { link } from './module-link.js';
import { getDeferredExports } from './module-proxy.js';
import { assert } from './error/assert.js';
import { compartmentEvaluate } from './compartment-evaluate.js';
import { makeSafeEvaluator } from './make-safe-evaluator.js';
import { globalThis } from './commons.js';
import { makeCompartmentConstructor } from './compartment.js';
import { tameFunctionToString } from './tame-function-tostring.js';
import { getGlobalIntrinsics } from './intrinsics.js';
const { quote: q } = assert;
const markVirtualizedNativeFunction = tameFunctionToString();
// moduleAliases associates every public module exports namespace with its
// corresponding compartment and specifier so they can be used to link modules
// across compartments.
// The mechanism to thread an alias is to use the compartment.module function
// to obtain the exports namespace of a foreign module and pass it into another
// compartment's moduleMap constructor option.
const moduleAliases = new WeakMap();
// privateFields captures the private state for each compartment.
const privateFields = new WeakMap();
// Compartments do not need an importHook or resolveHook to be useful
// as a vessel for evaluating programs.
// However, any method that operates the module system will throw an exception
// if these hooks are not available.
const assertModuleHooks = compartment => {
const { importHook, resolveHook } = weakmapGet(privateFields, compartment);
if (typeof importHook !== 'function' || typeof resolveHook !== 'function') {
throw TypeError(
'Compartment must be constructed with an importHook and a resolveHook for it to be able to load modules',
);
}
};
export const InertCompartment = function Compartment(
_endowments = {},
_modules = {},
_options = {},
) {
throw TypeError(
'Compartment.prototype.constructor is not a valid constructor.',
);
};
/**
* @param {Compartment} compartment
* @param {string} specifier
*/
const compartmentImportNow = (compartment, specifier) => {
const { execute, exportsProxy } = link(
privateFields,
moduleAliases,
compartment,
specifier,
);
execute();
return exportsProxy;
};
export const CompartmentPrototype = {
constructor: InertCompartment,
get globalThis() {
return weakmapGet(privateFields, this).globalObject;
},
get name() {
return weakmapGet(privateFields, this).name;
},
/**
* @param {string} source is a JavaScript program grammar construction.
* @param {object} [options]
* @param {Array<import('./lockdown-shim').Transform>} [options.transforms]
* @param {boolean} [options.sloppyGlobalsMode]
* @param {object} [options.__moduleShimLexicals__]
* @param {boolean} [options.__evadeHtmlCommentTest__]
* @param {boolean} [options.__evadeImportExpressionTest__]
* @param {boolean} [options.__rejectSomeDirectEvalExpressions__]
*/
evaluate(source, options = {}) {
const compartmentFields = weakmapGet(privateFields, this);
return compartmentEvaluate(compartmentFields, source, options);
},
toString() {
return '[object Compartment]';
},
module(specifier) {
if (typeof specifier !== 'string') {
throw TypeError('first argument of module() must be a string');
}
assertModuleHooks(this);
const { exportsProxy } = getDeferredExports(
this,
weakmapGet(privateFields, this),
moduleAliases,
specifier,
);
return exportsProxy;
},
async import(specifier) {
if (typeof specifier !== 'string') {
throw TypeError('first argument of import() must be a string');
}
assertModuleHooks(this);
return promiseThen(
load(privateFields, moduleAliases, this, specifier),
() => {
// The namespace box is a contentious design and likely to be a breaking
// change in an appropriately numbered future version.
const namespace = compartmentImportNow(
/** @type {Compartment} */ (this),
specifier,
);
return { namespace };
},
);
},
async load(specifier) {
if (typeof specifier !== 'string') {
throw TypeError('first argument of load() must be a string');
}
assertModuleHooks(this);
return load(privateFields, moduleAliases, this, specifier);
},
importNow(specifier) {
if (typeof specifier !== 'string') {
throw TypeError('first argument of importNow() must be a string');
}
assertModuleHooks(this);
return compartmentImportNow(/** @type {Compartment} */ (this), specifier);
},
};
defineProperties(InertCompartment, {
prototype: { value: CompartmentPrototype },
});
/**
* @callback MakeCompartmentConstructor
* @param {MakeCompartmentConstructor} targetMakeCompartmentConstructor
* @param {Record<string, any>} intrinsics
* @param {(object: object) => void} markVirtualizedNativeFunction
* @returns {Compartment['constructor']}
*/
/** @type {MakeCompartmentConstructor} */
export const makeCompartmentConstructor = (
targetMakeCompartmentConstructor,
intrinsics,
// @ts-ignore Compartment is definitely on globalThis.
globalThis.Compartment = makeCompartmentConstructor(
makeCompartmentConstructor,
getGlobalIntrinsics(globalThis),
markVirtualizedNativeFunction,
) => {
function Compartment(endowments = {}, moduleMap = {}, options = {}) {
if (new.target === undefined) {
throw TypeError(
"Class constructor Compartment cannot be invoked without 'new'",
);
}
// Extract options, and shallow-clone transforms.
const {
name = '<unknown>',
transforms = [],
__shimTransforms__ = [],
resolveHook,
importHook,
moduleMapHook,
importMetaHook,
} = options;
const globalTransforms = [...transforms, ...__shimTransforms__];
// Map<FullSpecifier, ModuleCompartmentRecord>
const moduleRecords = new Map();
// Map<FullSpecifier, ModuleInstance>
const instances = new Map();
// Map<FullSpecifier, {ExportsProxy, ProxiedExports, activate()}>
const deferredExports = new Map();
// Validate given moduleMap.
// The module map gets translated on-demand in module-load.js and the
// moduleMap can be invalid in ways that cannot be detected in the
// constructor, but these checks allow us to throw early for a better
// developer experience.
for (const [specifier, aliasNamespace] of entries(moduleMap || {})) {
if (typeof aliasNamespace === 'string') {
// TODO implement parent module record retrieval.
throw TypeError(
`Cannot map module ${q(specifier)} to ${q(
aliasNamespace,
)} in parent compartment`,
);
} else if (weakmapGet(moduleAliases, aliasNamespace) === undefined) {
// TODO create and link a synthetic module instance from the given
// namespace object.
throw ReferenceError(
`Cannot map module ${q(
specifier,
)} because it has no known compartment in this realm`,
);
}
}
const globalObject = {};
setGlobalObjectSymbolUnscopables(globalObject);
// We must initialize all constant properties first because
// `makeSafeEvaluator` may use them to create optimized bindings
// in the evaluator.
// TODO: consider merging into a single initialization if internal
// evaluator is no longer eagerly created
setGlobalObjectConstantProperties(globalObject);
const { safeEvaluate } = makeSafeEvaluator({
globalObject,
globalTransforms,
sloppyGlobalsMode: false,
});
setGlobalObjectMutableProperties(globalObject, {
intrinsics,
newGlobalPropertyNames: sharedGlobalPropertyNames,
makeCompartmentConstructor: targetMakeCompartmentConstructor,
markVirtualizedNativeFunction,
});
// TODO: maybe add evalTaming to the Compartment constructor 3rd options?
setGlobalObjectEvaluators(
globalObject,
safeEvaluate,
markVirtualizedNativeFunction,
);
assign(globalObject, endowments);
weakmapSet(privateFields, this, {
name: `${name}`,
globalTransforms,
globalObject,
safeEvaluate,
resolveHook,
importHook,
moduleMap,
moduleMapHook,
importMetaHook,
moduleRecords,
__shimTransforms__,
deferredExports,
instances,
});
}
Compartment.prototype = CompartmentPrototype;
return Compartment;
};
);

@@ -136,5 +136,4 @@ // @ts-check

*/
// TODO Change to the following line, once our tools don't choke on `?.`.
// See https://github.com/endojs/endo/issues/1514
// const get = key => touchCell(key)?.data?.get(key);
// UNTIL https://github.com/endojs/endo/issues/1514
// Prefer: const get = key => touchCell(key)?.data?.get(key);
const get = key => {

@@ -141,0 +140,0 @@ const cell = touchCell(key);

@@ -18,3 +18,3 @@ import {

} from './commons.js';
import { InertCompartment } from './compartment-shim.js';
import { InertCompartment } from './compartment.js';

@@ -21,0 +21,0 @@ /**

@@ -1,374 +0,27 @@

// Copyright (C) 2018 Agoric
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// @ts-check
import { makeEnvironmentCaptor } from '@endo/env-options';
import {
FERAL_FUNCTION,
FERAL_EVAL,
TypeError,
arrayFilter,
globalThis,
is,
ownKeys,
stringSplit,
noEvalEvaluate,
} from './commons.js';
import { makeHardener } from './make-hardener.js';
import { makeIntrinsicsCollector } from './intrinsics.js';
import whitelistIntrinsics 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';
import tameRegExpConstructor from './tame-regexp-constructor.js';
import enablePropertyOverrides from './enable-property-overrides.js';
import tameLocaleMethods from './tame-locale-methods.js';
import {
setGlobalObjectConstantProperties,
setGlobalObjectMutableProperties,
setGlobalObjectEvaluators,
} from './global-object.js';
import { makeSafeEvaluator } from './make-safe-evaluator.js';
import { initialGlobalPropertyNames } from './permits.js';
import { tameFunctionToString } from './tame-function-tostring.js';
import { tameDomains } from './tame-domains.js';
// We import this first to fail as fast as possible if the shim has been
// transformed or embedded in a way that causes it to run in sloppy mode.
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_NO_SLOPPY.md
import './assert-sloppy-mode.js';
import { globalThis } from './commons.js';
import { repairIntrinsics } from './lockdown.js';
import { tameConsole } from './error/tame-console.js';
import tameErrorConstructor from './error/tame-error-constructor.js';
import { assert, makeAssert } from './error/assert.js';
import { getAnonymousIntrinsics } from './get-anonymous-intrinsics.js';
import { makeCompartmentConstructor } from './compartment-shim.js';
import { tameHarden } from './tame-harden.js';
import { tameSymbolConstructor } from './tame-symbol-constructor.js';
/** @typedef {import('../types.js').LockdownOptions} LockdownOptions */
const { Fail, details: d, quote: q } = assert;
/** @type {Error=} */
let priorLockdown;
// Build a harden() with an empty fringe.
// Gate it on lockdown.
/**
* @template T
* @param {T} ref
* @returns {T}
* @param {import('./lockdown.js').LockdownOptions} options
*/
const safeHarden = makeHardener();
/**
* @callback Transform
* @param {string} source
* @returns {string}
*/
/**
* @callback CompartmentConstructor
* @param {object} endowments
* @param {object} moduleMap
* @param {object} [options]
* @param {Array<Transform>} [options.transforms]
* @param {Array<Transform>} [options.__shimTransforms__]
*/
// TODO https://github.com/endojs/endo/issues/814
// Lockdown currently allows multiple calls provided that the specified options
// of every call agree. With experience, we have observed that lockdown should
// only ever need to be called once and that simplifying lockdown will improve
// the quality of audits.
const assertDirectEvalAvailable = () => {
let allowed = false;
try {
allowed = FERAL_FUNCTION(
'eval',
'SES_changed',
`\
eval("SES_changed = true");
return SES_changed;
`,
)(FERAL_EVAL, false);
// If we get here and SES_changed stayed false, that means the eval was sloppy
// and indirect, which generally creates a new global.
// We are going to throw an exception for failing to initialize SES, but
// good neighbors clean up.
if (!allowed) {
delete globalThis.SES_changed;
}
} catch (_error) {
// We reach here if eval is outright forbidden by a Content Security Policy.
// We allow this for SES usage that delegates the responsibility to isolate
// guest code to production code generation.
allowed = true;
}
if (!allowed) {
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_DIRECT_EVAL.md
throw TypeError(
`SES cannot initialize unless 'eval' is the original intrinsic 'eval', suitable for direct-eval (dynamically scoped eval) (SES_DIRECT_EVAL)`,
);
}
globalThis.lockdown = options => {
const hardenIntrinsics = repairIntrinsics(options);
globalThis.harden = hardenIntrinsics();
};
/**
* @param {LockdownOptions} [options]
* @returns {() => void} repairIntrinsics
* @param {import('./lockdown.js').LockdownOptions} options
*/
export const repairIntrinsics = (options = {}) => {
// First time, absent options default to 'safe'.
// Subsequent times, absent options default to first options.
// Thus, all present options must agree with first options.
// Reconstructing `option` here also ensures that it is a well
// behaved record, with only own data properties.
//
// The `overrideTaming` is not a safety issue. Rather it is a tradeoff
// between code compatibility, which is better with the `'moderate'`
// setting, and tool compatibility, which is better with the `'min'`
// setting. See
// https://github.com/Agoric/SES-shim/blob/master/packages/ses/README.md#enabling-override-by-assignment)
// for an explanation of when to use which.
//
// The `stackFiltering` is not a safety issue. Rather it is a tradeoff
// between relevance and completeness of the stack frames shown on the
// console. Setting`stackFiltering` to `'verbose'` applies no filters, providing
// the raw stack frames that can be quite versbose. Setting
// `stackFrameFiltering` to`'concise'` limits the display to the stack frame
// information most likely to be relevant, eliminating distracting frames
// such as those from the infrastructure. However, the bug you're trying to
// track down might be in the infrastrure, in which case the `'verbose'` setting
// is useful. See
// [`stackFiltering` options](https://github.com/Agoric/SES-shim/blob/master/packages/ses/lockdown-options.md#stackfiltering-options)
// for an explanation.
const { getEnvironmentOption: getenv } = makeEnvironmentCaptor(globalThis);
const {
errorTaming = getenv('LOCKDOWN_ERROR_TAMING', 'safe'),
errorTrapping = getenv('LOCKDOWN_ERROR_TRAPPING', 'platform'),
unhandledRejectionTrapping = getenv(
'LOCKDOWN_UNHANDLED_REJECTION_TRAPPING',
'report',
),
regExpTaming = getenv('LOCKDOWN_REGEXP_TAMING', 'safe'),
localeTaming = getenv('LOCKDOWN_LOCALE_TAMING', 'safe'),
consoleTaming = getenv('LOCKDOWN_CONSOLE_TAMING', 'safe'),
overrideTaming = getenv('LOCKDOWN_OVERRIDE_TAMING', 'moderate'),
stackFiltering = getenv('LOCKDOWN_STACK_FILTERING', 'concise'),
domainTaming = getenv('LOCKDOWN_DOMAIN_TAMING', 'safe'),
evalTaming = getenv('LOCKDOWN_EVAL_TAMING', 'safeEval'),
overrideDebug = arrayFilter(
stringSplit(getenv('LOCKDOWN_OVERRIDE_DEBUG', ''), ','),
/** @param {string} debugName */
debugName => debugName !== '',
),
__hardenTaming__ = getenv('LOCKDOWN_HARDEN_TAMING', 'safe'),
dateTaming = 'safe', // deprecated
mathTaming = 'safe', // deprecated
...extraOptions
} = options;
evalTaming === 'unsafeEval' ||
evalTaming === 'safeEval' ||
evalTaming === 'noEval' ||
Fail`lockdown(): non supported option evalTaming: ${q(evalTaming)}`;
// Assert that only supported options were passed.
// Use Reflect.ownKeys to reject symbol-named properties as well.
const extraOptionsNames = ownKeys(extraOptions);
extraOptionsNames.length === 0 ||
Fail`lockdown(): non supported option ${q(extraOptionsNames)}`;
priorLockdown === undefined ||
// eslint-disable-next-line @endo/no-polymorphic-call
assert.fail(
d`Already locked down at ${priorLockdown} (SES_ALREADY_LOCKED_DOWN)`,
TypeError,
);
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_ALREADY_LOCKED_DOWN.md
priorLockdown = TypeError('Prior lockdown (SES_ALREADY_LOCKED_DOWN)');
// Tease V8 to generate the stack string and release the closures the stack
// trace retained:
priorLockdown.stack;
assertDirectEvalAvailable();
/**
* Because of packagers and bundlers, etc, multiple invocations of lockdown
* might happen in separate instantiations of the source of this module.
* In that case, each one sees its own `firstOptions` variable, so the test
* above will not detect that lockdown has already happened. We
* unreliably test some telltale signs that lockdown has run, to avoid
* trying to lock down a locked down environment. Although the test is
* unreliable, this is consistent with the SES threat model. SES provides
* security only if it runs first in a given realm, or if everything that
* runs before it is SES-aware and cooperative. Neither SES nor anything
* can protect itself from corrupting code that runs first. For these
* purposes, code that turns a realm into something that passes these
* tests without actually locking down counts as corrupting code.
*
* The specifics of what this tests for may change over time, but it
* should be consistent with any setting of the lockdown options.
*/
const seemsToBeLockedDown = () => {
return (
globalThis.Function.prototype.constructor !== globalThis.Function &&
// @ts-ignore harden is absent on globalThis type def.
typeof globalThis.harden === 'function' &&
// @ts-ignore lockdown is absent on globalThis type def.
typeof globalThis.lockdown === 'function' &&
globalThis.Date.prototype.constructor !== globalThis.Date &&
typeof globalThis.Date.now === 'function' &&
// @ts-ignore does not recognize that Date constructor is a special
// Function.
// eslint-disable-next-line @endo/no-polymorphic-call
is(globalThis.Date.prototype.constructor.now(), NaN)
);
};
if (seemsToBeLockedDown()) {
// See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_MULTIPLE_INSTANCES.md
throw TypeError(
`Already locked down but not by this SES instance (SES_MULTIPLE_INSTANCES)`,
);
}
/**
* 1. TAME powers & gather intrinsics first.
*/
tameDomains(domainTaming);
// Replace Function.prototype.toString with one that recognizes
// shimmed functions as honorary native functions.
const markVirtualizedNativeFunction = tameFunctionToString();
const { addIntrinsics, completePrototypes, finalIntrinsics } =
makeIntrinsicsCollector();
const tamedHarden = tameHarden(safeHarden, __hardenTaming__);
addIntrinsics({ harden: tamedHarden });
addIntrinsics(tameFunctionConstructors());
addIntrinsics(tameDateConstructor(dateTaming));
addIntrinsics(tameErrorConstructor(errorTaming, stackFiltering));
addIntrinsics(tameMathObject(mathTaming));
addIntrinsics(tameRegExpConstructor(regExpTaming));
addIntrinsics(tameSymbolConstructor());
addIntrinsics(getAnonymousIntrinsics());
completePrototypes();
const intrinsics = finalIntrinsics();
/**
* Wrap console unless suppressed.
* At the moment, the console is considered a host power in the start
* compartment, and not a primordial. Hence it is absent from the whilelist
* and bypasses the intrinsicsCollector.
*
* @type {((error: any) => string | undefined) | undefined}
*/
let optGetStackString;
if (errorTaming !== 'unsafe') {
optGetStackString = intrinsics['%InitialGetStackString%'];
}
const consoleRecord = tameConsole(
consoleTaming,
errorTrapping,
unhandledRejectionTrapping,
optGetStackString,
);
globalThis.console = /** @type {Console} */ (consoleRecord.console);
// @ts-ignore assert is absent on globalThis type def.
if (errorTaming === 'unsafe' && globalThis.assert === assert) {
// If errorTaming is 'unsafe' we replace the global assert with
// one whose `details` template literal tag does not redact
// unmarked substitution values. IOW, it blabs information that
// was supposed to be secret from callers, as an aid to debugging
// at a further cost in safety.
// @ts-ignore assert is absent on globalThis type def.
globalThis.assert = makeAssert(undefined, true);
}
// Replace *Locale* methods with their non-locale equivalents
tameLocaleMethods(intrinsics, localeTaming);
/**
* 2. WHITELIST to standardize the environment.
*/
// Remove non-standard properties.
// All remaining function encountered during whitelisting are
// branded as honorary native functions.
whitelistIntrinsics(intrinsics, markVirtualizedNativeFunction);
// Initialize the powerful initial global, i.e., the global of the
// start compartment, from the intrinsics.
setGlobalObjectConstantProperties(globalThis);
setGlobalObjectMutableProperties(globalThis, {
intrinsics,
newGlobalPropertyNames: initialGlobalPropertyNames,
makeCompartmentConstructor,
markVirtualizedNativeFunction,
});
if (evalTaming === 'noEval') {
setGlobalObjectEvaluators(
globalThis,
noEvalEvaluate,
markVirtualizedNativeFunction,
);
} else if (evalTaming === 'safeEval') {
const { safeEvaluate } = makeSafeEvaluator({ globalObject: globalThis });
setGlobalObjectEvaluators(
globalThis,
safeEvaluate,
markVirtualizedNativeFunction,
);
} else if (evalTaming === 'unsafeEval') {
// Leave eval function and Function constructor of the initial compartment in-tact.
// Other compartments will not have access to these evaluators unless a guest program
// escapes containment.
}
/**
* 3. HARDEN to share the intrinsics.
*
* We define hardenIntrinsics here so that options are in scope, but return
* it to the caller because we intend to eventually allow vetted shims to run
* between repairs and the hardening of intrinsics and so we can benchmark
* repair separately from hardening.
*/
function hardenIntrinsics() {
// Circumvent the override mistake.
// TODO consider moving this to the end of the repair phase, and
// therefore before vetted shims rather than afterwards. It is not
// clear yet which is better.
// @ts-ignore enablePropertyOverrides does its own input validation
enablePropertyOverrides(intrinsics, overrideTaming, overrideDebug);
// Finally register and optionally freeze all the intrinsics. This
// must be the operation that modifies the intrinsics.
tamedHarden(intrinsics);
// Reveal harden after lockdown.
// Harden is dangerous before lockdown because hardening just
globalThis.repairIntrinsics = options => {
const hardenIntrinsics = repairIntrinsics(options);
// Reveal hardenIntrinsics after repairs.
globalThis.hardenIntrinsics = () => {
// Reveal harden after hardenIntrinsics.
// Harden is dangerous before hardenIntrinsics because hardening just
// about anything will inadvertently render intrinsics irreparable.

@@ -380,17 +33,4 @@ // Also, for modules that must work both before or after lockdown (code

// @ts-ignore harden not yet recognized on globalThis.
globalThis.harden = tamedHarden;
// Returning `true` indicates that this is a JS to SES transition.
return true;
}
return hardenIntrinsics;
globalThis.harden = hardenIntrinsics();
};
};
/**
* @param {LockdownOptions} [options]
*/
export const lockdown = (options = {}) => {
const hardenIntrinsics = repairIntrinsics(options);
hardenIntrinsics();
};

@@ -22,3 +22,3 @@ // Portions adapted from V8 - Copyright 2016 the V8 project authors.

* @param {object} [options.moduleLexicals]
* @param {Array<import('./lockdown-shim.js').Transform>} [options.globalTransforms]
* @param {Array<import('./lockdown.js').Transform>} [options.globalTransforms]
* @param {boolean} [options.sloppyGlobalsMode]

@@ -58,3 +58,3 @@ */

* @param {object} [options]
* @param {Array<import('./lockdown-shim.js').Transform>} [options.localTransforms]
* @param {Array<import('./lockdown.js').Transform>} [options.localTransforms]
*/

@@ -61,0 +61,0 @@ const safeEvaluate = (source, options) => {

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

// Without Math.random
const SharedMath = {
const CommonMath = {
E: 'number',

@@ -446,3 +446,3 @@ LN10: 'number',

// https://github.com/tc39/proposal-array-grouping
groupBy: 'boolean',
groupBy: fn,
},

@@ -654,8 +654,12 @@

'%InitialMath%': {
...SharedMath,
// random is standard but omitted from SharedMath
...CommonMath,
// `%InitialMath%.random()` has the standard unsafe behavior
random: fn,
},
'%SharedMath%': SharedMath,
'%SharedMath%': {
...CommonMath,
// `%SharedMath%.random()` is tamed to always throw
random: fn,
},

@@ -674,2 +678,3 @@ '%InitialDate%': {

'[[Proto]]': '%FunctionPrototype%',
// `%SharedDate%.now()` is tamed to always throw
now: fn,

@@ -1076,2 +1081,4 @@ parse: fn,

prototype: '%MapPrototype%',
// https://github.com/tc39/proposal-array-grouping
groupBy: fn,
},

@@ -1078,0 +1085,0 @@

@@ -20,22 +20,34 @@ // @ts-check

const tamedMethods = {
/**
* `%SharedDate%.now()` throw a `TypeError` starting with "secure mode".
* See https://github.com/endojs/endo/issues/910#issuecomment-1581855420
*/
now() {
return NaN;
throw TypeError('secure mode Calling %SharedDate%.now() throws');
},
};
// Tame the Date constructor.
// Common behavior
// * new Date(x) coerces x into a number and then returns a Date
// for that number of millis since the epoch
// * new Date(NaN) returns a Date object which stringifies to
// 'Invalid Date'
// * new Date(undefined) returns a Date object which stringifies to
// 'Invalid Date'
// OriginalDate (normal standard) behavior
// * Date(anything) gives a string with the current time
// * new Date() returns the current time, as a Date object
// SharedDate behavior
// * Date(anything) returned 'Invalid Date'
// * new Date() returns a Date object which stringifies to
// 'Invalid Date'
/**
* Tame the Date constructor.
* See https://github.com/endojs/endo/issues/910#issuecomment-1581855420
*
* Common behavior
* * `new Date(x)` coerces x into a number and then returns a Date
* for that number of millis since the epoch
* * `new Date(NaN)` returns a Date object which stringifies to
* 'Invalid Date'
* * `new Date(undefined)` returns a Date object which stringifies to
* 'Invalid Date'
*
* OriginalDate (normal standard) behavior preserved by
* `%InitialDate%`.
* * `Date(anything)` gives a string with the current time
* * `new Date()` returns the current time, as a Date object
*
* `%SharedDate%` behavior
* * `Date(anything)` throws a TypeError starting with "secure mode"
* * `new Date()` throws a TypeError starting with "secure mode"
*
* @param {{powers?: string}} [opts]
*/
const makeDateConstructor = ({ powers = 'none' } = {}) => {

@@ -55,6 +67,10 @@ let ResultDate;

if (new.target === undefined) {
return 'Invalid Date';
throw TypeError(
'secure mode Calling %SharedDate% constructor as a function throws',
);
}
if (rest.length === 0) {
rest = [NaN];
throw TypeError(
'secure mode Calling new %SharedDate%() with no arguments throws',
);
}

@@ -74,3 +90,3 @@ return construct(OriginalDate, rest, new.target);

parse: {
value: Date.parse,
value: OriginalDate.parse,
writable: true,

@@ -81,3 +97,3 @@ enumerable: false,

UTC: {
value: Date.UTC,
value: OriginalDate.UTC,
writable: true,

@@ -95,3 +111,3 @@ enumerable: false,

now: {
value: Date.now,
value: OriginalDate.now,
writable: true,

@@ -98,0 +114,0 @@ enumerable: false,

@@ -19,4 +19,23 @@ import {

const sharedMath = create(objectPrototype, otherDescriptors);
// Use concise methods to obtain named functions without constructors.
const tamedMethods = {
/**
* `%SharedMath%.random()` throws a TypeError starting with "secure mode".
* See https://github.com/endojs/endo/issues/910#issuecomment-1581855420
*/
random() {
throw TypeError('secure mode %SharedMath%.random() throws');
},
};
const sharedMath = create(objectPrototype, {
...otherDescriptors,
random: {
value: tamedMethods.random,
writable: true,
enumerable: false,
configurable: true,
},
});
return {

@@ -23,0 +42,0 @@ '%InitialMath%': initialMath,

@@ -22,3 +22,3 @@ /**

export interface LockdownOptions {
export interface RepairOptions {
regExpTaming?: 'safe' | 'unsafe';

@@ -40,2 +40,7 @@ localeTaming?: 'safe' | 'unsafe';

// Deprecated in favor of the more specific RepairOptions
export type LockdownOptions = RepairOptions;
export type RepairIntrinsics = (options?: LockdownOptions) => void;
export type HardenIntrinsics = () => void;
export type Lockdown = (options?: LockdownOptions) => void;

@@ -177,3 +182,3 @@

export interface Assert {
export interface AssertionFunctions {
(

@@ -185,8 +190,2 @@ value: any,

typeof: AssertTypeof;
error(
details?: Details,
errorConstructor?: ErrorConstructor,
options?: AssertMakeErrorOptions,
): Error;
fail(details?: Details, errorConstructor?: ErrorConstructor): never;
equal(

@@ -199,2 +198,11 @@ left: any,

string(specimen: any, details?: Details): asserts specimen is string;
fail(details?: Details, errorConstructor?: ErrorConstructor): never;
}
export interface AssertionUtilities {
error(
details?: Details,
errorConstructor?: ErrorConstructor,
options?: AssertMakeErrorOptions,
): Error;
note(error: Error, details: Details): void;

@@ -208,5 +216,12 @@ details(

bare(payload: any, spaces?: string | number): ToStringable;
}
export interface DeprecatedAssertionUtilities {
makeAssert: MakeAssert;
}
export type Assert = AssertionFunctions &
AssertionUtilities &
DeprecatedAssertionUtilities;
interface CompartmentEvaluateOptions {

@@ -223,2 +238,4 @@ sloppyGlobalsMode?: boolean;

var repairIntrinsics: RepairIntrinsics;
var hardenIntrinsics: HardenIntrinsics;
var lockdown: Lockdown;

@@ -225,0 +242,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

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