Skip to content

build: harden the npm config with @lavamoat/harden - #140

Merged
botandbang merged 2 commits into
mainfrom
bnb/harden
Nov 10, 2026
Merged

botandbang merged 2 commits into
mainfrom
bnb/harden

Conversation

@botandbang

@botandbang botandbang commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Applies @lavamoat/harden 0.8.1 at its moderate level for npm, plus n_nogit from strict, and makes npm run lint fail if any of it gets removed.

Added

  • .npmrc settings: min-release-age=3 (skip versions published in the last 3 days), allow-git=none, git=false, allow-remote=none and allow-scripts-pin=true.
  • devEngines in package.json requiring npm 11.17 or newer, with onFail: error. Node 24 and 26 bundle npm 11.19.1 and 11.20.0, and CI's test job installs the latest npm, so every CI job meets it. Node 22's npm 10.9.9 now stops with EBADDEVENGINES.
  • allowScripts in package.json with one entry, "fsevents@2.3.3": false. fsevents (macOS only, pulled in by vite through @doc-kit/generator-react) has no install script of its own; npm infers node-gyp rebuild from its binding.gyp. The package already ships a prebuilt fsevents.node that loads without that step, so the entry records the review and silences npm's "install scripts blocked" warning. No other dependency has an install script, so nothing is allowed to run one.
  • @lavamoat/harden 0.8.1 as a pinned dev dependency.
  • tools/harden/decisions.json, the decisions snapshot used to apply this (harden defaults -p npm -l moderate -d tools/harden/decisions.json). It picks allowScripts over disabling scripts, denies existing install scripts instead of approving them, turns on n_nogit, and turns off n_runner.
  • npm run lint:harden, now part of npm run lint. harden check can only check one whole level and has no way to skip an opinion, so the script runs it at strict with --json and pipes the report to tools/harden/check.mjs, which fails on every opinion that isn't applied unless decisions.json turns it off. That leaves n_runner as the only strict opinion it ignores.
  • tools/harden/test/check.test.mjs, a node --test suite for check.mjs: it passes when everything is applied, fails on an unapplied opinion, passes when the only unapplied opinion is one decisions.json declines, and fails on empty input (what it gets if harden crashes). lint:harden runs it before the check, so the CI lint job runs it too.

Changed

  • npm run lint runs lint:harden after docov. The lint job in CI already runs it after a root install.
  • Every actions/setup-node step that installs (ci.yml's lint, types and test jobs, docs.yml and the nightly build) now sets package-manager-cache: false, like release.yml's pack job already did. setup-node v7 reads devEngines.packageManager, turns on npm caching, and then fails every job because the repo has no lockfile.
  • The README's development section says which npm version installs need and what .npmrc blocks, and mentions lint:harden.

Not applied

n_runner replaces npm's script shell with a wrapper that filters environment variables, reorders PATH and adds Node permission flags. harden's README says it doesn't work in Windows cmd.exe, so it's left off to keep npm run working for anyone developing on Windows.

Checks

harden check -p npm -l strict before: 1 of 8 opinions (n_git only). After: 7 of 8, everything except n_runner. -l moderate now passes.

All run locally after deleting every node_modules:

  • Node 24.22.2 / npm 11.19.1: npm install --workspaces --include-workspace-root, npm run lint, npm run types (no diff), npm test and npm run coverage (182 tests each run, 0 failures, 100% coverage in every package), npm run docs, npm run build -w static, and npm pack -w core -w static --dry-run.
  • Node 26.13.0 / npm 11.20.0: the lint job's npm install --workspaces=false then npm run lint, and the types job's install then npm run types.
  • npm 12.2.2 (what the test job's npm install -g npm installs today): npm install -w translate, npm run coverage -w translate and npm run lint:harden.
  • Removing git=false from .npmrc makes lint:harden fail with harden: n_nogit is not applied; removing min-release-age too adds n_age.
  • The 4 check.mjs tests pass and cover it fully (c8, 100% statements and branches through the spawned process). Ignoring decisions.json in check.mjs fails the declined-opinion test, and ignoring satisfied fails 3 of 4.

The release age gate didn't block any dependency, since every install above resolved. Dependabot already waits 7 days before bumping, so its regular PRs clear the 3-day gate. Security updates skip that cooldown, so one for a version under 3 days old would fail to install until it ages; npm's min-release-age-exclude in .npmrc can exempt that one package if it can't wait.

@socket-security

socket-security Bot commented Nov 10, 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
Addednpm/​@​lavamoat/​harden@​0.8.17810010095100

View full report

@botandbang
botandbang merged commit 23a7c0e into main Nov 10, 2026
41 checks passed
@botandbang
botandbang deleted the bnb/harden branch November 10, 2026 19:18
@nodevu-ci nodevu-ci Bot mentioned this pull request Nov 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant