Run npm ci --ignore-scripts in CI with an explicit allowlist #46

Closed
opened 2026-06-14 19:25:19 +00:00 by james · 0 comments
Owner

A common npm supply-chain payload is a malicious postinstall script that runs at npm ci time and exfiltrates secrets, scans the filesystem, or implants further malware. Disabling install scripts in CI shrinks the blast radius of a compromised dependency to packages we explicitly trust to run code at install time — typically a small handful of native-build deps.

Scope

  • Add --ignore-scripts to every npm ci invocation in .forgejo/workflows/pr.yml and any other workflow that installs deps (release workflow once #16 lands).
  • Identify the packages that legitimately need install scripts in this codebase. Native-build candidates: better-sqlite3, argon2, esbuild, possibly Sharp once we touch image handling. Confirm the list against the current package.json rather than guessing.
  • Permit only that allowlist via the appropriate mechanism — current options are:
    • npm's built-in --foreground-scripts plus a small wrapper that runs npm rebuild <allowlisted-pkg> after the no-scripts install,
    • or the @lavamoat/allow-scripts workflow (separate config file, allow-scripts runs the whitelisted ones).
      Pick whichever fits cleanly; document the choice.
  • Document the policy and the allowlist so adding a new native-build dep is a deliberate review step, not an invisible "it just worked locally".

Acceptance criteria

  • CI installs run with scripts disabled by default — confirm by grep over the workflows.
  • An explicit allowlist of packages permitted to run install-time scripts lives in the repo (.npmrc / package.json config / a config file for the chosen tool).
  • npm test (the existing dual-engine matrix) still passes, confirming better-sqlite3 / argon2 / similar native deps still build.
  • Policy documented next to the CI conventions (sibling of #44 / #45).

Part of epic #2.

A common npm supply-chain payload is a malicious `postinstall` script that runs at `npm ci` time and exfiltrates secrets, scans the filesystem, or implants further malware. Disabling install scripts in CI shrinks the blast radius of a compromised dependency to packages we *explicitly* trust to run code at install time — typically a small handful of native-build deps. ## Scope - Add `--ignore-scripts` to every `npm ci` invocation in `.forgejo/workflows/pr.yml` and any other workflow that installs deps (release workflow once #16 lands). - Identify the packages that legitimately need install scripts in this codebase. Native-build candidates: `better-sqlite3`, `argon2`, `esbuild`, possibly Sharp once we touch image handling. Confirm the list against the current `package.json` rather than guessing. - Permit *only* that allowlist via the appropriate mechanism — current options are: - `npm`'s built-in `--foreground-scripts` plus a small wrapper that runs `npm rebuild <allowlisted-pkg>` after the no-scripts install, - or the `@lavamoat/allow-scripts` workflow (separate config file, `allow-scripts` runs the whitelisted ones). Pick whichever fits cleanly; document the choice. - Document the policy and the allowlist so adding a new native-build dep is a deliberate review step, not an invisible "it just worked locally". ## Acceptance criteria - [ ] CI installs run with scripts disabled by default — confirm by `grep` over the workflows. - [ ] An explicit allowlist of packages permitted to run install-time scripts lives in the repo (`.npmrc` / `package.json` config / a config file for the chosen tool). - [ ] `npm test` (the existing dual-engine matrix) still passes, confirming `better-sqlite3` / `argon2` / similar native deps still build. - [ ] Policy documented next to the CI conventions (sibling of #44 / #45). Part of epic #2.
james closed this issue 2026-06-15 13:14:16 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
james/carol#46
No description provided.