Skip to content

Commit 6445f79

Browse files
authored
Merge pull request #1491 from endojs/naugtur-policy-attenuate-globals
feat(compartment-mapper): globals attenuation enabling LavaMoat feature parity
2 parents 635d678 + 5c7e048 commit 6445f79

24 files changed

+1112
-156
lines changed

packages/compartment-mapper/demo/policy/app.js

+4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
/* global require Buffer module */
2+
require('@endo/compartment-mapper-demo-polyfill1');
23
const Poet = require('entropoetry');
34
const fs = require('fs');
45

6+
// eslint-disable-next-line no-undef
7+
console.log({ answerPolyfill });
8+
59
fs.existsSync('/notthere');
610

711
const p = new Poet();
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
console.log('Attenuator imported');
2-
export const attenuate = (params, originalModuleNamespace) => {
2+
const attenuate = (params, originalObject) => {
33
console.log('Attenuator called', params);
44
const ns = params.reduce((acc, k) => {
5-
acc[k] = originalModuleNamespace[k];
5+
acc[k] = originalObject[k];
66
return acc;
77
}, {});
88
return ns;
99
};
10+
11+
export const attenuateGlobals = attenuate;
12+
export const attenuateModule = attenuate;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
console.log('Attenuator2 imported');
2+
3+
const { create, assign, fromEntries, entries, defineProperties } = Object;
4+
5+
// minimal implementation of LavaMoat-style globals attenuator with write propagation
6+
let globalOverrides = create(null);
7+
export const attenuateGlobals = (params, originalObject, globalThis) => {
8+
const policy = params[0];
9+
console.log('Attenuator2 called', params);
10+
if (policy === 'root') {
11+
assign(globalThis, originalObject);
12+
// This assumes that the root compartment is the first to be attenuated
13+
globalOverrides = globalThis;
14+
return;
15+
}
16+
defineProperties(
17+
globalThis,
18+
fromEntries(
19+
entries(policy)
20+
.map(([key, policyValue]) => {
21+
if (policyValue) {
22+
const spec = {
23+
configurable: false,
24+
enumerable: true,
25+
get() {
26+
console.log('- get', key);
27+
return globalOverrides[key] || originalObject[key];
28+
},
29+
};
30+
if (policyValue === 'write') {
31+
spec.set = value => {
32+
console.log('- set', key);
33+
globalOverrides[key] = value;
34+
};
35+
}
36+
return [key, spec];
37+
}
38+
return null;
39+
})
40+
.filter(a => a),
41+
),
42+
);
43+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@endo/compartment-mapper-demo-lavamoat-style-attenuator",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"type": "module",
6+
"scripts": {
7+
"preinstall": "echo DO NOT CALL INSTALL SCRIPTS; exit -1"
8+
}
9+
}

packages/compartment-mapper/demo/policy/index.mjs

+27-11
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ const ApiSubsetOfBuffer = harden({ from: Buffer.from });
2525

2626
const options = {
2727
policy: {
28+
defaultAttenuator:
29+
'@endo/compartment-mapper-demo-lavamoat-style-attenuator',
2830
entry: {
29-
globals: 'any',
31+
globals: ['root'],
32+
noGlobalFreeze: true,
3033
packages: 'any',
3134
builtins: {
3235
fs: {
@@ -36,10 +39,13 @@ const options = {
3639
},
3740
},
3841
resources: {
39-
'@endo/compartment-mapper-demo-policy-attenuator1': {
40-
globals: {
41-
console: true,
42-
},
42+
'@endo/compartment-mapper-demo-polyfill1': {
43+
globals: [
44+
{
45+
console: true,
46+
answerPolyfill: 'write',
47+
},
48+
],
4349
},
4450
dotenv: {
4551
builtins: {
@@ -51,8 +57,9 @@ const options = {
5157
path: true,
5258
},
5359
globals: {
54-
console: true,
55-
process: true,
60+
// one attenuator implementation can be used for builtins and globals
61+
attenuate: '@endo/compartment-mapper-demo-policy-attenuator1',
62+
params: ['console', 'process'],
5663
},
5764
},
5865
entropoetry: {
@@ -72,8 +79,16 @@ const options = {
7279
builtins: {
7380
buffer: true,
7481
},
82+
globals: 'any',
83+
},
84+
'@endo/compartment-mapper-demo-policy-attenuator1': {
7585
globals: {
76-
Buffer: true,
86+
console: true,
87+
},
88+
},
89+
'@endo/compartment-mapper-demo-lavamoat-style-attenuator': {
90+
globals: {
91+
console: true,
7792
},
7893
},
7994
},
@@ -105,19 +120,20 @@ console.log('\n\n________________________________________________ Location\n');
105120

106121
console.log('\n\n________________________________________________ Archive\n');
107122
{
123+
console.log('>----------start -> makeArchive');
108124
const archive = await makeArchive(readPower, entrypointPath, {
109125
modules: options.modules,
110126
policy: options.policy,
111127
});
112-
console.log('>----------makeArchive');
128+
console.log('>----------makeArchive -> parseArchive');
113129
const application = await parseArchive(archive, '<unknown>', {
114130
modules: options.modules,
115131
});
116-
console.log('>----------parseArchive');
132+
console.log('>----------parseArchive -> import');
117133
const { namespace } = await application.import({
118134
globals: options.globals,
119135
modules: options.modules,
120136
});
121-
console.log('>----------import');
137+
console.log('>----------import -> end');
122138
console.log(2, namespace.poem);
123139
}

packages/compartment-mapper/demo/policy/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
"preinstall": "echo DO NOT CALL INSTALL SCRIPTS; exit -1"
99
},
1010
"dependencies": {
11+
"@endo/compartment-mapper-demo-lavamoat-style-attenuator": "file:att2",
1112
"@endo/compartment-mapper-demo-policy-attenuator1": "file:att1",
13+
"@endo/compartment-mapper-demo-polyfill1": "file:polyfill",
1214
"dotenv": "16.0.1",
1315
"entropoetry": "2.0.0-alpha4"
1416
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
(function polyfill() {
2+
if (typeof globalThis !== 'undefined') {
3+
// eslint-disable-next-line no-undef
4+
globalThis.answerPolyfill = 42;
5+
} else {
6+
this.answerPolyfill = 42;
7+
}
8+
console.log('answerPolyfill added');
9+
})();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@endo/compartment-mapper-demo-polyfill1",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"type": "module",
6+
"scripts": {
7+
"preinstall": "echo DO NOT CALL INSTALL SCRIPTS; exit -1"
8+
}
9+
}

packages/compartment-mapper/src/compartment-map.js

+3-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// @ts-check
22
/// <reference types="ses"/>
33

4+
import { assertPackagePolicy } from './policy-format.js';
5+
46
// TODO convert to the new `||` assert style.
57
// Deferred because this file pervasively uses simple template strings rather than
68
// template strings tagged with `assert.details` (aka `X`), and uses
@@ -378,14 +380,7 @@ const assertPolicy = (
378380
path,
379381
url = '<unknown-compartment-map.json>',
380382
) => {
381-
const policy = Object(allegedPolicy);
382-
assert(
383-
allegedPolicy === undefined ||
384-
(allegedPolicy === policy && !Array.isArray(policy)),
385-
`${path}.policy must be undefined or an object, got ${allegedPolicy} in ${q(
386-
url,
387-
)}`,
388-
);
383+
assertPackagePolicy(allegedPolicy, `${path}.policy`, url);
389384
};
390385

391386
/**

packages/compartment-mapper/src/import-archive.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ export const parseArchive = async (
269269
// must be given a module namespace object that passes a brand check.
270270
// We don't have module instances for the preload phase, so we supply fake
271271
// namespaces.
272-
const { compartment } = link(compartmentMap, {
272+
const { compartment, pendingJobsPromise } = link(compartmentMap, {
273273
makeImportHook,
274274
parserForLanguage,
275275
modules: Object.fromEntries(
@@ -280,6 +280,8 @@ export const parseArchive = async (
280280
Compartment,
281281
});
282282

283+
await pendingJobsPromise;
284+
283285
await compartment.load(moduleSpecifier);
284286
unseen.size === 0 ||
285287
Fail`Archive contains extraneous files: ${q([...unseen])} in ${q(
@@ -288,7 +290,7 @@ export const parseArchive = async (
288290
}
289291

290292
/** @type {ExecuteFn} */
291-
const execute = options => {
293+
const execute = async options => {
292294
const { globals, modules, transforms, __shimTransforms__, Compartment } =
293295
options || {};
294296
const makeImportHook = makeArchiveImportHookMaker(
@@ -298,7 +300,7 @@ export const parseArchive = async (
298300
computeSha512,
299301
computeSourceLocation,
300302
);
301-
const { compartment } = link(compartmentMap, {
303+
const { compartment, pendingJobsPromise } = link(compartmentMap, {
302304
makeImportHook,
303305
parserForLanguage,
304306
globals,
@@ -307,6 +309,9 @@ export const parseArchive = async (
307309
__shimTransforms__,
308310
Compartment,
309311
});
312+
313+
await pendingJobsPromise;
314+
310315
// eslint-disable-next-line dot-notation
311316
return compartment['import'](moduleSpecifier);
312317
};

packages/compartment-mapper/src/import-hook.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
/** @typedef {import('./types.js').CompartmentDescriptor} CompartmentDescriptor */
1212
/** @typedef {import('./types.js').ImportHookMaker} ImportHookMaker */
1313

14-
import { assertModulePolicy } from './policy.js';
14+
import { enforceModulePolicy } from './policy.js';
1515
import { unpackReadPowers } from './powers.js';
1616

1717
// q, as in quote, for quoting strings in error messages.
@@ -146,7 +146,7 @@ export const makeImportHookMaker = (
146146
// The `moduleMapHook` captures all third-party dependencies.
147147
if (moduleSpecifier !== '.' && !moduleSpecifier.startsWith('./')) {
148148
if (has(exitModules, moduleSpecifier)) {
149-
assertModulePolicy(moduleSpecifier, compartmentDescriptor.policy, {
149+
enforceModulePolicy(moduleSpecifier, compartmentDescriptor.policy, {
150150
exit: true,
151151
});
152152
packageSources[moduleSpecifier] = {

packages/compartment-mapper/src/import.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export const loadLocation = async (readPowers, moduleLocation, options) => {
8181
undefined,
8282
searchSuffixes,
8383
);
84-
const { compartment } = link(compartmentMap, {
84+
const { compartment, pendingJobsPromise } = link(compartmentMap, {
8585
makeImportHook,
8686
parserForLanguage,
8787
globals,
@@ -93,6 +93,8 @@ export const loadLocation = async (readPowers, moduleLocation, options) => {
9393
Compartment,
9494
});
9595

96+
await pendingJobsPromise;
97+
9698
return compartment.import(moduleSpecifier);
9799
};
98100

0 commit comments

Comments
 (0)