Hardened JavaScript for fearless cooperation through secure execution contexts and object-capability security
—
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.
Creates isolated execution contexts with configurable globals, module systems, and security policies.
/**
* Creates a new compartment with isolated global scope
* @param options - Configuration options for the compartment
*/
class Compartment {
constructor(options?: CompartmentOptions & { __options__: true });
// Legacy constructor (deprecated)
constructor(
globals?: Record<PropertyKey, any> | undefined,
modules?: Record<string, ModuleDescriptor>,
options?: CompartmentOptions
);
}Usage Examples:
import 'ses';
lockdown();
// Basic compartment with minimal capabilities
const basicCompartment = new Compartment({
__options__: true
});
// Compartment with specific globals
const endowedCompartment = new Compartment({
globals: {
console: harden(console),
fetch: harden(fetch), // Provide specific capabilities
myAPI: harden({
getValue: () => "secure data"
})
},
__options__: true
});
// Named compartment for debugging
const namedCompartment = new Compartment({
name: "user-script-sandbox",
globals: { console: harden(console) },
__options__: true
});Evaluates JavaScript code within the compartment's isolated context.
/**
* Evaluates JavaScript code in the compartment's context
* @param code - JavaScript code string to evaluate
* @param options - Evaluation configuration options
* @returns Result of code evaluation
*/
evaluate(code: string, options?: EvaluateOptions): any;Usage Examples:
import 'ses';
lockdown();
const compartment = new Compartment({
globals: { console: harden(console) },
__options__: true
});
// Basic evaluation
const result = compartment.evaluate(`
const data = { message: "Hello from compartment!" };
console.log("Compartment executing code");
data.message;
`);
console.log(result); // "Hello from compartment!"
// Evaluation with options
const sloppyResult = compartment.evaluate(`
// This would normally fail in strict mode
undeclaredVar = "sloppy mode";
undeclaredVar;
`, {
sloppyGlobalsMode: true
});
// Code transformation during evaluation
const transformedResult = compartment.evaluate(`
console.log("Original message");
`, {
transforms: [
(source) => source.replace(/Original/, 'Transformed')
]
});Dynamically imports modules with promise-based loading.
/**
* Dynamically imports a module asynchronously
* @param specifier - Module specifier to import
* @returns Promise resolving to module namespace object
*/
import(specifier: string | null): Promise<{ namespace: ModuleExportsNamespace }>;Usage Examples:
import 'ses';
lockdown();
const compartment = new Compartment({
importHook: async (specifier) => {
if (specifier === 'my-module') {
return {
source: `
export const greeting = "Hello";
export function sayHello(name) {
return greeting + ", " + name + "!";
}
`
};
}
throw new Error(`Module not found: ${specifier}`);
},
__options__: true
});
// Import module asynchronously
compartment.import('my-module').then(({ namespace }) => {
console.log(namespace.sayHello('World')); // "Hello, World!"
});Synchronously imports modules for use cases where async loading is not suitable.
/**
* Synchronously imports a module that's already loaded or can be loaded synchronously
* @param specifier - Module specifier to import
* @returns Module namespace object
*/
importNow(specifier: string): ModuleExportsNamespace;Usage Examples:
import 'ses';
lockdown();
const compartment = new Compartment({
modules: {
'math-utils': {
source: `
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;
`
}
},
importNowHook: (specifier) => {
if (specifier === 'runtime-module') {
return {
source: `export const runtime = true;`
};
}
},
__options__: true
});
// Import pre-loaded module
const mathUtils = compartment.importNow('math-utils');
console.log(mathUtils.add(2, 3)); // 5
// Import via importNowHook
const runtimeMod = compartment.importNow('runtime-module');
console.log(runtimeMod.runtime); // truePreloads modules without importing them into the current scope.
/**
* Preloads a module without importing it
* @param specifier - Module specifier to load
* @returns Promise that resolves when module is loaded
*/
load(specifier: string): Promise<void>;Accesses already loaded module namespaces.
/**
* Gets the namespace of an already loaded module
* @param specifier - Module specifier
* @returns Module namespace object
*/
module(specifier: string): ModuleExportsNamespace;Access to compartment metadata and global object.
/**
* The compartment's global object
*/
get globalThis(): Record<PropertyKey, any>;
/**
* The compartment's name for debugging
*/
get name(): string;Usage Examples:
import 'ses';
lockdown();
const compartment = new Compartment({
name: 'test-compartment',
globals: { customGlobal: 'test value' },
__options__: true
});
// Access compartment properties
console.log(compartment.name); // 'test-compartment'
console.log(compartment.globalThis.customGlobal); // 'test value'
// Each compartment has its own global object
const anotherCompartment = new Compartment({ __options__: true });
console.log(compartment.globalThis !== anotherCompartment.globalThis); // true
// But they share the same intrinsics
console.log(compartment.globalThis.Array === anotherCompartment.globalThis.Array); // trueComprehensive configuration for compartment behavior, module loading, and security policies.
interface CompartmentOptions {
/** Compartment name for debugging purposes */
name?: string;
/** Initial global variables for the compartment */
globals?: Map<string, any>;
/** Static module map for pre-loaded modules */
modules?: Map<string, ModuleDescriptor>;
/** Source transformation functions applied to evaluated code */
transforms?: Array<Transform>;
/** Internal shim-specific transforms */
__shimTransforms__?: Array<Transform>;
/** Module resolution hook for converting import specifiers */
resolveHook?: ResolveHook;
/** Async module loading hook */
importHook?: ImportHook;
/** Sync module loading hook */
importNowHook?: ImportNowHook;
/** Dynamic module mapping hook */
moduleMapHook?: ModuleMapHook;
/** Hook for customizing import.meta objects */
importMetaHook?: ImportMetaHook;
/** Use native compartment implementation if available */
__native__?: boolean;
/** Control namespace boxing behavior */
__noNamespaceBox__?: boolean;
/** Fail fast on first module loading error */
noAggregateLoadErrors?: boolean;
}Options for controlling code evaluation behavior.
interface EvaluateOptions {
/** Source transformation functions for this evaluation */
transforms?: Array<Transform>;
/** Allow sloppy mode for legacy code compatibility */
sloppyGlobalsMode?: boolean;
/** Internal module shim lexical bindings */
__moduleShimLexicals__?: Record<string, any>;
/** Evade HTML comment test for browser compatibility */
__evadeHtmlCommentTest__?: boolean;
/** Reject some direct eval expressions for security */
__rejectSomeDirectEvalExpressions__?: boolean;
}Secure communication between compartments using hardened objects:
import 'ses';
lockdown();
// Create a shared communication channel
const createChannel = () => {
const listeners = [];
return harden({
send: (message) => {
listeners.forEach(listener => listener(message));
},
listen: (callback) => {
listeners.push(callback);
}
});
};
const channel = createChannel();
// Compartment A - sender
const senderCompartment = new Compartment({
globals: { channel },
__options__: true
});
// Compartment B - receiver
const receiverCompartment = new Compartment({
globals: {
channel,
console: harden(console)
},
__options__: true
});
// Set up receiver
receiverCompartment.evaluate(`
channel.listen((message) => {
console.log('Received:', message);
});
`);
// Send message from sender
senderCompartment.evaluate(`
channel.send({ type: 'greeting', data: 'Hello from sender!' });
`);Sharing modules between compartments:
import 'ses';
lockdown();
// Shared module compartment
const moduleCompartment = new Compartment({
modules: {
'shared-utils': {
source: `
export const utilities = {
format: (str) => str.toUpperCase(),
validate: (data) => typeof data === 'string'
};
`
}
},
__options__: true
});
// Consumer compartments that reference the shared module
const consumerA = new Compartment({
modules: {
'shared-utils': {
namespace: 'shared-utils',
compartment: moduleCompartment
}
},
__options__: true
});
const consumerB = new Compartment({
modules: {
'shared-utils': {
namespace: 'shared-utils',
compartment: moduleCompartment
}
},
__options__: true
});
// Both compartments can use the shared module
const utilsA = consumerA.importNow('shared-utils');
const utilsB = consumerB.importNow('shared-utils');
console.log(utilsA.utilities === utilsB.utilities); // true - same objectImplementing fine-grained permissions using object capabilities:
import 'ses';
lockdown();
// Create capability objects
const createFileCapability = (allowedPaths) => harden({
readFile: (path) => {
if (!allowedPaths.includes(path)) {
throw new Error(`Access denied: ${path}`);
}
return `Content of ${path}`;
}
});
const createNetworkCapability = (allowedHosts) => harden({
fetch: (url) => {
const hostname = new URL(url).hostname;
if (!allowedHosts.includes(hostname)) {
throw new Error(`Network access denied: ${hostname}`);
}
return Promise.resolve(`Response from ${url}`);
}
});
// Create compartment with specific capabilities
const sandboxedCompartment = new Compartment({
globals: {
fileSystem: createFileCapability(['/public/data.txt']),
network: createNetworkCapability(['api.example.com']),
console: harden(console)
},
__options__: true
});
// Code in compartment can only access granted capabilities
sandboxedCompartment.evaluate(`
try {
const data = fileSystem.readFile('/public/data.txt'); // Allowed
console.log('File access:', data);
} catch (error) {
console.log('File error:', error.message);
}
try {
const data = fileSystem.readFile('/private/secret.txt'); // Denied
console.log('Should not reach here');
} catch (error) {
console.log('Security error:', error.message);
}
`);Install with Tessl CLI
npx tessl i tessl/npm-ses