Skip to content

chore: integrate LavaMoat/harden - #9937

Draft
Mrtenz wants to merge 4 commits into
mainfrom
mrtenz/harden
Draft

Mrtenz wants to merge 4 commits into
mainfrom
mrtenz/harden

Conversation

@Mrtenz

@Mrtenz Mrtenz commented Aug 24, 2026 •

Copy link
Copy Markdown
Member

Explanation

Scripts run through yarn run currently execute with the full ambient authority of the developer's shell: unrestricted filesystem access, network access, the ability to spawn child processes, and access to every environment variable including credentials. A compromised dependency that gets to run any lifecycle or package script therefore has a straightforward path to exfiltrating secrets or tampering with files outside the repository.

This PR integrates the LavaMoat/harden setup, ported from MetaMask/metamask-module-template.

It adds a lavamoat/ directory containing:

  • Script permission configs with secure defaults — scripts.strict.json (deny all powerful IO capabilities), scripts.loose.json (a more permissive variant for scripts that genuinely need native capabilities), and scripts.skills.json (used by skills:postinstall).
  • .env.ban.json — a list of keywords used to filter sensitive environment variables out of the script environment.
  • .runner-plugin.js — a Yarn 4 plugin that hooks wrapScriptExecution and applies the above. For every script it resolves the matching config from the workspace's scriptsConfig field, translates it into Node's permission model flags via NODE_OPTIONS, strips banned environment variables, and moves node_modules/.bin to the end of PATH to eliminate bin confusion attacks.
  • plugin-allow-scripts.js — the LavaMoat allow-scripts plugin, now vendored locally rather than fetched from a remote URL at install time.

Every workspace declares scriptsConfig.#default pointing at the strict config, and a new Yarn constraint in yarn.config.cjs enforces that this stays in place for newly added packages.

Some changes whose purpose may not be obvious:

  • .nvmrc is pinned to Node 26 — the permission model flags used here require a recent runtime.
  • .npmrc and .yarnrc are added — these make accidental use of npm or Yarn 1 safe (ignore-scripts, offline), since neither honours the Yarn 4 plugin.
  • enableHardenedMode: true in .yarnrc.yml validates lockfile content against the remote registries.
  • enableMirror: false and globalFolder: .yarn/global — despite enableGlobalCache being disabled, Yarn still writes to the global folder, so it is redirected into the project to stay within the permitted write scope.
  • lavamoat/ is excluded from ESLint and oxfmt — the plugin sources are vendored from upstream and are intentionally kept unmodified so they remain easy to diff against the template.

References

Based on MetaMask/metamask-module-template#335

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Add the `LavaMoat/harden` setup, based on
MetaMask/metamask-module-template#335.

This introduces a `lavamoat/` directory with config files containing
secure defaults for running scripts (`scripts.strict.json`,
`scripts.loose.json`, and `scripts.skills.json`), plus a banned
environment variable list, and a Yarn runner plugin that applies them.
The plugin wraps every `yarn run` script with Node's permission model,
restricting filesystem, network, child process, worker, addon, WASI,
and inspector access, filters sensitive environment variables out of
the script environment, and moves `node_modules/.bin` to the end of
`PATH` to prevent bin confusion attacks.

Each workspace now declares `scriptsConfig.#default` pointing at the
strict config, enforced by a new Yarn constraint. Yarn hardened mode is
enabled, `.npmrc` and `.yarnrc` are added to make accidental npm or
Yarn 1 usage safe, and Node is pinned to 26 since the permission model
requires a recent runtime.
@socket-security

socket-security Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​lavamoat/​allow-scripts@​5.1.010010010094100

View full report

@Mrtenz Mrtenz mentioned this pull request Sep 29, 2026
4 tasks
pull Bot pushed a commit to Reality2byte/core that referenced this pull request Sep 29, 2026
## Explanation

Modern versions of Node.js support running TypeScript files by default,
so we can drop the `tsx` dependency.

This is in preparation for MetaMask#9937, as `tsx` requests too many permissions
by default.

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## Checklist

- [ ] I've updated the test suite for new or updated code as appropriate
- [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [ ] I've communicated my changes to consumers by [updating changelogs
for packages I've
changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md)
- [ ] I've introduced [breaking
changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md)
in this PR and have prepared draft pull requests for clients and
consumer packages to resolve them

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Wide CI and developer-tooling surface area depends on experimental
Node TypeScript flags and the custom resolver; regressions would break
lint, type generation, and workflows rather than shipped wallet logic.
> 
> **Overview**
> Replaces **`tsx`** across the monorepo with **Node.js** running
TypeScript directly, using `--experimental-transform-types` and a new
**`scripts/resolver/register.ts`** hook so existing `.js` import paths
and some package resolutions (e.g. `@metamask/utils`) still work at
runtime.
> 
> Root and workspace **`package.json`** scripts for lint, messenger
action types, changelog/codeowners helpers, and similar tooling now
invoke `node` (with the resolver where needed). **`tsx`** is removed
from devDependencies repo-wide, LavaMoat drops **`tsx>esbuild`**, and
GitHub Actions call yarn scripts like **`get-changed-workspaces`** and
**`close-stale-release-prs`** instead of `yarn tsx scripts/...`.
> 
> **`messenger-cli`** and **`platform-api-docs`** tests were updated to
spawn the CLI the same way CI does; **`bridge-controller`** /
**`bridge-status-controller`** align on **`messenger-cli`** scripts
(replacing the old generate-method-action-types entry), with a
regenerated ordering tweak in bridge-status method action types.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
20ac37f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant