Frontend Labs
⋯

50. Supply chain: lockfiles, hashes, audits, and trusted publishing

PyPI hosts over half a million projects, and anyone can publish. Attacks seen in the wild: typosquatting (reqeusts, python-dateutil vs dateutil), dependency confusion (a public package with the same name as your internal one, at a higher version), account takeover of a maintainer, malicious code in setup.py that runs at install time, and compromised release pipelines. The Security guide Ch 47 and Security in Depth Ch 13 cover the theory; here is the Python practice.

# 1. Lock exact versions AND hashes of everything, including transitive dependencies.
uv lock                                   # uv.lock records sha256 hashes for every artifact
uv sync --locked                          # install exactly that; fail if the lock is stale
# with pip: pip-compile --generate-hashes; pip install --require-hashes -r requirements.txt

# 2. Known vulnerabilities (PyPI advisory database / OSV).
uv tool run pip-audit                     # in CI, fail the build on findings

# 3. Prefer wheels: an sdist runs arbitrary build code on your machine at install time.
uv sync --no-build                        # refuse to build anything from source
# pip install --only-binary :all: ...

# 4. Internal packages: one index, explicitly. Never mix --extra-index-url with public PyPI for
#    names you own (dependency confusion). In uv, pin each package to an index:
#    [tool.uv.sources] mylib = { index = "internal" }  +  [[tool.uv.index]] name="internal" explicit=true

# 5. See what you actually pull in, and why.
uv tree --depth 2

Publishing your own packages: use PyPI Trusted Publishing from CI (GitHub Actions OIDC; no long-lived API token to steal), and PyPI now records attestations (Sigstore-signed provenance) for packages published this way. Turn on 2FA for every maintainer account.

Before adding a dependency, check: is it the real name (look at the project's repository link on PyPI); how many maintainers; when it was last released; how many dependencies it brings (uv tree after a trial add); and whether the standard library already does it. Every dependency is code that runs with your program's privileges, and in AI projects, often with access to GPUs, datasets, and cloud credentials.

War story. In late 2022, a malicious package named torchtriton was uploaded to public PyPI. PyTorch nightly builds depended on a package of that name hosted on PyTorch's own index, but pip's resolution with --extra-index-url let the public, higher-priority copy win. For several days, people installing PyTorch nightly got a binary that read SSH keys, .gitconfig, and other files and sent them out over DNS. It was textbook dependency confusion. Lesson: when you use more than one package index, pin each internal name to its index explicitly, and verify hashes.


Generated from real-life-python-guide.md · 8 Oct 2026 16:36