Regenerate backend lockfiles to clear pip-audit failures, and assert they match pyproject - #292
Merged
Merged
Conversation
The backend CI job has failed at the `pip-audit` step on every run since Aug 10, on main and on every open PR, blocking the `Tests` step behind it. pip-audit reported 10 advisories across three pinned packages: aiohttp 3.14.1 → 3.14.3 (PYSEC-2026-3545/3546/3547) cryptography 49.0.0 → 50.0.1 (PYSEC-2026-3552) pip 26.1.2 → 26.2.1 (PYSEC-2026-3721) The pip pin is the odd one: pip-compile records it under --allow-unsafe because pip-api and pip-tools require it, and `pip install --require-hashes` was then *downgrading* the runner's own pip to the vulnerable version. Bumping the pin is enough — dropping it would need the --allow-unsafe flag to change, which is a bigger call than this fix warrants. Recompiling also pulls fastapi 0.139.0 → 0.141.1 and litellm 1.92.0 → 1.94.1. Those are not upgrades so much as the lock catching up: pyproject has pinned 0.141.1 and 1.94.1 since #280, but the lockfiles were last regenerated at 26c434a. Because CI installs the lock and then runs `pip install --no-deps -e .`, the stale lock silently won and the declared pins never took effect. Regenerated with: pip-compile --allow-unsafe --generate-hashes \ --output-file=requirements.lock pyproject.toml \ --upgrade-package aiohttp --upgrade-package cryptography pip-compile --allow-unsafe --extra=dev --generate-hashes \ --output-file=requirements-dev.lock pyproject.toml \ --upgrade-package aiohttp --upgrade-package cryptography \ --upgrade-package pip No other package versions moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KqXjgMRArAxubgd5f5UYgC
Pinning pip 26.2.1 in the previous commit broke the dev toolchain: pip-tools 7.6.0 imports stdlib_pkgs from pip._internal.utils.compat, which pip 26.2 removed, so `pip-compile` dies on import. It went unnoticed because CI never ran pip-compile — only pip install, ruff, mypy, pip-audit and pytest. pyproject now floors pip-tools at >=7.6.1 with the reason inline, so the constraint survives the next regeneration rather than being rediscovered. Adds a `Check lockfiles match pyproject` step to the backend job. CI installs `--require-hashes` from the lock and then `--no-deps -e .`, so pyproject's pins are never enforced — a bump that edits pyproject without regenerating the locks silently does nothing and CI keeps testing the old versions. That is how fastapi and litellm drifted two and three releases behind their declared pins. Since pip-compile without --upgrade keeps whatever the lock already pins, the re-run is a no-op until the two disagree. The step runs last so a drift failure never masks lint, type or test results. Verified: plain re-run of both pip-compile commands is byte-identical to the committed locks; introducing a pyproject-only bump makes the step fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KqXjgMRArAxubgd5f5UYgC
d3mocide
marked this pull request as ready for review
September 16, 2026 14:52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The
Backend — lint, type-check & testsjob has failed at thepip-auditstep on every run since Aug 10 — onmain(run 31128880028 atd49e0b6) and on every open PR (#286, #287, #288, #289, #290, #291). Because the audit runs beforeTests, the backend test suite has not actually executed in CI on any of them.Advisories cleared
aiohttpandcryptographyare transitive (litellm, pywebpush, argon2-cffi), so they only move in the lock.The
pipentry is worth understanding:pip-compilerecords it under--allow-unsafebecausepip-apiandpip-toolsrequire it, andpip install --require-hashes -r requirements-dev.lockwas then downgrading the runner's own pip — the CI log showsFound existing installation: pip 26.2.1 → Successfully installed pip-26.1.2, i.e. the lockfile was actively installing the vulnerable version over a patched one.pip-tools 7.6.0 → 7.6.1 (fallout from the pip bump)
Pinning pip 26.2.1 broke the dev toolchain: pip-tools 7.6.0 imports
stdlib_pkgsfrompip._internal.utils.compat, which pip 26.2 removed, sopip-compiledies on import:Nothing in CI caught this, because CI never ran
pip-compile— onlypip install, ruff, mypy, pip-audit and pytest.pyproject.tomlnow floorspip-tools>=7.6.1with the reason inline so the constraint survives the next regeneration.The lock was stale vs. pyproject
Recompiling moves three more packages that I did not ask to upgrade:
fastapi==0.141.1litellm==1.94.1uvicorn[standard]==0.52.0These aren't upgrades so much as the lock catching up. Dependabot has been bumping
pyproject.toml(#280 and the fastapi/uvicorn bumps before it) without regenerating the lockfiles, which were last rebuilt at26c434a. Since CI installs the lock and then runspip install --no-deps -e ., the--no-depsmeans pyproject's pins are never enforced — so CI and the built image have been running fastapi 0.139.0 / litellm 1.92.0 / uvicorn 0.51.0 while pyproject declared 0.141.1 / 1.94.1 / 0.52.0.Merging this is the point at which those declared versions actually take effect. I verified the suite against them (below).
Complete set of version changes — seven in
requirements-dev.lock, five inrequirements.lock, nothing else:New CI step:
Check lockfiles match pyprojectAdded to the backend job to stop this recurring. It re-runs both
pip-compilecommands and fails if the result differs from what's committed. Sincepip-compilewithout--upgradekeeps whatever the lock already pins, the re-run is a no-op until pyproject and the locks disagree.It runs last, after
Tests, so a drift failure never masks lint/type/test results — the opposite of the ordering problem that hid the test suite for two months.Worth considering separately:
Audit dependenciescurrently sits beforeTests, which is why a dependency CVE has been hiding whether the code works. MovingTestsabove it would decouple the two. I left the ordering alone since it wasn't asked for.Testing
Clean Python 3.12 venv, installed exactly as CI does (
pip install --require-hashes -r requirements-dev.lock && pip install --no-deps -e .):pip-audit— No known vulnerabilities found (was 10 across 3 packages)ruff check .— All checks passedmypy luma --ignore-missing-imports— Success, 78 source filespytest -x -q— 201 passedDrift check, both directions:
pip-compilecommands under the locked toolchain is byte-identical to the committed locks, so the step is a no-op on a healthy tree.orjson3.11.9 → 3.11.8) makes the step fail as intended.Since
cryptographyis a major bump (49 → 50) and underpins auth and push, I smoke-tested those paths directly on the new version: Argon2id hash/verify round-trip, VAPID keypair generation,pywebpushimport, and PyJWT HS256 encode/decode all work.🤖 Generated with Claude Code
https://claude.ai/code/session_01KqXjgMRArAxubgd5f5UYgC