0
# Compartments
1
2
Compartments provide isolated execution contexts for running untrusted code with controlled capabilities and communication channels. Each compartment has its own global object but shares frozen intrinsics with other compartments, enabling secure code execution without complete process isolation.
3
4
## Capabilities
5
6
### Compartment Constructor
7
8
Creates isolated execution contexts with configurable globals, module systems, and security policies.
9
10
```javascript { .api }
11
/**
12
* Creates a new compartment with isolated global scope
13
* @param options - Configuration options for the compartment
14
*/
15
class Compartment {
16
constructor(options?: CompartmentOptions & { __options__: true });
17
18
// Legacy constructor (deprecated)
19
constructor(
20
globals?: Record<PropertyKey, any> | undefined,
21
modules?: Record<string, ModuleDescriptor>,
22
options?: CompartmentOptions
23
);
24
}
25
```
26
27
**Usage Examples:**
28
29
```javascript
30
import 'ses';
31
32
lockdown();
33
34
// Basic compartment with minimal capabilities
35
const basicCompartment = new Compartment({
36
__options__: true
37
});
38
39
// Compartment with specific globals
40
const endowedCompartment = new Compartment({
41
globals: {
42
console: harden(console),
43
fetch: harden(fetch), // Provide specific capabilities
44
myAPI: harden({
45
getValue: () => "secure data"
46
})
47
},
48
__options__: true
49
});
50
51
// Named compartment for debugging
52
const namedCompartment = new Compartment({
53
name: "user-script-sandbox",
54
globals: { console: harden(console) },
55
__options__: true
56
});
57
```
58
59
### Code Evaluation
60
61
Evaluates JavaScript code within the compartment's isolated context.
62
63
```javascript { .api }
64
/**
65
* Evaluates JavaScript code in the compartment's context
66
* @param code - JavaScript code string to evaluate
67
* @param options - Evaluation configuration options
68
* @returns Result of code evaluation
69
*/
70
evaluate(code: string, options?: EvaluateOptions): any;
71
```
72
73
**Usage Examples:**
74
75
```javascript
76
import 'ses';
77
78
lockdown();
79
80
const compartment = new Compartment({
81
globals: { console: harden(console) },
82
__options__: true
83
});
84
85
// Basic evaluation
86
const result = compartment.evaluate(`
87
const data = { message: "Hello from compartment!" };
88
console.log("Compartment executing code");
89
data.message;
90
`);
91
console.log(result); // "Hello from compartment!"
92
93
// Evaluation with options
94
const sloppyResult = compartment.evaluate(`
95
// This would normally fail in strict mode
96
undeclaredVar = "sloppy mode";
97
undeclaredVar;
98
`, {
99
sloppyGlobalsMode: true
100
});
101
102
// Code transformation during evaluation
103
const transformedResult = compartment.evaluate(`
104
console.log("Original message");
105
`, {
106
transforms: [
107
(source) => source.replace(/Original/, 'Transformed')
108
]
109
});
110
```
111
112
### Module Import (Asynchronous)
113
114
Dynamically imports modules with promise-based loading.
115
116
```javascript { .api }
117
/**
118
* Dynamically imports a module asynchronously
119
* @param specifier - Module specifier to import
120
* @returns Promise resolving to module namespace object
121
*/
122
import(specifier: string | null): Promise<{ namespace: ModuleExportsNamespace }>;
123
```
124
125
**Usage Examples:**
126
127
```javascript
128
import 'ses';
129
130
lockdown();
131
132
const compartment = new Compartment({
133
importHook: async (specifier) => {
134
if (specifier === 'my-module') {
135
return {
136
source: `
137
export const greeting = "Hello";
138
export function sayHello(name) {
139
return greeting + ", " + name + "!";
140
}
141
`
142
};
143
}
144
throw new Error(`Module not found: ${specifier}`);
145
},
146
__options__: true
147
});
148
149
// Import module asynchronously
150
compartment.import('my-module').then(({ namespace }) => {
151
console.log(namespace.sayHello('World')); // "Hello, World!"
152
});
153
```
154
155
### Module Import (Synchronous)
156
157
Synchronously imports modules for use cases where async loading is not suitable.
158
159
```javascript { .api }
160
/**
161
* Synchronously imports a module that's already loaded or can be loaded synchronously
162
* @param specifier - Module specifier to import
163
* @returns Module namespace object
164
*/
165
importNow(specifier: string): ModuleExportsNamespace;
166
```
167
168
**Usage Examples:**
169
170
```javascript
171
import 'ses';
172
173
lockdown();
174
175
const compartment = new Compartment({
176
modules: {
177
'math-utils': {
178
source: `
179
export const add = (a, b) => a + b;
180
export const multiply = (a, b) => a * b;
181
`
182
}
183
},
184
importNowHook: (specifier) => {
185
if (specifier === 'runtime-module') {
186
return {
187
source: `export const runtime = true;`
188
};
189
}
190
},
191
__options__: true
192
});
193
194
// Import pre-loaded module
195
const mathUtils = compartment.importNow('math-utils');
196
console.log(mathUtils.add(2, 3)); // 5
197
198
// Import via importNowHook
199
const runtimeMod = compartment.importNow('runtime-module');
200
console.log(runtimeMod.runtime); // true
201
```
202
203
### Module Loading
204
205
Preloads modules without importing them into the current scope.
206
207
```javascript { .api }
208
/**
209
* Preloads a module without importing it
210
* @param specifier - Module specifier to load
211
* @returns Promise that resolves when module is loaded
212
*/
213
load(specifier: string): Promise<void>;
214
```
215
216
### Module Access
217
218
Accesses already loaded module namespaces.
219
220
```javascript { .api }
221
/**
222
* Gets the namespace of an already loaded module
223
* @param specifier - Module specifier
224
* @returns Module namespace object
225
*/
226
module(specifier: string): ModuleExportsNamespace;
227
```
228
229
### Compartment Properties
230
231
Access to compartment metadata and global object.
232
233
```javascript { .api }
234
/**
235
* The compartment's global object
236
*/
237
get globalThis(): Record<PropertyKey, any>;
238
239
/**
240
* The compartment's name for debugging
241
*/
242
get name(): string;
243
```
244
245
**Usage Examples:**
246
247
```javascript
248
import 'ses';
249
250
lockdown();
251
252
const compartment = new Compartment({
253
name: 'test-compartment',
254
globals: { customGlobal: 'test value' },
255
__options__: true
256
});
257
258
// Access compartment properties
259
console.log(compartment.name); // 'test-compartment'
260
console.log(compartment.globalThis.customGlobal); // 'test value'
261
262
// Each compartment has its own global object
263
const anotherCompartment = new Compartment({ __options__: true });
264
console.log(compartment.globalThis !== anotherCompartment.globalThis); // true
265
266
// But they share the same intrinsics
267
console.log(compartment.globalThis.Array === anotherCompartment.globalThis.Array); // true
268
```
269
270
## Configuration Options
271
272
### CompartmentOptions Interface
273
274
Comprehensive configuration for compartment behavior, module loading, and security policies.
275
276
```javascript { .api }
277
interface CompartmentOptions {
278
/** Compartment name for debugging purposes */
279
name?: string;
280
281
/** Initial global variables for the compartment */
282
globals?: Map<string, any>;
283
284
/** Static module map for pre-loaded modules */
285
modules?: Map<string, ModuleDescriptor>;
286
287
/** Source transformation functions applied to evaluated code */
288
transforms?: Array<Transform>;
289
290
/** Internal shim-specific transforms */
291
__shimTransforms__?: Array<Transform>;
292
293
/** Module resolution hook for converting import specifiers */
294
resolveHook?: ResolveHook;
295
296
/** Async module loading hook */
297
importHook?: ImportHook;
298
299
/** Sync module loading hook */
300
importNowHook?: ImportNowHook;
301
302
/** Dynamic module mapping hook */
303
moduleMapHook?: ModuleMapHook;
304
305
/** Hook for customizing import.meta objects */
306
importMetaHook?: ImportMetaHook;
307
308
/** Use native compartment implementation if available */
309
__native__?: boolean;
310
311
/** Control namespace boxing behavior */
312
__noNamespaceBox__?: boolean;
313
314
/** Fail fast on first module loading error */
315
noAggregateLoadErrors?: boolean;
316
}
317
```
318
319
### EvaluateOptions Interface
320
321
Options for controlling code evaluation behavior.
322
323
```javascript { .api }
324
interface EvaluateOptions {
325
/** Source transformation functions for this evaluation */
326
transforms?: Array<Transform>;
327
328
/** Allow sloppy mode for legacy code compatibility */
329
sloppyGlobalsMode?: boolean;
330
331
/** Internal module shim lexical bindings */
332
__moduleShimLexicals__?: Record<string, any>;
333
334
/** Evade HTML comment test for browser compatibility */
335
__evadeHtmlCommentTest__?: boolean;
336
337
/** Reject some direct eval expressions for security */
338
__rejectSomeDirectEvalExpressions__?: boolean;
339
}
340
```
341
342
## Advanced Usage Patterns
343
344
### Cross-Compartment Communication
345
346
Secure communication between compartments using hardened objects:
347
348
```javascript
349
import 'ses';
350
351
lockdown();
352
353
// Create a shared communication channel
354
const createChannel = () => {
355
const listeners = [];
356
return harden({
357
send: (message) => {
358
listeners.forEach(listener => listener(message));
359
},
360
listen: (callback) => {
361
listeners.push(callback);
362
}
363
});
364
};
365
366
const channel = createChannel();
367
368
// Compartment A - sender
369
const senderCompartment = new Compartment({
370
globals: { channel },
371
__options__: true
372
});
373
374
// Compartment B - receiver
375
const receiverCompartment = new Compartment({
376
globals: {
377
channel,
378
console: harden(console)
379
},
380
__options__: true
381
});
382
383
// Set up receiver
384
receiverCompartment.evaluate(`
385
channel.listen((message) => {
386
console.log('Received:', message);
387
});
388
`);
389
390
// Send message from sender
391
senderCompartment.evaluate(`
392
channel.send({ type: 'greeting', data: 'Hello from sender!' });
393
`);
394
```
395
396
### Module Federation
397
398
Sharing modules between compartments:
399
400
```javascript
401
import 'ses';
402
403
lockdown();
404
405
// Shared module compartment
406
const moduleCompartment = new Compartment({
407
modules: {
408
'shared-utils': {
409
source: `
410
export const utilities = {
411
format: (str) => str.toUpperCase(),
412
validate: (data) => typeof data === 'string'
413
};
414
`
415
}
416
},
417
__options__: true
418
});
419
420
// Consumer compartments that reference the shared module
421
const consumerA = new Compartment({
422
modules: {
423
'shared-utils': {
424
namespace: 'shared-utils',
425
compartment: moduleCompartment
426
}
427
},
428
__options__: true
429
});
430
431
const consumerB = new Compartment({
432
modules: {
433
'shared-utils': {
434
namespace: 'shared-utils',
435
compartment: moduleCompartment
436
}
437
},
438
__options__: true
439
});
440
441
// Both compartments can use the shared module
442
const utilsA = consumerA.importNow('shared-utils');
443
const utilsB = consumerB.importNow('shared-utils');
444
445
console.log(utilsA.utilities === utilsB.utilities); // true - same object
446
```
447
448
### Capability-Based Security
449
450
Implementing fine-grained permissions using object capabilities:
451
452
```javascript
453
import 'ses';
454
455
lockdown();
456
457
// Create capability objects
458
const createFileCapability = (allowedPaths) => harden({
459
readFile: (path) => {
460
if (!allowedPaths.includes(path)) {
461
throw new Error(`Access denied: ${path}`);
462
}
463
return `Content of ${path}`;
464
}
465
});
466
467
const createNetworkCapability = (allowedHosts) => harden({
468
fetch: (url) => {
469
const hostname = new URL(url).hostname;
470
if (!allowedHosts.includes(hostname)) {
471
throw new Error(`Network access denied: ${hostname}`);
472
}
473
return Promise.resolve(`Response from ${url}`);
474
}
475
});
476
477
// Create compartment with specific capabilities
478
const sandboxedCompartment = new Compartment({
479
globals: {
480
fileSystem: createFileCapability(['/public/data.txt']),
481
network: createNetworkCapability(['api.example.com']),
482
console: harden(console)
483
},
484
__options__: true
485
});
486
487
// Code in compartment can only access granted capabilities
488
sandboxedCompartment.evaluate(`
489
try {
490
const data = fileSystem.readFile('/public/data.txt'); // Allowed
491
console.log('File access:', data);
492
} catch (error) {
493
console.log('File error:', error.message);
494
}
495
496
try {
497
const data = fileSystem.readFile('/private/secret.txt'); // Denied
498
console.log('Should not reach here');
499
} catch (error) {
500
console.log('Security error:', error.message);
501
}
502
`);
503
```