Without a committed lock file, every npm install — on your machine, in CI, and at deploy time — is a fresh resolution against whatever versions are current on the registry at that moment. A transitive dependency can silently update between your local install and the production deploy, introducing a behavioral change or a newly-published vulnerability without any visible diff. SLSA L2 requires that the build process be reproducible from a pinned source; a missing lock file breaks that invariant. CWE-829 (Inclusion of Functionality from Untrusted Control Sphere) applies because the install is pulling code from a source you haven't reviewed at that exact version. Lock files are the minimum viable proof that what you shipped is what you tested.
High because a missing lock file means your production environment can silently install different code than what was tested, turning any npm publish into a potential silent update to your running application.
Generate and commit the lock file immediately. Remove it from .gitignore if present — there is no valid reason to exclude a lock file from an application repository.
npm install
git add package-lock.json
git commit -m "chore: add package lock file"
For monorepos or workspaces, commit the root lock file. For pnpm or yarn, commit pnpm-lock.yaml or yarn.lock respectively. CI should run npm ci (not npm install) to enforce the lock file on every build.
ID: dependency-supply-chain.security-vulns.lockfile-committed
Severity: high
What to look for: Check for the presence of a lock file (package-lock.json, pnpm-lock.yaml, yarn.lock, or bun.lockb) in the project root. Verify the lock file is not listed in .gitignore. To check currency: compare the dependencies and devDependencies entries in package.json against the resolved packages in the lock file. If package.json lists a package that has no entry in the lock file at all, the lock file is stale. Before evaluating, extract and quote the first 5 lines of the lockfile header to confirm it is a valid, non-empty lockfile for the correct package manager. Count all instances found and enumerate each.
Detector snippet (shell-capable tools only): If the tool has shell access, check whether any supported lockfile exists in the project root. Exit 0 means a lockfile is present — proceed to prose to verify it's not gitignored and is current. Exit 1 means NO lockfile — fail. Exit >=2 or command not found — fall back to prose reasoning.
test -f package-lock.json -o -f pnpm-lock.yaml -o -f yarn.lock -o -f bun.lockb
Pass criteria: A lock file exists, is not in .gitignore, and all packages in package.json have corresponding resolved entries in the lock file. At least 1 implementation must be confirmed.
Fail criteria: No lock file exists, or the lock file is in .gitignore, or one or more packages in package.json are absent from the lock file (indicating npm install has not been run after editing package.json).
Skip (N/A) when: The project has no package.json (no npm ecosystem detected).
Cross-reference: The no-wildcard-versions check verifies that the lockfile's pinned versions are not undermined by loose ranges in the manifest.
Detail on fail: "No package-lock.json, pnpm-lock.yaml, or yarn.lock found in project root" or "yarn.lock is listed in .gitignore — lock file not committed" or "package.json has packages not present in lock file — run npm install to sync"
Remediation: A committed lock file guarantees that every install — on your machine, CI, and production — uses exactly the same dependency versions. Without it, a new version of a transitive dependency could silently install on next deploy, introducing breaking changes or security vulnerabilities.
To generate and commit the lock file:
npm install # generates package-lock.json
git add package-lock.json
git commit -m "chore: add package lock file"
Remove it from .gitignore if present — there is almost never a good reason to gitignore a lock file in an application project (libraries are the exception).