Join our Newsletter — 33% off our NHI Course

Why do source distributions used as build requirements increase supply chain risk for Python installs?

Source distributions increase risk because installing them can execute package code during the build process before the package is trusted or fully accounted for. When that happens in build requirements, malicious code can run outside the normal lockfile view and disappear after installation. The result is hidden dependency execution, weaker provenance, and a much harder investigation path if compromise is suspected.

Why This Matters for Security Teams

Build requirements are a high-friction part of Python supply chain security because they run before the final application is fully assembled, so they often sit outside the strongest visibility and review paths. That makes source distributions more dangerous than prebuilt artifacts: the build step can execute package code while the dependency graph is still being resolved, and the resulting behaviour may never be obvious from the installed package alone. For teams trying to understand provenance, that is a trust boundary problem, not just a packaging preference.

In practice, the risk is that a seemingly ordinary install process becomes a code-execution path during build, with dependencies that are harder to pin, inspect, and later prove. That is why provenance controls such as SLSA matter here, because they focus attention on where artifacts came from and what was allowed to run while they were produced. Python teams usually discover the problem only after a compromised build, not during routine dependency review.

How It Works in Practice

Source distributions increase risk because the installer often has to build the package locally, and that means executing build hooks, backend logic, or helper scripts before the final artifact is trusted. In a normal install, a wheel can be treated as a more bounded artifact. With an sdist, the build system may need to fetch, evaluate, and execute package-controlled code to produce that artifact first. If the package is used as a build requirement, that execution happens early in the chain, where it can influence later dependency resolution, generated files, and the final environment.

That creates three practical problems:

  • Build-time code can run before the dependency is locked down.
  • Malicious logic can affect what gets built, imported, or cached.
  • Post-install inspection may miss what happened during the build step itself.

The control question is therefore not just “is this package version approved?” but “can we trust the build path that turns this source into a usable dependency?” Frameworks such as NIST SSDF (SP 800-218) and OpenSSF guidance both push teams toward hardened build practices, deterministic inputs, and stronger artifact provenance. For Python specifically, that often means preferring wheels where possible, isolating builds, validating hashes, and treating build dependencies as executable supply chain inputs rather than passive metadata.

Where this guidance breaks down is in projects that require native compilation, dynamic build tooling, or packages that only publish sdists, because the build step itself becomes part of the attack surface and cannot be removed without changing the delivery model.

Common Variations and Edge Cases

Tighter build controls often increase operational overhead, so teams need to balance reproducibility and safety against developer convenience and package availability. Some Python projects are forced to consume sdists because no wheel exists for the target platform, because native extensions must be compiled locally, or because build backends generate code at install time. In those cases, the risk is not theoretical, it is an architectural consequence of how the package is delivered.

One important distinction is between ordinary runtime dependencies and build requirements. Build requirements are often overlooked because they feel temporary, but they can still execute with significant privilege inside CI or developer environments. That is why supply chain hardening is usually strongest when teams separate build and runtime stages, reduce what can execute during build, and verify artifact provenance before promotion. The PyPI Breach is a useful reminder that package ecosystem trust can be abused at the distribution layer, not just through code already inside an application.

Current guidance suggests treating any sdist used in build requirements as higher risk than a wheel with equivalent version semantics, especially when the package comes from a new publisher, has sparse release history, or requires network access during build. The practical tradeoff is simple: the more flexibility a build needs, the more carefully the team has to constrain what that build is allowed to do.

Risk and Threat Considerations

Build requirements widen the compromise window because they can execute before normal application controls, review gates, and runtime protections are in place. That makes them attractive for supply chain abuse, especially when the package source is less trusted than the final application code.

Failure mechanism: An attacker abuses build-time execution in an sdist, then hides malicious behaviour inside installation, code generation, or temporary build artefacts. Because the action occurs before the package is fully installed, defenders may only see the resulting output, not the code path that produced it.

Impact: The result can be dependency poisoning, hidden code execution in CI or developer environments, and a much harder incident investigation because the compromised behaviour may not persist in an obvious installed file.

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 NIST CSF 2.0, NIST SP 800-63 and CIS Controls v8 set the governance and control requirements practitioners need to meet.

Framework Control / Reference Relevance
NIST CSF 2.0 PR.IP-1 — Configuration Management Build requirements affect artifact integrity and approved software paths.
PR.DS-6 — Data at Rest is Protected Package artifacts and build outputs need integrity protection across the supply chain.
DE.CM-8 — Vulnerability Scans Build-time execution risks need monitoring for unexpected package behaviour.
Recommendation — Harden build pipelines so source-built dependencies are controlled and traceable. Protect build inputs and outputs with integrity checks and trusted storage. Monitor build activity for anomalous dependency execution and tampering.
NIST SP 800-63 Digital Identity Guidelines Build provenance depends on trusted assertions about package origin and integrity.
Recommendation — Use strong provenance assertions before accepting source-built packages.
CIS Controls v8 16.9 — Centralized Access Control of Scripts Build requirements can execute scripts that need controlled execution paths.
18.9 — Establish and Maintain Secure Software Development Standards Secure build practices directly reduce supply chain exposure from sdists.
Recommendation — Restrict script execution in build pipelines to approved, isolated environments. Require secure build standards for source-distributed Python dependencies.
MITRE ATT&CK T1195 — Supply Chain Compromise Malicious sdists are a classic software supply chain compromise path.
Recommendation — Map package-build compromise activity to supply-chain detection and response.

Practitioner Guidance

What to prioritise: Treat build requirements as executable inputs, not just versioned dependencies. If a package must be built from source, classify that path as higher risk than consuming a verified wheel and require additional review before it reaches shared build infrastructure.

What to verify: Confirm whether the build step needs network access, dynamic code generation, or environment-sensitive scripts. Those are the conditions that most often turn a routine install into an execution path that can alter provenance, cache state, or downstream artefacts.

Decision rule: If the package can be consumed as a prebuilt wheel with integrity verification, prefer that path. If it must be built from source, isolate the build, minimise its privileges, and preserve enough logs and artefacts to reconstruct what ran.

Practitioner takeaway: The real control is not “avoid sdists at all costs”, it is “never let an untrusted build step become the place where your supply chain makes irreversible decisions.”