Chapter 1 of 9 · 6 checks · 4 min
Dependencies and lockfiles
Checks that every build installs exactly the dependency tree you reviewed, and that installing it cannot quietly run code you never approved.
Every project commits a lockfile that pins the full dependency treeMust
Why it matters and how to check
A manifest with ranges such as
^4.2.0lets each install pick whatever version is newest that day, so two builds of the same commit can contain different code. A lockfile (package-lock.json,pnpm-lock.yaml,yarn.lock,poetry.lock,uv.lock,go.sum,Cargo.lock) records exact versions and integrity hashes for direct and transitive dependencies. OpenSSF Scorecard's Pinned-Dependencies check looks for exactly this.To verify: list every directory with a manifest and confirm a lockfile sits next to it in version control. Check
.gitignoredoes not exclude lockfiles, and that lockfile changes show up in pull request diffs rather than being regenerated silently during builds.CI installs from the lockfile and fails if it does not matchMust
Why it matters and how to check
A lockfile only helps if CI refuses to change it. The npm docs describe
npm cias a clean install that exits with an error when the lockfile andpackage.jsondisagree, instead of updating the lockfile. Other tools have the same mode under a different flag.To verify: read the install step in every workflow. It should use the frozen form for its package manager. Then edit
package.jsonon a branch without updating the lockfile and confirm the pipeline fails.Example
Shell npm ci # npm pnpm install --frozen-lockfile # pnpm yarn install --immutable # Yarn Berry uv sync --locked # uv pip install --require-hashes -r requirements.txt cargo build --locked # Rust go mod verify # Go: checks the module cache against go.sumPython installs use hash-checking mode or a tool lockfile with hashesShould
Why it matters and how to check
pip's secure installs guide describes hash-checking mode: with
--require-hashes, every requirement, including transitive ones, must be pinned with==and carry a--hash, and pip refuses anything that does not match. The same guide suggests--only-binary :all:for a stricter setup, because building a source distribution can run arbitrary code.To verify: open the requirements file used for production. Every line should have a pinned version and at least one
sha256hash, usually generated by a tool such aspip-compile --generate-hashesor a lockfile from uv or Poetry. Run the production install command and confirm it includes--require-hashesor the locked mode of your tool.Dependency install scripts run only for packages you approvedMust
Why it matters and how to check
Lifecycle scripts such as
preinstallandpostinstallrun with the permissions of whoever installs the package, including a CI job holding secrets. They have been the delivery route for many malicious npm releases. GitHub's changelog for npm 12 saysnpm installno longer runspreinstall,installorpostinstallscripts from dependencies unless they are explicitly allowed, and that Git and remote URL dependencies are also off by default.To verify: check the npm version in CI with
npm --version. On npm 12, read theallowScriptsentries inpackage.jsonand confirm each approved package really needs a build step. On older npm, or other package managers, check whether CI sets--ignore-scriptsor the equivalent and runs approved builds explicitly. Nobody should setdangerously-allow-all-scriptsexcept as a short migration step.Example
Shell # npm 12: see which dependencies want to run scripts npm approve-scripts --allow-scripts-pending # approve the ones that need a native build, block the rest npm approve-scripts esbuild sharp npm deny-scripts some-telemetry-packageEach new direct dependency is checked before it is addedShould
Why it matters and how to check
The OpenSSF Concise Guide says to evaluate software before selecting it as a direct dependency and to double-check its name to counter typosquatting. A lookalike name (
reqeusts,colours-js) or a package published last week by an unknown account is a common way in.To verify: pick the last five dependencies added to the repository and check that someone confirmed the exact name, the source repository, the maintainers, recent activity and whether the code is really needed. Make this a pull request template question for any change that touches a manifest.
Do
- Compare the package name character by character with the project homepage
- Check the OpenSSF Scorecard result for the package repository when there is one
Avoid
- Adding a dependency for a few lines of code you could write yourself
Production dependencies come from the registry, not Git branches or tarball URLsShould
Why it matters and how to check
A dependency pointing at a Git branch or a remote tarball has no registry integrity record and can change under you. The npm 12 changelog turns Git dependencies (
--allow-git) and remote URL dependencies (--allow-remote) off by default for the same reason.To verify: search the manifests and lockfiles for
git+,github:,http://andhttps://in dependency versions. Each hit should be replaced with a published version, or pinned to a full commit hash with a comment explaining why it cannot come from the registry.