fix(ses): Do not get confused by well-known look-alikes
MODIFY
packages/ses/src/commons.js
packages/ses/src/commons.js
@@ -72,6 +72,8 @@ export const {
toStringTag: toStringTagSymbol,
iterator: iteratorSymbol,
matchAll: matchAllSymbol,
+ keyFor: symbolKeyFor,
+ for: symbolFor,
} = Symbol;
export const { isInteger } = Number;

MODIFY
packages/ses/src/whitelist-intrinsics.js
packages/ses/src/whitelist-intrinsics.js
import { whitelist, FunctionInstance, isAccessorPermit } from './whitelist.js';
import {
+ Map,
String,
TypeError,
+ arrayFilter,
arrayIncludes,
+ arrayMap,
+ entries,
getOwnPropertyDescriptor,
getPrototypeOf,
isObject,
+ mapGet,
objectHasOwnProperty,
ownKeys,
- stringSlice,
+ symbolKeyFor,
} from './commons.js';
+/**
+ * whitelistIntrinsics()
+ * Removes all non-allowed properties found by recursively and
+ * reflectively walking own property chains.
+ *
+ * @param {Object} intrinsics
+ * @param {(Object) => void} markVirtualizedNativeFunction
+ */
+export default function whitelistIntrinsics(
+ intrinsics,
+ markVirtualizedNativeFunction,
+) {
+ // These primitives are allowed allowed for permits.
+ const primitives = ['undefined', 'boolean', 'number', 'string', 'symbol'];
+
+ // These symbols are allowed as well-known symbols
+ const wellKnownSymbolNames = new Map(
+ intrinsics.Symbol
+ ? arrayMap(
+ arrayFilter(
+ entries(whitelist.Symbol),
+ ([name, permit]) =>
+ permit === 'symbol' &&
+ typeof intrinsics.Symbol[name] === 'symbol',
+ ),
+ ([name]) => [intrinsics.Symbol[name], `@@${name}`],
+ )
+ : [],
+ );
+
/**
* asStringPropertyName()
*
@@ -67,28 +102,24 @@ function asStringPropertyName(path, prop) {
return prop;
}
+ const wellKnownSymbol = mapGet(wellKnownSymbolNames, prop);
+
if (typeof prop === 'symbol') {
- return `@@${stringSlice(String(prop), 14, -1)}`;
+ if (wellKnownSymbol) {
+ return wellKnownSymbol;
+ } else {
+ const registeredKey = symbolKeyFor(prop);
+ if (registeredKey !== undefined) {
+ return `RegisteredSymbol(${registeredKey})`;
+ } else {
+ return `Unique${String(prop)}`;
+ }
+ }
}
throw new TypeError(`Unexpected property name type ${path} ${prop}`);
}
-/**
- * whitelistIntrinsics()
- * Removes all non-allowed properties found by recursively and
- * reflectively walking own property chains.
- *
- * @param {Object} intrinsics
- * @param {(Object) => void} markVirtualizedNativeFunction
- */
-export default function whitelistIntrinsics(
- intrinsics,
- markVirtualizedNativeFunction,
-) {
- // These primities are allowed allowed for permits.
- const primitives = ['undefined', 'boolean', 'number', 'string', 'symbol'];
-
/*
* visitPrototype()
* Validate the object's [[prototype]] against a permit.

MODIFY
packages/ses/src/whitelist.js
packages/ses/src/whitelist.js
@@ -224,7 +224,10 @@ export const NativeErrors = [
* property definition.
* <li>"ObjectPrototype" is the default "[[Proto]]" (when not specified).
* <li>Constants "fn" and "getter" are used to keep the structure DRY.
- * <li>Symbol properties are listed using the "@@name" form.
+ * <li>Symbol properties are listed as follow:
+ * <li>Well-known symbols use the "@@name" form.
+ * <li>Registered symbols use the "RegisteredSymbol(key)" form.
+ * <li>Unique symbols use the "UniqueSymbol(description)" form.
*/
// Function Instances

ADD
packages/ses/test/test-whitelist-intrinsics.js
+import test from 'ava';
+import '../index.js';
+import whitelistIntrinsics from '../src/whitelist-intrinsics.js';
+
+// eslint-disable-next-line no-eval
+if (!eval.toString().includes('native code')) {
+ throw new TypeError('Module "esm" enabled: aborting');
+}
+
+test('whitelistIntrinsics - Well-known symbols', t => {
+ const SymbolIterator = Symbol('Symbol.iterator');
+ const RogueSymbolIterator = Symbol('Symbol.iterator');
+ const ArrayProto = { [RogueSymbolIterator]() {} };
+ const StringProto = { [SymbolIterator]() {} };
+ const StringProtoIterator = StringProto[SymbolIterator];
+ const intrinsics = Object.freeze({
+ __proto__: null,
+ Symbol: {
+ __proto__: Function.prototype,
+ // Well-known symbol under test
+ iterator: SymbolIterator,
+ // Needed symbol found on %FunctionPrototype%
+ hasInstance: Symbol.hasInstance,
+ },
+ Array: {
+ __proto__: Function.prototype,
+ prototype: ArrayProto,
+ },
+ String: {
+ __proto__: Function.prototype,
+ prototype: StringProto,
+ },
+ // Required intrinsics
+ '%ArrayPrototype%': ArrayProto,
+ '%StringPrototype%': StringProto,
+ '%FunctionPrototype%': Function.prototype,
+ // Yes, this is clearly not inert
+ '%InertFunction%': Function.prototype.constructor,
+ '%ObjectPrototype%': Object.prototype,
+ Object,
+ });
+ whitelistIntrinsics(intrinsics, () => {});
+ t.is(
+ ArrayProto[RogueSymbolIterator],
+ undefined,
+ `Well-known Symbol look-alike should have been removed`,
+ );
+ t.is(
+ StringProto[SymbolIterator],
+ StringProtoIterator,
+ `Well-known Symbol is kept`,
+ );
+});

fix(ses): Do not get confused by well-known look-alikes