Skip to content

Commit 05f1c8f

Browse files
committed
fix(ses): Lockdown under no-unsafe-eval Content-Security-Policy
1 parent 342afa8 commit 05f1c8f

File tree

3 files changed

+51
-4
lines changed

3 files changed

+51
-4
lines changed

packages/ses/src/tame-function-constructors.js

+20-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
SyntaxError,
44
TypeError,
55
defineProperties,
6+
freeze,
67
getPrototypeOf,
78
setPrototypeOf,
89
} from './commons.js';
@@ -46,10 +47,26 @@ export default function tameFunctionConstructors() {
4647
try {
4748
// Verify that the method is not callable.
4849
// eslint-disable-next-line @endo/no-polymorphic-call
49-
FERAL_FUNCTION.prototype.constructor('return 1');
50-
} catch (ignore) {
50+
eval('');
51+
} catch {
52+
const unsafeFunctionConstructors = [
53+
(function () {}),
54+
(function *() {}),
55+
(async function() {}),
56+
(async function *() {}),
57+
].filter(f => {
58+
try {
59+
f.constructor('');
60+
} catch {
61+
return false;
62+
}
63+
return true;
64+
})
65+
if (unsafeFunctionConstructors.length) {
66+
throw new Error(`SES_NO_UNSAFE_EVAL: Lockdown can proceed only with a usable evaluator or if all evaluators are neutralized by a no-unsafe-eval Content-Security-Policy; eval is disabled but Lockdown found working function constructors: ${unsafeFunctionConstructors.map(f => f.constructor.name).join(', ')}`);
67+
}
5168
// Throws, no need to patch.
52-
return harden({});
69+
return freeze({});
5370
}
5471

5572
const newIntrinsics = {};

packages/ses/test/no-eval.js

+30
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,33 @@
55
globalThis.eval = _source => {
66
throw new TypeError('no unsafe-eval, as if by content-security-policy');
77
};
8+
9+
const functionPrototype = Function.prototype;
10+
const functionConstructor = _source => {
11+
throw new TypeError('no unsafe-eval, as if by content-security-policy');
12+
};
13+
functionConstructor.prototype = functionPrototype;
14+
globalThis.Function = functionConstructor;
15+
Object.defineProperty(Function.prototype, 'constructor', {
16+
value: functionConstructor,
17+
writable: false,
18+
configurable: true,
19+
});
20+
21+
for (const f of [
22+
(function *() {}),
23+
(async function() {}),
24+
(async function *() {}),
25+
]) {
26+
const constructor = _source => {
27+
throw new TypeError('no unsafe-eval, as if by content-security-policy');
28+
};
29+
constructor.__proto__ = functionConstructor;
30+
const prototype = f.__proto__;
31+
constructor.prototype = prototype;
32+
Object.defineProperty(prototype, 'constructor', {
33+
value: constructor,
34+
writable: false,
35+
configurable: true,
36+
});
37+
}

packages/ses/test/test-no-eval.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import '../index.js';
55
// I've manually verified that this is not failing due to the dynamic-eval
66
// check in src/lockdown-shim.js, and the remaining issues are captured in
77
// https://github.com/endojs/endo/issues/903.
8-
test.failing('lockdown must not throw when eval is forbidden', t => {
8+
test('lockdown must not throw when eval is forbidden', t => {
99
lockdown({ errorTaming: 'unsafe' });
1010
t.pass();
1111
});

0 commit comments

Comments
 (0)