Skip to main content
Category: Software Supply Chain

Dependency Pinning

Also known as: version pinning, pinning dependencies
Simply put

Dependency pinning is the practice of specifying the exact version of each software library or package your application depends on, rather than allowing a range of versions. This helps ensure that builds are reproducible and that unexpected or potentially malicious updates to a dependency are not automatically pulled into your project. It is a foundational practice for managing software supply chain risk.

Formal definition

Dependency pinning refers to explicitly declaring the precise version (or, in stricter implementations, the specific release number, git tag, or content hash) of each direct and transitive dependency in a project's dependency manifest or lock file. Rather than using version ranges or floating constraints (e.g., semver ranges like ^1.2.3 or ~1.2.0), pinning locks each dependency to a known-good version. This practice reduces the risk of supply chain attacks that exploit automatic version resolution, such as dependency confusion or compromised upstream releases. Pinning exists on a spectrum of strictness: at the least strict end, a dependency may be pinned to a branch or release series; at the stricter end, it is pinned to an exact release number, git commit, or cryptographic digest. While pinning improves build reproducibility and limits exposure to unexpected changes, it does not by itself verify the integrity or provenance of the pinned artifact, and it introduces a maintenance burden, as pinned versions must be actively monitored and updated to incorporate security patches.

Why it matters

Dependency pinning is a foundational defense against a class of software supply chain attacks that exploit automatic version resolution. When projects use floating version ranges (such as semver ranges like ^1.2.3 or ~1.2.0), build systems may silently pull in newer versions of dependencies without any deliberate action by the development team. If an upstream package is compromised or a malicious version is published (as in dependency confusion attacks), projects using unpinned dependencies can automatically incorporate that malicious code into their builds. By pinning to exact versions, teams create a deliberate gate that prevents unexpected updates from flowing into their software without review.

Beyond security, dependency pinning is essential for build reproducibility. Without pinning, two builds of the same codebase run at different times may resolve to different dependency versions, leading to inconsistent behavior, difficult-to-diagnose bugs, and unreliable testing. Reproducible builds are a prerequisite for meaningful security auditing, since teams need confidence that the artifacts they audit match what is actually deployed.

However, pinning is not without trade-offs. Pinned versions must be actively monitored and updated to incorporate security patches; otherwise, teams risk running known-vulnerable versions indefinitely. Pinning also does not, by itself, verify the integrity or provenance of the pinned artifact. It is best understood as one layer in a broader supply chain security strategy, complemented by practices such as hash verification, provenance attestation, and automated dependency update tooling.

Who it's relevant to

Application Developers
Developers are directly responsible for managing dependency manifests and lock files. Adopting dependency pinning helps ensure that the libraries included in a project are deliberate choices, reducing the risk of unexpected behavior or security issues introduced by automatic version updates.
DevOps and Build Engineers
Build engineers rely on reproducible builds to maintain confidence that tested artifacts match deployed artifacts. Dependency pinning is a key practice for achieving this reproducibility, and build pipelines should be configured to respect pinned versions and flag any unexpected resolution changes.
Application Security Engineers
Security practitioners need to understand pinning as both a control and a responsibility. Pinning limits exposure to supply chain attacks that exploit automatic version resolution, but it also requires active monitoring to ensure pinned versions are updated when security patches are released.
Open Source Maintainers
Maintainers of open source libraries must balance pinning in their own development workflows against the needs of downstream consumers. Overly strict pinning in a published library can create dependency conflicts, while insufficient pinning in development can lead to unreproducible test results.
Security Architects and Supply Chain Risk Managers
Those responsible for organizational supply chain security strategy should view dependency pinning as a foundational layer. It complements other controls such as hash verification, artifact signing, and provenance attestation, and its effectiveness depends on pairing it with processes for timely dependency updates.

Inside Dependency Pinning

Exact Version Specification
The practice of declaring a dependency using a precise version identifier (e.g., 1.4.2) rather than a range or floating constraint, ensuring that builds resolve to a single known version of each package.
Lock Files
Automatically generated files (such as package-lock.json, Pipfile.lock, or Gemfile.lock) that record the exact resolved versions of all direct and transitive dependencies, serving as a machine-readable manifest of the pinned dependency tree.
Hash or Integrity Verification
Cryptographic checksums stored alongside pinned versions to verify that the fetched artifact matches the expected content, providing a defense against tampering or substitution attacks on package registries.
Transitive Dependency Resolution
The process of recursively resolving and recording exact versions for all indirect dependencies introduced by direct dependencies, since unpinned transitive dependencies can introduce unexpected or malicious code even when direct dependencies are pinned.
Reproducible Builds
A key outcome of dependency pinning in which successive builds from the same source and lock file produce functionally identical artifacts, enabling auditability and reducing the risk of non-deterministic supply chain compromise.

Common questions

Answers to the questions practitioners most commonly ask about Dependency Pinning.

Does dependency pinning eliminate supply chain vulnerabilities?
No. Dependency pinning ensures reproducibility and prevents unexpected version changes, but it does not verify that the pinned version is free of vulnerabilities. A pinned dependency can still contain known or undiscovered security flaws. Pinning must be combined with vulnerability scanning and timely updates to be effective as a supply chain security measure.
Is dependency pinning the same as using a lock file?
Not exactly. Lock files typically capture resolved dependency versions, including transitive dependencies, as a side effect of the package manager's resolution process. Dependency pinning is the deliberate practice of specifying exact versions in manifest files or build configurations. Lock files may serve a similar reproducibility goal, but pinning represents an explicit intent to control version selection, and the two approaches differ in scope, granularity, and how they handle transitive dependencies.
How should teams handle transitive dependencies when pinning?
Pinning direct dependencies alone does not guarantee that transitive (indirect) dependencies remain fixed. Teams should use lock files or equivalent mechanisms in their package manager to capture the full resolved dependency tree. Some ecosystems support pinning transitive dependencies explicitly, while others rely on lock files to achieve this. Regularly auditing the full dependency tree, not just direct dependencies, is necessary to maintain consistency.
What is a practical strategy for keeping pinned dependencies up to date?
Teams typically use automated dependency update tools that propose version bumps as pull requests, allowing review and testing before changes are merged. Pinned versions should be reviewed on a regular cadence, and security advisories should trigger immediate evaluation of affected pins. Combining pinning with automated vulnerability scanning helps teams identify when a pinned version has become a liability rather than a safeguard.
How does dependency pinning interact with container image builds and CI/CD pipelines?
In CI/CD pipelines, dependency pinning helps ensure that builds are reproducible across environments and over time. Without pinning, successive builds may resolve different dependency versions, potentially introducing untested code. For container images, pinning both base image tags (preferably by digest) and application-level dependencies reduces the risk of unintended changes. However, pinning in these contexts requires active maintenance to incorporate security patches.
Are there cases where dependency pinning may introduce risk rather than reduce it?
Yes. If pinned versions are not actively maintained, they can become stale and accumulate known vulnerabilities over time. Pinning can create a false sense of security if teams treat it as a set-and-forget practice. In ecosystems where security patches are released frequently, overly rigid pinning without a corresponding update process may leave applications exposed to known issues longer than necessary.

Common misconceptions

Pinning dependencies eliminates the need to update them.
Pinning freezes the version used at build time but does not address newly discovered vulnerabilities in those versions. Pinned dependencies still require regular review, vulnerability scanning, and deliberate updates to remain secure.
Pinning direct dependencies is sufficient to secure the dependency tree.
Transitive (indirect) dependencies can also be exploited or updated unexpectedly. Without a lock file or equivalent mechanism that pins the entire resolved dependency graph, transitive dependencies may still float to unreviewed versions.
Dependency pinning alone prevents dependency confusion or typosquatting attacks.
Pinning specifies a version but does not, by itself, verify the source registry or the integrity of the artifact. Without additional controls such as hash verification, namespace scoping, or private registry configuration, pinning may still resolve to a malicious package that matches the pinned name and version.

Best practices

Commit lock files to version control so that every developer and CI/CD pipeline resolves the same dependency versions, ensuring reproducible and auditable builds.
Pin both direct and transitive dependencies by using lock files or tools that capture the full dependency graph, rather than relying solely on version constraints in manifest files.
Enable hash or integrity verification for pinned packages where your package manager supports it, to detect tampered or substituted artifacts fetched from registries.
Establish a regular cadence for reviewing and updating pinned dependencies, combining automated vulnerability scanning with manual assessment before accepting version changes.
Use private or curated registries with namespace scoping to reduce the risk that pinned package names resolve to malicious packages on public registries.
Integrate dependency pinning checks into CI/CD pipelines so that builds fail if lock files are missing, outdated relative to the manifest, or contain unverified hashes.