Because the runtime trusts the compiled artefact, not just the source file humans review. A compromised build step can distribute a malicious .pyc alongside an innocent .py, and downstream consumers may execute the cache without ever seeing the payload in code review or static scanning.
How a poisoned Python cache becomes a supply-chain problem
A poisoned .pyc is dangerous because it changes what consumers execute, not just what they can inspect. In Python ecosystems, that matters whenever a build, packaging, or deployment step can publish artefacts that downstream systems trust more than the source tree they review. The risk is strongest when the cache can travel with the package or be regenerated from a compromised pipeline.
The core failure mode is provenance drift. Reviewers, linters, and static scanners usually focus on source files, but the runtime may load compiled bytecode or cached artefacts as the effective execution target. If an attacker can alter that artefact at build time, the malicious logic can survive ordinary code review paths and land in consumers’ environments as a trusted dependency.
This is why the issue sits inside software supply-chain security rather than ordinary file tampering. The artefact itself becomes the delivery vehicle, and the trust boundary shifts from “did we read the source” to “did we verify what was built, packaged, signed, and installed.” In practice, that means a clean repository is not enough if the release pipeline, wheel contents, or cached bytecode are not controlled end to end. For a broader control model, SLSA is useful because it frames build provenance and artefact integrity as first-class security requirements.
Where the attack enters and why review misses it
Poisoning can happen anywhere an attacker gets influence over the built artefact: a compromised maintainer account, a malicious build step, a contaminated cache, a vulnerable CI job, or a package publication pipeline that trusts the wrong output. Once the bytecode is replaced, downstream consumers may execute the attacker’s version even if the source file that humans inspect still looks benign.
That separation between source and executable output is what makes the attack subtle. Static scanning often reasons over .py files and dependency metadata, but a poisoned cache can carry hidden behaviour that is only visible after compilation or unpacking. In ecosystem terms, the package manager may faithfully deliver the artefact while the pipeline silently transferred trust from the code author to the build system.
Attackers prefer this path because it lets them abuse ordinary distribution trust. A malicious artefact inside an otherwise familiar package can inherit the reputation of the project, the maintainers, and the registry. The downstream consumer is then exposed not by a single vulnerable line of code, but by the assumption that the published object represents the reviewed source. NIST SSDF (SP 800-218) is relevant here because it ties secure build and release practices to software integrity, not just source review.
Why defenders should treat cache integrity as a release-control issue
The practical lesson is that Python bytecode and other compiled caches need the same control discipline as any other release artefact. If you cannot prove which source produced the cache, when it was built, and whether the build step was trustworthy, then the cache should not be treated as a neutral performance optimisation. It is part of the shipped security boundary.
This also changes how teams should think about verification. Signed artefacts, reproducible builds, controlled CI runners, and post-build integrity checks matter more than simply scanning repository contents. Where package ecosystems are involved, the safest posture is to assume that anything shipped alongside source may be executed unless the pipeline proves otherwise.
For Python-specific ecosystem exposure, PyPI secrets exposure 2023 and Ultralytics PyPI compromise 2024 are useful reminders that package trust failures often start upstream of the consuming application. They show why release integrity and credential hygiene are part of the same risk surface as the cache itself.
Risk and Threat Considerations
A poisoned cache creates a supply-chain risk because the attacker only needs to compromise the artefact once for every downstream consumer to inherit the payload. That makes the blast radius larger than a single repository or workstation, especially when the package is reused in build pipelines, containers, or deployed images.
Failure mechanism: The build or publication step produces a bytecode artefact that differs from the reviewed source, and consumers execute the cached output because their trust model prioritises shipped artefacts over human-reviewed code.
Impact: Malicious logic can bypass source review and static scanning, enabling hidden backdoors, credential theft, persistence, or further propagation through dependent systems.
Standards & Framework Alignment
This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.
SLSA, NIST SP 800-53 Rev 5 and CIS Controls v8 set the governance and control requirements practitioners need to meet.
| Framework | Control / Reference | Relevance |
|---|---|---|
| SLSA | Supply-chain Levels for Software Artifacts | Build provenance and artefact integrity are central to poisoned bytecode risk. |
| Recommendation — Adopt provenance controls so shipped Python artefacts can be verified against trusted builds. | ||
| NIST SP 800-53 Rev 5 | SI-7 — Software, Firmware, and Information Integrity | Poisoned caches undermine integrity of executable software artefacts. |
| CM-5 — Access Restrictions for Change | Attackers need change paths into build and packaging outputs. | |
| Recommendation — Verify release artefacts before deployment and detect unauthorised code changes. Restrict who can alter build outputs, caches, and published packages. | ||
| CIS Controls v8 | CIS-16 — Application Software Security | Python cache poisoning is a software supply-chain integrity problem. |
| Recommendation — Protect software release paths so only vetted artefacts reach consumers. | ||
Practitioner Guidance
What to verify: Confirm which artefacts are actually executed in your Python deployment path, then check whether those artefacts are produced in controlled, reproducible builds. If a cache or wheel can be published without provenance evidence, treat that as a release-control gap rather than a developer convenience.
What good looks like: The build system can show a clear lineage from source commit to packaged artefact, and consumers can validate that the shipped object matches the expected release output. In other words, the artefact is trusted because it is proven, not because it merely came from the right registry.
Practitioner takeaway: For supply-chain defence, the important question is not whether the source looked clean, but whether the executable artefact was produced, signed, and delivered by a pipeline you can trust.
Related resources from NHI Mgmt Group
- Why do immutable package caches still create supply-chain risk?
- Why do compromised CI and developer credentials create such a large supply chain risk for Python ecosystems?
- Why do poisoned AI configuration files create supply chain risk for software teams?
- Why does a malicious setup script create such serious supply chain risk in Python?