Comparing version 0.16.0 to 0.17.0
@@ -6,2 +6,29 @@ # Change Log | ||
## [0.17.0](https://github.com/endojs/endo/compare/ses@0.16.0...ses@0.17.0) (2022-10-24) | ||
### ⚠ BREAKING CHANGES | ||
* **ses:** Prevent surprising global unscopables behavior | ||
* **ses:** Divide scope proxy into four layers | ||
### Features | ||
* **ses:** Revocable evalScope ([0187d1e](https://github.com/endojs/endo/commit/0187d1e3532cfc2f052619c46a8a6331a8c15ae8)) | ||
### Bug Fixes | ||
* **ses:** Prevent surprising global unscopables behavior ([dcb8f5d](https://github.com/endojs/endo/commit/dcb8f5da2a453ac72b7f2cc3208f591a9c298402)) | ||
* **ses:** Protect necessary eval admission before it has been admitted ([3d022b1](https://github.com/endojs/endo/commit/3d022b1e1b34ff9b86cf6af236c499bfbe291298)) | ||
* **ses:** Typo in compartmentEvaluate ([d66db7a](https://github.com/endojs/endo/commit/d66db7a67fab6bdd36d18a931c6a9163e842c3fe)) | ||
* **ses:** Typo in scope-constants ([a4ee1ea](https://github.com/endojs/endo/commit/a4ee1ea5e54d894ad3a3ce0c5e42507f026199e6)) | ||
### Code Refactoring | ||
* **ses:** Divide scope proxy into four layers ([37c4b4a](https://github.com/endojs/endo/commit/37c4b4a22996e33dd4b2a48c67ce649ba88e5528)) | ||
## [0.16.0](https://github.com/endojs/endo/compare/ses@0.15.23...ses@0.16.0) (2022-10-19) | ||
@@ -8,0 +35,0 @@ |
10
NEWS.md
User-visible changes in SES: | ||
# v0.17.0 (2022-10-24) | ||
- Previous versions of SES would leak the proxy used to isolate evaluated | ||
code to functions added to the global object by guest code. | ||
The value of `this` in such functions should be `undefined`, but that is not | ||
possible to emulate in this shim. | ||
This version changes the value of `this` in such functions to be the same as | ||
`globalThis` of the compartment, as would be correct in sloppy mode. | ||
- Removes experimental support for "known scope proxies". | ||
# v0.16.0 (2022-10-19) | ||
@@ -4,0 +14,0 @@ |
{ | ||
"name": "ses", | ||
"version": "0.16.0", | ||
"version": "0.17.0", | ||
"description": "Hardened JavaScript for Fearless Cooperation", | ||
@@ -56,3 +56,3 @@ "keywords": [ | ||
"lint:types": "tsc", | ||
"prepublish": "yarn run clean && yarn build", | ||
"prepare": "yarn run clean && yarn build", | ||
"qt": "ava", | ||
@@ -63,5 +63,5 @@ "test": "tsd && ava", | ||
"devDependencies": { | ||
"@endo/compartment-mapper": "^0.7.14", | ||
"@endo/compartment-mapper": "^0.7.15", | ||
"@endo/eslint-config": "^0.5.1", | ||
"@endo/static-module-record": "^0.7.13", | ||
"@endo/static-module-record": "^0.7.14", | ||
"@endo/test262-runner": "^0.1.28", | ||
@@ -184,3 +184,3 @@ "ava": "^3.12.1", | ||
}, | ||
"gitHead": "8da6dc1002417c0f18cd43b351f8f62d7010260c" | ||
"gitHead": "8fb324d8f13a0c6939dc0c1feb831f72298f1853" | ||
} |
@@ -77,2 +77,3 @@ /* global globalThis */ | ||
matchAll: matchAllSymbol, | ||
unscopables: unscopablesSymbol, | ||
keyFor: symbolKeyFor, | ||
@@ -79,0 +80,0 @@ for: symbolFor, |
@@ -31,7 +31,3 @@ /// <reference types="ses"> | ||
let { globalTransforms } = compartmentFields; | ||
const { | ||
globalObject, | ||
globalLexicals, | ||
knownScopeProxies, | ||
} = compartmentFields; | ||
const { globalObject, globalLexicals } = compartmentFields; | ||
@@ -62,3 +58,2 @@ let localObject = globalLexicals; | ||
sloppyGlobalsMode, | ||
knownScopeProxies, | ||
})); | ||
@@ -71,3 +66,3 @@ } | ||
export const compartmentEvaluate = (compartmentFields, source, options) => { | ||
// Perform this check first to avoid unecessary sanitizing. | ||
// Perform this check first to avoid unnecessary sanitizing. | ||
// TODO Maybe relax string check and coerce instead: | ||
@@ -74,0 +69,0 @@ // https://github.com/tc39/proposal-dynamic-code-brand-checks |
@@ -10,3 +10,2 @@ // @ts-check | ||
WeakMap, | ||
WeakSet, | ||
arrayFilter, | ||
@@ -22,5 +21,5 @@ arrayJoin, | ||
weakmapSet, | ||
weaksetHas, | ||
} from './commons.js'; | ||
import { | ||
setGlobalObjectSymbolUnscopables, | ||
setGlobalObjectConstantProperties, | ||
@@ -124,8 +123,2 @@ setGlobalObjectMutableProperties, | ||
/* eslint-disable-next-line no-underscore-dangle */ | ||
__isKnownScopeProxy__(value) { | ||
const { knownScopeProxies } = weakmapGet(privateFields, this); | ||
return weaksetHas(knownScopeProxies, value); | ||
}, | ||
module(specifier) { | ||
@@ -283,2 +276,4 @@ if (typeof specifier !== 'string') { | ||
setGlobalObjectSymbolUnscopables(globalObject); | ||
// We must initialize all constant properties first because | ||
@@ -291,3 +286,2 @@ // `makeSafeEvaluator` may use them to create optimized bindings | ||
const knownScopeProxies = new WeakSet(); | ||
const { safeEvaluate } = makeSafeEvaluator({ | ||
@@ -298,3 +292,2 @@ globalObject, | ||
sloppyGlobalsMode: false, | ||
knownScopeProxies, | ||
}); | ||
@@ -322,3 +315,2 @@ | ||
globalObject, | ||
knownScopeProxies, | ||
globalLexicals, | ||
@@ -325,0 +317,0 @@ safeEvaluate, |
@@ -1,2 +0,11 @@ | ||
import { defineProperty, objectHasOwnProperty, entries } from './commons.js'; | ||
import { | ||
TypeError, | ||
assign, | ||
create, | ||
defineProperty, | ||
entries, | ||
freeze, | ||
objectHasOwnProperty, | ||
unscopablesSymbol, | ||
} from './commons.js'; | ||
import { makeEvalFunction } from './make-eval-function.js'; | ||
@@ -7,2 +16,32 @@ import { makeFunctionConstructor } from './make-function-constructor.js'; | ||
/** | ||
* The host's ordinary global object is not provided by a `with` block, so | ||
* assigning to Symbol.unscopables has no effect. | ||
* Since this shim uses `with` blocks to create a confined lexical scope for | ||
* guest programs, we cannot emulate the proper behavior. | ||
* With this shim, assigning Symbol.unscopables causes the given lexical | ||
* names to fall through to the terminal scope proxy. | ||
* But, we can install this setter to prevent a program from proceding on | ||
* this false assumption. | ||
* | ||
* @param {Object} globalObject | ||
*/ | ||
export const setGlobalObjectSymbolUnscopables = globalObject => { | ||
defineProperty( | ||
globalObject, | ||
unscopablesSymbol, | ||
freeze( | ||
assign(create(null), { | ||
set: freeze(() => { | ||
throw new TypeError( | ||
`Cannot set Symbol.unscopables of a Compartment's globalThis`, | ||
); | ||
}), | ||
enumerable: false, | ||
configurable: false, | ||
}), | ||
), | ||
); | ||
}; | ||
/** | ||
* setGlobalObjectConstantProperties() | ||
@@ -9,0 +48,0 @@ * Initializes a new global object using a process similar to ECMA specifications |
// Portions adapted from V8 - Copyright 2016 the V8 project authors. | ||
// https://github.com/v8/v8/blob/master/src/builtins/builtins-function.cc | ||
import { | ||
WeakSet, | ||
apply, | ||
immutableObject, | ||
proxyRevocable, | ||
weaksetAdd, | ||
} from './commons.js'; | ||
import { getScopeConstants } from './scope-constants.js'; | ||
import { createScopeHandler } from './scope-handler.js'; | ||
import { apply, freeze } from './commons.js'; | ||
import { strictScopeTerminator } from './strict-scope-terminator.js'; | ||
import { createSloppyGlobalsScopeTerminator } from './sloppy-globals-scope-terminator.js'; | ||
import { makeEvalScopeKit } from './eval-scope.js'; | ||
import { applyTransforms, mandatoryTransforms } from './transforms.js'; | ||
import { makeEvaluateFactory } from './make-evaluate-factory.js'; | ||
import { makeEvaluate } from './make-evaluate.js'; | ||
import { assert } from './error/assert.js'; | ||
@@ -29,3 +24,2 @@ | ||
* @param {bool} [options.sloppyGlobalsMode] | ||
* @param {WeakSet} [options.knownScopeProxies] | ||
*/ | ||
@@ -37,16 +31,15 @@ export const makeSafeEvaluator = ({ | ||
sloppyGlobalsMode = false, | ||
knownScopeProxies = new WeakSet(), | ||
} = {}) => { | ||
const { scopeHandler, scopeController } = createScopeHandler( | ||
const scopeTerminator = sloppyGlobalsMode | ||
? createSloppyGlobalsScopeTerminator(globalObject) | ||
: strictScopeTerminator; | ||
const evalScopeKit = makeEvalScopeKit(); | ||
const { evalScope } = evalScopeKit; | ||
const evaluateContext = freeze({ | ||
evalScope, | ||
globalLexicals, | ||
globalObject, | ||
globalLexicals, | ||
{ | ||
sloppyGlobalsMode, | ||
}, | ||
); | ||
const { proxy: scopeProxy, revoke: revokeScopeProxy } = proxyRevocable( | ||
immutableObject, | ||
scopeHandler, | ||
); | ||
weaksetAdd(knownScopeProxies, scopeProxy); | ||
scopeTerminator, | ||
}); | ||
@@ -57,7 +50,5 @@ // Defer creating the actual evaluator to first use. | ||
let evaluate; | ||
const makeEvaluate = () => { | ||
const provideEvaluate = () => { | ||
if (!evaluate) { | ||
const constants = getScopeConstants(globalObject, globalLexicals); | ||
const evaluateFactory = makeEvaluateFactory(constants); | ||
evaluate = apply(evaluateFactory, scopeProxy, []); | ||
evaluate = makeEvaluate(evaluateContext); | ||
} | ||
@@ -72,3 +63,3 @@ }; | ||
const safeEvaluate = (source, { localTransforms = [] } = {}) => { | ||
makeEvaluate(); | ||
provideEvaluate(); | ||
@@ -83,5 +74,8 @@ // Execute the mandatory transforms last to ensure that any rewritten code | ||
scopeController.allowNextEvalToBeUnsafe = true; | ||
let err; | ||
try { | ||
// Allow next reference to eval produce the unsafe FERAL_EVAL. | ||
// eslint-disable-next-line @endo/no-polymorphic-call | ||
evalScopeKit.allowNextEvalToBeUnsafe(); | ||
// Ensure that "this" resolves to the safe global. | ||
@@ -94,11 +88,10 @@ return apply(evaluate, globalObject, [source]); | ||
} finally { | ||
const unsafeEvalWasStillExposed = scopeController.allowNextEvalToBeUnsafe; | ||
scopeController.allowNextEvalToBeUnsafe = false; | ||
const unsafeEvalWasStillExposed = 'eval' in evalScope; | ||
delete evalScope.eval; | ||
if (unsafeEvalWasStillExposed) { | ||
// Barring a defect in the SES shim, the scope proxy should allow the | ||
// Barring a defect in the SES shim, the evalScope should allow the | ||
// powerful, unsafe `eval` to be used by `evaluate` exactly once, as the | ||
// very first name that it attempts to access from the lexical scope. | ||
// A defect in the SES shim could throw an exception after we set | ||
// `scopeController.allowNextEvalToBeUnsafe` and before `evaluate` | ||
// calls `eval` internally. | ||
// `evalScope.eval` and before `evaluate` calls `eval` internally. | ||
// If we get here, SES is very broken. | ||
@@ -113,3 +106,3 @@ // This condition is one where this vat is now hopelessly confused, and | ||
// source code. | ||
revokeScopeProxy(); | ||
evalScopeKit.revoked = { err }; | ||
// TODO A GOOD PLACE TO PANIC(), i.e., kill the vat incarnation. | ||
@@ -116,0 +109,0 @@ // See https://github.com/Agoric/SES-shim/issues/490 |
@@ -80,3 +80,3 @@ import { | ||
* identifierPattern | ||
* Simplified validation of indentifier names: may only contain alphanumeric | ||
* Simplified validation of identifier names: may only contain alphanumeric | ||
* characters (or "$" or "_"), and may not start with a digit. This is safe | ||
@@ -148,25 +148,26 @@ * and does not reduces the compatibility of the shim. The motivation for | ||
* @param {Object} globalObject | ||
* @param {Object} localObject | ||
* @param {Object} globalLexicals | ||
*/ | ||
export const getScopeConstants = (globalObject, localObject = {}) => { | ||
export const getScopeConstants = (globalObject, globalLexicals = {}) => { | ||
// getOwnPropertyNames() does ignore Symbols so we don't need to | ||
// filter them out. | ||
const globalNames = getOwnPropertyNames(globalObject); | ||
const localNames = getOwnPropertyNames(localObject); | ||
const globalObjectNames = getOwnPropertyNames(globalObject); | ||
const globalLexicalNames = getOwnPropertyNames(globalLexicals); | ||
// Collect all valid & immutable identifiers from the endowments. | ||
const localConstants = arrayFilter( | ||
localNames, | ||
const globalLexicalConstants = arrayFilter( | ||
globalLexicalNames, | ||
name => | ||
isValidIdentifierName(name) && isImmutableDataProperty(localObject, name), | ||
isValidIdentifierName(name) && | ||
isImmutableDataProperty(globalLexicals, name), | ||
); | ||
// Collect all valid & immutable identifiers from the global that | ||
// are also not present in the endwoments (immutable or not). | ||
const globalConstants = arrayFilter( | ||
globalNames, | ||
// are also not present in the endowments (immutable or not). | ||
const globalObjectConstants = arrayFilter( | ||
globalObjectNames, | ||
name => | ||
// Can't define a constant: it would prevent a | ||
// lookup on the endowments. | ||
!arrayIncludes(localNames, name) && | ||
!arrayIncludes(globalLexicalNames, name) && | ||
isValidIdentifierName(name) && | ||
@@ -176,3 +177,6 @@ isImmutableDataProperty(globalObject, name), | ||
return [...globalConstants, ...localConstants]; | ||
return { | ||
globalObjectConstants, | ||
globalLexicalConstants, | ||
}; | ||
}; |
@@ -1364,3 +1364,2 @@ /* eslint-disable no-restricted-globals */ | ||
toString: fn, | ||
__isKnownScopeProxy__: fn, | ||
import: asyncFn, | ||
@@ -1367,0 +1366,0 @@ load: asyncFn, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2884002
65
60498
2