Why
Using npm install in CI allows npm to install dependency versions that differ from package-lock.json, meaning a package that changes between the lock file commit and the CI run can silently install a different (potentially compromised) version without any build failure.
Current state
.github/workflows/ci.yml lines 11 and 25 both use:
npm install updates package-lock.json if it finds discrepancies and installs whatever satisfies the semver range — it does not enforce exact lockfile versions. Any dependency drift from the committed lock file goes undetected.
Ideal state
- Both CI jobs use
npm ci instead of npm install.
npm ci performs a clean install that exactly matches package-lock.json, fails if the lock file is missing or out of sync, and does not modify any files.
- Dependency versions installed in CI are guaranteed to match the committed lock file.
Out of scope
- Pinning transitive dependencies via
overrides (tracked separately in the npm audit issue).
- Migrating to a different package manager.
Starting points
.github/workflows/ci.yml lines 11 and 25 — the two npm install commands to replace with npm ci
QA plan
- Replace both
npm install calls with npm ci in the workflow file.
- Push to a branch and observe the CI run — expect both jobs succeed.
- Temporarily introduce a version mismatch between
package.json and package-lock.json and push — expect npm ci fails with a clear lockfile-mismatch error.
Done when
Both CI jobs use npm ci and a mismatched package-lock.json causes the CI run to fail rather than silently installing different versions.
Why
Using
npm installin CI allows npm to install dependency versions that differ frompackage-lock.json, meaning a package that changes between the lock file commit and the CI run can silently install a different (potentially compromised) version without any build failure.Current state
.github/workflows/ci.ymllines 11 and 25 both use:npm installupdatespackage-lock.jsonif it finds discrepancies and installs whatever satisfies the semver range — it does not enforce exact lockfile versions. Any dependency drift from the committed lock file goes undetected.Ideal state
npm ciinstead ofnpm install.npm ciperforms a clean install that exactly matchespackage-lock.json, fails if the lock file is missing or out of sync, and does not modify any files.Out of scope
overrides(tracked separately in the npm audit issue).Starting points
.github/workflows/ci.ymllines 11 and 25 — the twonpm installcommands to replace withnpm ciQA plan
npm installcalls withnpm ciin the workflow file.package.jsonandpackage-lock.jsonand push — expectnpm cifails with a clear lockfile-mismatch error.Done when
Both CI jobs use
npm ciand a mismatchedpackage-lock.jsoncauses the CI run to fail rather than silently installing different versions.