ADR 0019: Python dependency security, pip-audit, and pinning policy
Ratification
Adopted under ADR 0018. Link the discussion Issue and merge PR when they exist. If not, use git history as the record.
- Discussion Issue: none linked for this merge
- Merge PR: see git history for this file
- Accepted: 2026-04-12
Context
Why this matters: Applications depend on thousands of lines of other people’s code. Unpinned installs mean “works on my machine” might pull a different transitive version tomorrow. Known CVEs in public packages are tracked in advisory databases; scanning turns that signal into a failing check instead of a surprise in production.
We pin exact versions in requirements.txt, run pip-audit in Make and CI, and treat
severity levels with a written exception path when fixes cannot land immediately—aligned with common supply-chain
guidance (for example OWASP and NIST SSDF).
Decision
Pinning and single source of truth
-
requirements.txtis the committed install manifest for application and developer tooling installed viamake install. It must list exact versions (as produced bypip freeze) so Docker, CI, and local environments resolve the same dependency graph. -
After adding or upgrading packages in a virtualenv, regenerate the file with
make requirements(see repositoryREADME.md) and commit the diff in the same change that introduces the dependency change, unless the upgrade is a dedicated maintenance PR. -
Do not maintain a second parallel lockfile for Python unless a future ADR introduces a standard (for example
uv.lock); until then,requirements.txtremains authoritative.
Vulnerability scanning with pip-audit
-
Use pip-audit against the environment installed from
requirements.txtas the primary OSV-backed scanner for the PyPI ecosystem. -
Expose a Make target
make deps-auditthat fails the command with non-zero exit when pip-audit reports vulnerabilities, so developers can run the same check as CI locally. -
Integrate the same command into continuous integration (for example the existing quality workflow on
main/ pull requests) so dependency regressions cannot merge silently.
Severity and response expectations
- Critical / High: resolve before merging the affected change, or document a time-bound exception (see below). Prefer upgrading to a patched release; if none exists, consider removing or replacing the dependency after risk review.
- Medium: schedule remediation within the current iteration or the next; do not let findings accumulate without a tracked issue.
- Low / informational: triage during routine maintenance; batch upgrades are acceptable.
pip-audit may report vulnerabilities in dev-only tools (linters, test runners). Apply the same severity logic; if a fix would be disruptive, use the exception process instead of ignoring the report silently.
Exception process
- When remediation is not immediately possible (no fixed version, breaking upgrade required, or vendor delay), add a short note in the pull request: affected package, CVE/advisory ID, risk summary, owner, and review date (default: 30 days).
- Exceptions are not permanent: either upgrade, replace, or re-justify on each review date.
Upgrade cadence
-
Reactive: any PR that touches
requirements.txtshould prefer pulling in security patches for direct dependencies when pip-audit or release notes indicate fixes. - Proactive: at least one scheduled dependency hygiene pass per month (or each sprint) for non-emergency upgrades and churn reduction—optional but recommended as the team grows.
Scope
-
In scope: Python packages installed from PyPI via
requirements.txt, pip-audit wiring, Make and CI, and PR/review expectations. - Out of scope: base OS and container image CVEs (covered by image rebuild and base-image policy under ADR 0015), and non-Python supply chains unless separately documented.
Alternatives considered
-
Safety / other scanners only.
- Pros: historical familiarity in some teams.
- Cons: pip-audit aligns with PyPA and OSV; fewer surprises for Python-centric workflows.
-
Poetry or
uvlockfiles without migrating the repo.- Pros: richer dependency resolution metadata.
- Cons: would duplicate or replace the current
requirements.txt-centric Makefile and CI; defer until a dedicated migration ADR.
Consequences
Positive
- Reproducible installs and a defined path for CVE response.
- Same command locally and in CI reduces “works on my machine” drift for security checks.
Trade-offs
- Exact pins require deliberate upgrades; dependabot-style automation may still be added later.
- pip-audit depends on advisory completeness; false negatives are possible for unreported issues.
Compatibility and migration
- Backward-compatibility impact: none for API consumers; dependency upgrades follow normal release notes.
- Migration plan: add
pip-auditto developer/CI install lists, implementmake deps-audit, then gate CI. - Rollback strategy: revert the failing upgrade commit or temporarily document an exception with expiry.
Implementation plan
- Add pip-audit to the project’s install surface (for example
requirements.txtor a documented CI install step). - Implement
make deps-auditand document it next to other verify targets. - Run pip-audit in CI after
pip install -r requirements.txt; fail on unmitigated Critical/High per team policy above. - Reference this ADR from contributor or engineering-practices documentation.
Validation
- Technical checks:
make deps-auditpasses on a clean tree after upgrades; CI job green. - Operational checks: maintainers know where to record exceptions (PR description + optional issue).
- Documentation updates: this ADR, backlog item, and developer docs cross-links.
References
- Related ADRs: ADR 0015 (container image).
- Backlog: P1 — Dependency security (pip-audit) and update policy.
- pip-audit: PyPI.
Page history
| Date | Change | Author |
|---|---|---|
| Added Page history section (repository baseline). | Ivan Boyarkin |