CVE-2026-69247 in cryptography 49.0.0 turns CI red; refresh the backend pins #137

Closed
opened 2026-08-03 21:46:17 +00:00 by claude-bot · 1 comment

What happened

The backend CI job went red on d878952 at the dependency audit step, not on any code in that commit:

Found 1 known vulnerability in 1 package
Name          Version  ID              Fix Versions
------------  -------  --------------  ------------
cryptography  49.0.0   CVE-2026-69247  50.0.0

Lint passed; frontend and e2e both passed. The advisory was published between run #54 (ae6dc43, green, 19:17 UTC) and run #55 (21:39 UTC) — pip-audit resolves advisories live, so nothing in the repository changed to cause this.

Why it matters more than a routine bump

cryptography is not a direct dependency. It arrives transitively:

cryptography==49.0.0
    # via
    #   authlib
    #   joserfc

Both of those are on the authentication pathauthlib drives the OIDC flow and joserfc handles the JWTs. So this sits under the mechanism guarding the archive, which is the part of §4 the project treats as load-bearing.

There is a second, procedural consequence: the audit step runs before the test step, so a red audit means the backend suite does not run at all. Every push lands unverified by CI until this is cleared. #94's 1186 backend tests passed locally but have never been run by CI.

What is being done

A full pip-compile --upgrade of both requirements.txt and requirements-dev.txt, rather than a targeted --upgrade-package cryptography. Chosen deliberately by @rbrooks over the narrower fix: it picks up anything else that has drifted or is quietly affected, and matches what Renovate's lock-file-maintenance would do anyway. The trade-off accepted is that a large diff makes any resulting failure harder to attribute, so the full suite and the e2e journeys are run before this is pushed.

pip-tools is not installed in backend/.venv and is not pinned in pyproject.toml — it is the tool that generates these files but is not itself part of the locked set. Installing it into the venv to regenerate; whether it deserves a pin of its own is a separate question and is not being decided here.

Renovate PR #133 does not cover this: it is chore(deps): lock file maintenance but touches only frontend/package-lock.json.

Done when

  • pip-audit reports no vulnerabilities against requirements.txt and requirements-dev.txt
  • The full backend suite passes on the refreshed pins
  • The e2e journeys pass, since the OIDC and session path is what the upgraded libraries sit under
  • CI is green, and the backend tests actually run

References

## What happened The `backend` CI job went red on d878952 at the **dependency audit** step, not on any code in that commit: ``` Found 1 known vulnerability in 1 package Name Version ID Fix Versions ------------ ------- -------------- ------------ cryptography 49.0.0 CVE-2026-69247 50.0.0 ``` Lint passed; `frontend` and `e2e` both passed. The advisory was published between run #54 (ae6dc43, green, 19:17 UTC) and run #55 (21:39 UTC) — `pip-audit` resolves advisories live, so nothing in the repository changed to cause this. ## Why it matters more than a routine bump `cryptography` is not a direct dependency. It arrives transitively: cryptography==49.0.0 # via # authlib # joserfc Both of those are on the **authentication path** — `authlib` drives the OIDC flow and `joserfc` handles the JWTs. So this sits under the mechanism guarding the archive, which is the part of §4 the project treats as load-bearing. There is a second, procedural consequence: the audit step runs *before* the test step, so a red audit means **the backend suite does not run at all**. Every push lands unverified by CI until this is cleared. #94's 1186 backend tests passed locally but have never been run by CI. ## What is being done A full `pip-compile --upgrade` of both `requirements.txt` and `requirements-dev.txt`, rather than a targeted `--upgrade-package cryptography`. Chosen deliberately by @rbrooks over the narrower fix: it picks up anything else that has drifted or is quietly affected, and matches what Renovate's lock-file-maintenance would do anyway. The trade-off accepted is that a large diff makes any resulting failure harder to attribute, so the full suite and the e2e journeys are run before this is pushed. `pip-tools` is not installed in `backend/.venv` and is not pinned in `pyproject.toml` — it is the tool that generates these files but is not itself part of the locked set. Installing it into the venv to regenerate; whether it deserves a pin of its own is a separate question and is **not** being decided here. Renovate PR #133 does not cover this: it is `chore(deps): lock file maintenance` but touches only `frontend/package-lock.json`. ## Done when - [ ] `pip-audit` reports no vulnerabilities against `requirements.txt` and `requirements-dev.txt` - [ ] The full backend suite passes on the refreshed pins - [ ] The e2e journeys pass, since the OIDC and session path is what the upgraded libraries sit under - [ ] CI is green, and the backend tests actually run ## References - Failing run: https://git.rhoving.com/rbrooks/Circa/actions/runs/55 - `backend/requirements.txt`, `backend/requirements-dev.txt` - `.forgejo/workflows/ci.yml` — the `Dependency audit` step, which audits the locked set rather than the installed set (#72's reasoning)
Author

Done in da4b933. CI run #56 is green on all three jobs.

Six packages moved: cryptography 49.0.0 → 50.0.0, fastapi 0.140.13 → 0.141.1, uvicorn 0.51.0 → 0.52.1, websockets 16.1.1 → 17.0.1, cffi 2.1.0 → 2.1.1, ruff 0.16.0 → 0.16.1.

Done when:

  • pip-audit reports no vulnerabilities against either locked set
  • The full backend suite passes on the refreshed pins — 1186 passed, 8 skipped, identical to before the upgrade
  • The e2e journeys pass — all 7, which is the check that mattered here since authlib and joserfc are what the OIDC and session journeys run through
  • CI is green, and the backend tests actually ran — this run is the first to execute them since the audit began failing, so #94's 1186 tests are now confirmed by CI rather than only locally

Three things worth recording, none of which were visible from the advisory.

The FastAPI bump was checked rather than assumed: openapi.json regenerates byte-identical, so the generated frontend types and the types:check gate are untouched. git reports no change to that file at all.

The pins were regenerated in a Python 3.12 container, not with the local interpreter, which is on 3.14. pip-compile resolves for the interpreter it runs on, so generating above the requires-python floor can silently omit a dependency only a lower version needs — CI runs 3.11, where that would surface as an ImportError rather than as a diff anyone would notice here. 3.12 is what the existing files' headers name, so this keeps the convention rather than changing it.

pip-audit cannot be run on Windows at all, and this is worth knowing before someone tries: it resolves the locked set by installing it, and uvloop is POSIX-only with no Windows wheel and no buildable source. The same applies to a plain pip install -r requirements-dev.txt. Neither is caused by this refresh — uvloop is unchanged at 0.22.1 and was never in backend/.venv — but it means a local audit cannot pass, and reading its failure as a finding would be a mistake. Audited in a Linux container instead, which is what CI does anyway.

One loose end, deliberately not decided here. pip-tools generates these files but is not pinned in pyproject.toml and is not in either locked set, so the tool that produces the lock is itself unlocked. I installed it into backend/.venv to do this work. Whether it deserves a pin is a real question — an unpinned generator can quietly change resolution behaviour between runs — but it is not this issue's, and I have not changed it.

Done in da4b933. CI run [#56](https://git.rhoving.com/rbrooks/Circa/actions/runs/56) is green on all three jobs. Six packages moved: `cryptography` 49.0.0 → **50.0.0**, `fastapi` 0.140.13 → 0.141.1, `uvicorn` 0.51.0 → 0.52.1, `websockets` 16.1.1 → 17.0.1, `cffi` 2.1.0 → 2.1.1, `ruff` 0.16.0 → 0.16.1. **Done when:** - [x] `pip-audit` reports no vulnerabilities against either locked set - [x] The full backend suite passes on the refreshed pins — 1186 passed, 8 skipped, identical to before the upgrade - [x] The e2e journeys pass — all 7, which is the check that mattered here since `authlib` and `joserfc` are what the OIDC and session journeys run through - [x] CI is green, **and the backend tests actually ran** — this run is the first to execute them since the audit began failing, so #94's 1186 tests are now confirmed by CI rather than only locally **Three things worth recording, none of which were visible from the advisory.** The FastAPI bump was checked rather than assumed: `openapi.json` regenerates **byte-identical**, so the generated frontend types and the `types:check` gate are untouched. `git` reports no change to that file at all. The pins were regenerated in a **Python 3.12 container**, not with the local interpreter, which is on 3.14. `pip-compile` resolves for the interpreter it runs on, so generating above the `requires-python` floor can silently omit a dependency only a lower version needs — CI runs 3.11, where that would surface as an ImportError rather than as a diff anyone would notice here. 3.12 is what the existing files' headers name, so this keeps the convention rather than changing it. `pip-audit` **cannot be run on Windows at all**, and this is worth knowing before someone tries: it resolves the locked set by installing it, and `uvloop` is POSIX-only with no Windows wheel and no buildable source. The same applies to a plain `pip install -r requirements-dev.txt`. Neither is caused by this refresh — `uvloop` is unchanged at 0.22.1 and was never in `backend/.venv` — but it means a local audit *cannot* pass, and reading its failure as a finding would be a mistake. Audited in a Linux container instead, which is what CI does anyway. **One loose end, deliberately not decided here.** `pip-tools` generates these files but is not pinned in `pyproject.toml` and is not in either locked set, so the tool that produces the lock is itself unlocked. I installed it into `backend/.venv` to do this work. Whether it deserves a pin is a real question — an unpinned generator can quietly change resolution behaviour between runs — but it is not this issue's, and I have not changed it.
Sign in to join this conversation.
No description provided.