npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@lavamoat/webpack

v0.3.0-beta.0

Published

LavaMoat Webpack plugin for running dependencies in Compartments without eval

Downloads

23

Readme

LavaMoat Webpack Plugin

Putting lava in your pack. For security. We need to work on our metaphors.

LavaMoat Webpack Plugin wraps each module in the bundle in a Compartment and enforces LavaMoat Policies independently per package.

Usage

Policy generation is now built into the plugin. While it might get confused about aliases and custom resolvers, it should generally work even when they're in use.

  1. Create a webpack bundle with the LavaMoat plugin enabled and the generatePolicy flag set to true
  2. Make sure you add a <script src="./lockdown"></script> before all other scripts or enable the HtmlWebpackPluginInterop option if you're using html-webpack-plugin. (Note there's no .js there because it's the only way to prevent webpack from minifying the file thus undermining its security guarantees)
  3. Tweak the policy if needed with policy-override.json

The plugin is emitting lockdown without the .js extension because that's the only way to prevent it from getting minified, which is likely to break it.

The LavaMoat plugin takes an options object with the following properties:

  • policy: the LavaMoat policy object. (unstable. This will surely change before v1 or a policy loader export will be provided from the main package to incorporate policy-override files)
  • runChecks: Optional boolean property to indicate whether to check resulting code with wrapping for correctness. Default is false.
  • diagnosticsVerbosity: Optional number property to represent diagnostics output verbosity. A larger number means more overwhelming diagnostics output. Default is 0.
    Setting positive verbosity will enable runChecks.
  • readableResourceIds: Decide whether to keep resource IDs human readable (regardless of production/development mode). If false, they are replaced with a sequence of numbers. Keeping them readable may be useful for debugging when a policy violation error is thrown.
  • lockdown: set configuration for SES lockdown. Setting the option replaces defaults from LavaMoat.
  • HtmlWebpackPluginInterop: add a script tag to the html output for ./lockdown file if HtmlWebpackPlugin is in use
const LavaMoatPlugin = require('@lavamoat/webpack')

module.exports = {
  // ... other webpack configuration properties
  plugins: [
    new LavaMoatPlugin({
      // policy generated by lavamoat
      policy: require('./lavamoat/policy.json'),
      // runChecks: true, // enables checking each wrapped module source if it's still proper JavaScript (in case mismatching braces somehow survived Webpack loaders processing)
      // readableResourceIds: true, // explicitly decide if resourceIds from policy should be readable in the bundle or turned into numbers. You might want to bundle in production mode but keep the ids for debugging
      //   diagnosticsVerbosity: 2, // level of output verbosity from the plugin
      // SES lockdown options to use at runtime
      // lockdown: {
      //   errorTaming: "unsafe",
      //   consoleTaming: "unsafe",
      //   overrideTaming: "severe"
      // },
      // HtmlWebpackPluginInterop: false, // set it to true if you want a script tag for ./lockdown file to automatically be added to your HTML template
    }),
  ],
  // ... other webpack configuration properties
}

One important thing to note when using the LavaMoat plugin is that it disables the concatenateModules optimization in webpack. This is because concatenation won't work with wrapped modules.

Excluding modules

[!WARNING] This is an experimental feature and excluding may be configured differently in the future if this approach is proven insecure.

The default way to define specific behaviors for webpack is creating module rules. To ensure exclude rules are applied on the same exact files that match certain rules (the same RegExp may be matched against different things at different times) we're providing the exclude functionality as a loader you can add to the list of existing loaders or use individually.
The loader is available as LavaMoatPlugin.exclude from the default export of the plugin. It doesn't do anything to the code, but its presence is detected and treated as a mark on the file. Any file that's been processed by LavaMoatPlugin.exclude will not be wrapped in a Compartment.

[!NOTE] Exclude loader will only work when used in webpack config. Specifying it inline require('path/to/excludeLoader.js!./module.js') will not result in module.js being excluded. (This is a security feature to prevent your dependencies from declaring they want to be excluded.)

Example: avoid wrapping CSS modules:

  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          LavaMoatPlugin.exclude,
        ],
        sideEffects: true,
      },
    ],
  },

See: examples/webpack.config.js for a complete example.

Gotchas

Implicit modules

  • Webpack may include dependencies polyfilling Node.js built-ins, such as the events or buffer packages. In other cases, it will ignore the built-ins and provide empty modules in their place (see below).

When a dependency (eg. buffer) is provided by Webpack, and you need to add it explicitly to your dependencies, you'll receive the following error:

Error: LavaMoat - Encountered unknown package directory for file "/home/(...)/node_modules/buffer/index.js"

Webpack-ignored modules

When a built-in Node.js module is ignored, Webpack generates something like this:

const nodeCrypto = __webpack_require__(/*! crypto */ '?0b7d')

A carveout is necessary in policy enforcement for these modules. Sadly, even treeshaking doesn't eliminate that module. It's left there and failing to work when reached by runtime control flow.

This plugin will skip policy enforcement for such ignored modules.

Security Claims

This is a beta release and does not provide any guarantees; even those listed below. Use at your own risk!

  • SES must be added to the page without any bundling or transforming for any security guarantees to be sustained.
    • The plugin is attempting to add it as an asset to the compilation for the sake of Developer Experience. Feedback welcome.
  • Each javascript module resulting from the webpack build is scoped to its package's policy

Threat Model

  • Webpack itself is considered trusted.
  • All plugins can bypass LavaMoat protections intentionally.
  • It's unlikely but possible that a plugin can bypass LavaMoat protections unintentionally.
  • It should not be possible for loaders to bypass LavaMoat protections.
  • Some plugins (eg. MiniCssExtractPlugin) execute code from the bundle at build time. To make the plugin work you need to trust it and the modules it runs and add the LavaMoat.exclude loader for them.
  • This Webpack plugin does not protect against malicious execution by other third-party plugins at runtime (use LavaMoat for that).

Webpack runtime

Elements of the Webpack runtime (e.g., __webpack_require__.*) are currently mostly left intact. To avoid opening up potential bypasses, some functionality of the Webpack runtime is not available.

Testing

Run npm test to start the automated tests.

Manual testing

  • Navigate to example/
  • Run npm ci and npm test
  • Open dist/index.html in your browser and inspect the console