{"id":12013,"library":"ses","title":"SES: Hardened JavaScript for Secure Execution","description":"SES (Secure EcmaScript) is a JavaScript shim providing a hardened environment for robust security and fearless cooperation. It implements Hardened JavaScript, a subset of JavaScript proposed to ECMA TC39, designed to prevent prototype pollution and other common vulnerabilities. The current stable version, 2.0.0, focuses on plugging side-channel attacks and refining security guarantees. SES operates by 'locking down' the global environment, freezing intrinsic objects, and providing the `Compartment` constructor for creating isolated execution contexts. Each `Compartment` has its own global object and module system but shares hardened, immutable primordials with other compartments. This approach ensures that mutually suspicious code can interact safely via object-capability (ocap) principles, where powers are explicitly granted. The package maintains an active release cadence, with frequent updates across the broader `@endo` ecosystem. Key differentiators include its comprehensive protection against tampering with built-in objects, enforcement of strict mode, and its utility in sandboxing third-party code for applications like blockchain smart contracts and browser extensions, notably used by Agoric and MetaMask.","status":"active","version":"2.0.0","language":"javascript","source_language":"en","source_url":"https://github.com/endojs/endo","tags":["javascript","lockdown","harden","Compartment","assert","security","confinement","isolation","object capabilities","typescript"],"install":[{"cmd":"npm install ses","lang":"bash","label":"npm"},{"cmd":"yarn add ses","lang":"bash","label":"yarn"},{"cmd":"pnpm add ses","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"While CommonJS `require` works, ESM `import` is the recommended pattern. `lockdown()` is typically called once globally.","wrong":"const lockdown = require('ses').lockdown;","symbol":"lockdown","correct":"import { lockdown } from 'ses';"},{"note":"The `Compartment` constructor is unavailable before `lockdown()` is called to ensure a hardened environment.","wrong":"const Compartment = require('ses').Compartment;","symbol":"Compartment","correct":"import { Compartment } from 'ses';"},{"note":"Used to recursively freeze objects and their reachable properties, making them tamper-proof. It's automatically applied to many intrinsics by `lockdown()`.","wrong":"const harden = require('ses').harden;","symbol":"harden","correct":"import { harden } from 'ses';"},{"note":"Importing 'ses' as a side effect calls `lockdown()` in some environments, but it's best practice to explicitly call `lockdown()` after import.","wrong":"require('ses'); // Only imports, does not automatically call lockdown","symbol":"Global Lockdown","correct":"import 'ses'; // Immediately hardens the global environment\nlockdown(); // Explicitly calls the global lockdown function"}],"quickstart":{"code":"import { lockdown, Compartment, harden } from 'ses';\n\n// Step 1: Lock down the global environment to prevent tampering.\n// This should be done as early as possible in your application's lifecycle.\nlockdown();\n\nconsole.log('Global environment locked down.');\n\n// Step 2: Create a new Compartment for secure execution of untrusted code.\n// Compartments are isolated and by default have no ambient authority (e.g., no `fetch`).\nconst untrustedCompartment = new Compartment({\n  globals: {\n    // Grant specific global capabilities to the compartment.\n    log: harden(console.log),\n    greet: harden((name: string) => `Hello, ${name} from compartment!\\n`),\n  },\n});\n\n// Step 3: Evaluate untrusted code within the compartment.\n// The code only has access to its explicit globals and hardened intrinsics.\nconst untrustedCode = `\n  try {\n    log(greet('World'));\n    // Attempting to access unauthorized globals will fail.\n    // console.error('This should not be accessible.');\n    // new Function('return this')().alert('Attempted to access window!');\n    log('Attempting to modify Object.prototype...');\n    Object.prototype.evil = 'muahaha'; // This will fail due to lockdown\n  } catch (e: any) {\n    log('Caught expected error: ' + e.message);\n  }\n  const func = new Function('return 1 + 1;'); // This will throw after lockdown\n`;\n\ntry {\n  untrustedCompartment.evaluate(untrustedCode);\n} catch (e: any) {\n  console.error('Error evaluating untrusted code:', e.message);\n}\n\n// Verify global prototype pollution did not occur in the main realm\nif (Object.prototype.hasOwnProperty('evil')) {\n  console.error('Prototype pollution detected in main realm!');\n} else {\n  console.log('Main realm is still secure.');\n}","lang":"typescript","description":"Demonstrates locking down the global environment and securely executing untrusted TypeScript code within an isolated Compartment, explicitly granting limited capabilities like logging and a custom greeting function. It also shows how attempts at prototype pollution or unauthorized global access are prevented."},"warnings":[{"fix":"Upgrade `ses` to version 2.0.0 or higher. Ensure your application architecture accounts for the strictness introduced by `lockdown()` regarding numeric representations.","message":"Version 2.0.0 includes a major fix for a NaN side-channel vulnerability in JavaScript. This change prevents the leakage of NaN bit encodings via shared TypedArray views of a common ArrayBuffer, which could potentially expose sensitive information. Users should upgrade to prevent this subtle information leak.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Ensure `lockdown()` is the very first significant operation in your main entry point. Avoid calling it multiple times or after third-party libraries have initialized, as they might expect a mutable global environment.","message":"Calling `lockdown()` irreversibly modifies the global JavaScript environment. It freezes all intrinsic objects and functions, preventing any further prototype pollution or global tampering. It must be called once and as early as possible in the application's lifecycle, before any untrusted code or potentially harmful shims are loaded.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Do not rely on global `new Function()` or `eval()` after `lockdown()`. Instead, use `compartment.evaluate(code)` for code evaluation within an explicitly controlled, isolated environment. For dynamic module loading, use `compartment.import()` or `compartment.moduleMapHook`.","message":"After `lockdown()` is invoked, the `Function` constructor and similar evaluators (e.g., `eval`, `setTimeout(string)`) become 'tamed' in the global realm, meaning they will throw an error when attempting to evaluate arbitrary source code. This is a critical security feature to prevent direct arbitrary code execution. Within a `Compartment`, its own `Function` constructor evaluates code only within that compartment's global scope.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Explicitly pass any required ambient authority (e.g., `console.log`, `fetch`) into the `Compartment`'s `globals` or via its `importHook` and `moduleMap`. Use `harden()` on any objects passed into the `globals` to prevent the compartment from tampering with them.","message":"Compartments operate under the Principle of Least Authority (POLA). By default, a `Compartment` receives no ambient authority, meaning it does not automatically have access to host-provided APIs like `fetch`, `localStorage`, `console`, or `process`.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Do not attempt to modify built-in objects or their prototypes after `lockdown()` has been called. If you need to introduce polyfills or shims, they must be applied *before* `lockdown()`.","cause":"`lockdown()` has frozen JavaScript intrinsics and objects to prevent prototype pollution and tampering with the global environment.","error":"TypeError: Cannot assign to read only property 'prototype' of object '#<Object>' (or similar 'Cannot assign to read only property' errors)"},{"fix":"To grant access to specific host APIs, pass them explicitly into the `Compartment`'s `globals` option. For example: `new Compartment({ globals: { fetch: harden(globalThis.fetch) } })`. Ensure any granted capabilities are `harden()`ed.","cause":"Code running inside a `Compartment` does not have access to ambient authority (global browser/Node.js APIs) by default, adhering to POLA.","error":"ReferenceError: fetch is not defined (or 'document is not defined', 'process is not defined')"},{"fix":"Use the `Compartment.prototype.evaluate(code)` method to securely run code within a specific `Compartment`'s scope. For dynamic module loading, explore `Compartment.prototype.import()` or `Compartment`'s `moduleMapHook`.","cause":"`lockdown()` replaces the global `Function` constructor and `eval` with versions that throw errors when used to evaluate source code directly, as a security measure.","error":"Error: Code evaluation is forbidden for `new Function(...)` (or `eval(...)`) after the compartment is locked down."}],"ecosystem":"npm"}