Many teams assume a lockfile captures every package that matters, but standard Python lockfiles often omit build requirements declared in pyproject.toml or setup.py. That omission leaves blind spots for security review and compliance evidence. A lockfile is only complete when it records both runtime and build-time dependencies, including any transitive build inputs that can be resolved during package installation.
Why This Matters for Security Teams
Python lockfiles are often treated as proof that dependency review is complete, but that assumption breaks down when the build step can pull in additional packages from pyproject.toml, setup.py, or backend-specific tooling. For security and compliance teams, the gap matters because the artefacts that actually execute during installation are not always the same artefacts that appear in the lockfile. That creates a review blind spot for hidden transitive inputs, surprise build logic, and changes that only emerge in CI or packaging pipelines.
The practical risk is not just inconsistency, it is incomplete evidence. If teams can only attest to runtime dependencies, they may miss the very packages that shape the final artifact, introduce installation-time code execution, or widen the supply-chain attack surface. Standards such as SLSA are useful here because they push teams toward provenance and build integrity rather than relying on a single dependency snapshot. In practice, many teams discover the missing piece only after a build behaves differently in CI than it did in local review, rather than through a deliberate dependency policy.
How It Works in Practice
A Python lockfile typically aims to pin the runtime set, but the build process can still resolve and execute separate inputs. That includes build-system requirements, backend plugins, and transitive build dependencies that are installed before the final package is assembled. If those inputs are not captured, the lockfile gives a false sense of completeness.
Teams usually need to think in two layers:
- Runtime dependencies: the packages imported by the application when it runs.
- Build dependencies: the packages and tools required to create, compile, or install the distribution.
The gap becomes most visible when a project uses dynamic metadata, native extensions, optional build backends, or conditional requirements. A package may appear stable at runtime while still depending on build-time helpers that are free to change between installs. That is why a defensible review process should inspect the build configuration, not just the lockfile contents. If the repository uses multiple packaging standards or custom build hooks, the security question is whether those paths are deterministic, recorded, and reviewable.
This is also where provenance controls matter. SLSA helps teams separate “what was pinned” from “what was actually built”, which is the distinction security reviewers need when validating release integrity. The goal is not merely to enumerate packages, but to ensure the installation path is reproducible enough for audit and incident review. These controls tend to break down when projects rely on backend-specific dynamic dependency resolution, because the final build inputs are determined too late to be captured cleanly in a runtime-only lockfile.
Common Variations and Edge Cases
Tighter dependency control often increases build friction, so teams have to balance reproducibility against packaging flexibility. The right answer depends on whether the project is pure Python, uses compiled extensions, or delegates work to a backend that resolves inputs at install time.
One common edge case is editable or local development installs. These can behave differently from release builds, so a lockfile may look complete in development while still failing to describe the production build path. Another is conditional build logic, where platform markers, environment variables, or optional extras change the final dependency set. In those cases, the lockfile may be accurate for one environment and incomplete for another.
A further complication is that some tools only lock the direct resolution tree and do not record the build-time environment that produced the wheel or sdist. For audit purposes, that means the organisation may need both a dependency lock and a build-provenance record. Current guidance suggests treating reproducibility as a property of the full pipeline, not just the dependency manifest. When the build backend can execute arbitrary code or fetch inputs dynamically, teams should assume the lockfile is a starting point, not the final control.
Standards & Framework Alignment
This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.
MITRE ATT&CK address the attack and risk surface, while CIS Controls v8 and NIST CSF 2.0 set the governance and control requirements practitioners need to meet.
| Framework | Control / Reference | Relevance |
|---|---|---|
| CIS Controls v8 | CIS 16 — Application Software Security | Python dependency integrity is part of secure software build and release practice. |
| CIS 15 — Service Provider Management | Third-party package and build ecosystems are a supply-chain dependency to govern. | |
| Recommendation — Verify build and dependency inputs before release and block unreviewed package resolution. Assess package and build ecosystem trust before allowing it into release paths. | ||
| NIST CSF 2.0 | PR.DS — Data Security | Dependency provenance protects the integrity of the software artifact and release inputs. |
| PR.IP — Information Protection Processes and Procedures | Lockfile blind spots require repeatable build and review procedures. | |
| PR.SD — Security by Design | Build dependencies should be governed as part of secure software design. | |
| Recommendation — Protect release integrity by documenting and controlling all artifact inputs. Define procedures that record build-time dependencies alongside runtime pins. Design packaging workflows so build dependencies are explicit and reproducible. | ||
| MITRE ATT&CK | T1195 — Supply Chain Compromise | Missing build inputs create an opening for dependency tampering and package compromise. |
| Recommendation — Hunt for supply-chain tampering in build tools, dependencies, and package sources. | ||
Practitioner Guidance
What to prioritise: Verify that your release process captures build dependencies separately from runtime pins, and treat any dynamic build path as a higher-risk condition until it is explicitly documented and reproducible.
What to verify: Check whether the package build can introduce inputs that are absent from the lockfile, including backend requirements, platform-specific branches, and transitive build-time packages. If the answer is yes, the lockfile is not sufficient evidence on its own.
Practitioner takeaway: The useful control is not “we have a lockfile”, it is “we can explain every dependency that influenced the shipped artifact, including the ones resolved during build.”