In the modern JavaScript ecosystem, supply chain attacks pose a significant threat. Malicious dependencies can compromise applications, stealing sensitive data or introducing vulnerabilities. LavaMoat, developed by the team behind MetaMask and supported by organizations like ConsenSys and Agoric, offers a robust solution to mitigate these risks. This article delves into LavaMoat's capabilities, providing practical guidance on its implementation to enhance your project's security.
LavaMoat is a suite of open-source tools designed to secure JavaScript projects against supply chain attacks. It protects applications at multiple stages: dependency installation, build time, and runtime. By sandboxing dependencies and enforcing strict access controls, LavaMoat prevents malicious code from accessing sensitive APIs or modifying core JavaScript primitives.
The tool leverages Secure EcmaScript (SES) to create isolated compartments for each package, ensuring that dependencies operate with only the permissions they require. LavaMoat is particularly valuable for frontend and backend JavaScript applications, including those in the blockchain and cryptocurrency spaces, where security is paramount.
LavaMoat operates through a combination of preventive measures:
Installation Protection: Using @lavamoat/allow-scripts
, it disables automatic execution of lifecycle scripts (e.g., postinstall) by default and requires an explicit allowlist in package.json
.
Build and Runtime Sandboxing: Tools like lavamoat-node
and bundler plugins (e.g., for Browserify or Webpack) wrap dependencies in SES compartments. This limits access to powerful APIs like process.env
or network requests.
Policy Enforcement: A generated policy file defines per-package permissions. LavaMoat uses static analysis to create an initial policy, which can be customized via overrides.
Primordial Protection: It prevents prototype pollution on JavaScript intrinsics (e.g., Object, Array) using lockdown mechanisms.
This compartmentalization ensures that even if a dependency is compromised, its impact is contained.
lavamoat-viz
to assess risk in dependency graphs.Outline the key features and their categories:
Show Mermaid Code
mindmap root((LavaMoat Features)) Security Proactive Runtime Security Primordial Protection Compartmentalization Automation Automated Configuration Policy Generation Integration Bundler Plugins Node.js Support Other Dependency Visualization Open-Source
To get started with LavaMoat, install the relevant packages via npm or Yarn. Begin with the installation security tool:
bash:
npm install --save-dev @lavamoat/allow-scripts
Or with Yarn:
bash:
yarn add -D @lavamoat/allow-scripts
For runtime protection in Node.js:
bash:
npm install --save-dev lavamoat
Initialize the setup:
bash:
npm exec allow-scripts setup
This configures your project to disable automatic script execution and prepares the allowlist.
@lavamoat/allow-scripts
safeguards the dependency installation phase by controlling lifecycle scripts.
bash:
npm exec allow-scripts auto
This scans your dependencies and updates package.json
with an initial configuration.
json:
{ "lavamoat": { "allowScripts": { "keccak": true, "core-js": false, "@lavamoat/preinstall-always-fail": true } } }
Here, keccak
is allowed to run scripts, while core-js
is denied. The @lavamoat/preinstall-always-fail
package ensures early failure if protections are bypassed.
bash:
npm exec allow-scripts
Integrate into your workflow by adding a setup script:
json:
{ "scripts": { "setup": "npm install && npm exec allow-scripts && tsc -b" } }
For Yarn Berry (v3+), import the plugin:
bash:
yarn plugin import https://raw.githubusercontent.com/LavaMoat/LavaMoat/main/packages/yarn-plugin-allow-scripts/bundles/@yarnpkg/plugin-allow-scripts.js
For Node.js environments, use lavamoat-node
to enforce policies during execution.
Run your application with LavaMoat:
bash:
lavamoat app.js
bash:
lavamoat app.js --autopolicy
This creates a policy.json
file in ./lavamoat/node/
.
json:
{ "scripts": { "lavamoat-policy": "lavamoat app.js --autopolicy", "start": "lavamoat app.js" } }
Runtime protection process:
Show Mermaid Code
sequenceDiagram participant User participant LavaMoat participant App participant Dependency User->>LavaMoat: Run app with lavamoat-node LavaMoat->>App: Load entry point App->>Dependency: Require dependency LavaMoat->>LavaMoat: Check policy for dependency alt Allowed Access LavaMoat->>Dependency: Grant limited APIs Dependency-->>App: Execute safely else Denied LavaMoat->>App: Throw error or restrict end App-->>User: Secure output
For browser applications, integrate LavaMoat with bundlers like Browserify or Webpack.
Install:
bash:
npm install --save-dev lavamoat-browserify
Usage in build script:
javascript:
const browserify = require('browserify'); const lavamoat = require('lavamoat-browserify'); browserify('./entry.js', { plugin: [lavamoat, { policy: require('./lavamoat/policy.json') }] }).bundle();
For Webpack, use @lavamoat/webpack
:
bash:
npm install --save-dev @lavamoat/webpack
Add to webpack.config.js:
javascript:
const LavaMoatPlugin = require('@lavamoat/webpack'); module.exports = { plugins: [ new LavaMoatPlugin({ generatePolicy: true }) ] };
LavaMoat policies define package permissions. Generated via --autopolicy
, the file structure includes globals, builtins, and packages.
json:
{ "resources": { "keccak": { "globals": { "Buffer": true }, "packages": { "bn.js": true } }, "textarea-caret": { "globals": { "document": "read", "getComputedStyle": true } } } }
Override defaults in policy-override.json
to grant or restrict access. For instance, deny network access for a suspicious package.
Structure of a LavaMoat policy:
Show Mermaid Code
classDiagram Policy --> Resources Resources --> Package : contains multiple Package --> Globals Package --> Builtins Package --> Packages : dependencies class Policy { +resources: Object } class Resources { +[packageName]: Package } class Package { +globals: Object +builtins: Object +packages: Object } class Globals { +[globalName]: boolean or string } class Builtins { +[builtinName]: boolean } class Packages { +[depName]: boolean }
LavaMoat empowers developers to fortify their JavaScript applications against evolving supply chain threats. By implementing its tools for installation, build, and runtime, you can significantly reduce risks without overhauling your codebase. Start with @lavamoat/allow-scripts
for quick wins and expand to full compartmentalization. For more details, visit the LavaMoat GitHub repository.