backend: bump Python deps with major-version cascades for 7 HIGH CVEs#7127
backend: bump Python deps with major-version cascades for 7 HIGH CVEs#7127
Conversation
cryptography: 43.0.0 -> 46.0.5 fixes CVE-2026-26007 (HIGH 6.5). Cascade: cryptography 46 requires cffi >=2.0.0, so cffi is bumped 1.16.0 -> 2.0.0 alongside. Three majors of cryptography in one bump (43->44->45->46) — held back to the lowest version that closes the CVE. Verified with docker test image rebuild + unit subset all green; cryptography 46.0.5 and cffi 2.0.0 confirmed installed.
pillow: 10.4.0 -> 12.2.0 fixes: - CVE-2026-25990 (HIGH 7.5, fixed in 12.1.1) - CVE-2026-40192 (HIGH 7.5, fixed in 12.2) Two majors (10 -> 11 -> 12). Cascade enforced by transitive pinning of pillow inside streamlit/altair: - streamlit: 1.37.1 -> 1.51.0 (1.37 pinned pillow<11; 1.51 allows <13) - altair: 5.4.0 -> 5.5.0 (streamlit 1.51 excludes altair 5.4.0/5.4.1) - narwhals: 1.5.2 -> 1.14.2 (altair 5.5 requires narwhals>=1.14.2) Held streamlit at 1.51 instead of 1.57 to avoid the further cascade into cachetools >=5.5 that the latest version forces. Verified with docker test image rebuild + unit subset all green.
protobuf: 4.25.8 -> 5.29.6 fixes CVE-2026-0994 (HIGH 7.5). Major bump (4.x -> 5.x) cascades through the gRPC + Google client stack, all of which pinned protobuf<5: - grpcio: 1.65.4 -> 1.66.0 (1.66 is the first to allow protobuf 5) - grpcio-status: 1.62.3 -> 1.66.0 (same constraint) - grpcio-tools: 1.62.3 -> 1.66.0 (was the hard <5.0dev pin) - google-cloud-firestore: 2.17.0 -> 2.20.0 (2.18+ accepts protobuf 5) Verified with docker test image rebuild + unit subset all green; protobuf 5.29.6, grpcio 1.66.0, firestore 2.20.0 confirmed installed.
starlette: 0.37.2 -> 0.40.0 fixes CVE-2024-47874 (HIGH 7.5): unbounded multipart parts could exhaust process memory. fastapi: 0.112.0 -> 0.118.0 cascade — fastapi 0.112 pinned starlette <0.38, so the smallest fastapi that allows starlette 0.40 is 0.118 (fastapi 0.118+ accepts starlette >=0.40,<0.49). Verified with docker test image rebuild + unit subset all green; fastapi 0.118.0 and starlette 0.40.0 confirmed installed.
Both are transitive build-tool deps not imported by app code, but they ship inside /opt/venv (and pip's vendored libs) and so surface in the Artifact Registry scan against the runtime image. Pinning them here forces the patched versions into the venv: - jaraco.context: 5.x (transitive) -> 6.1.0 — CVE-2026-23949 (HIGH 8.6) - wheel: 0.45.1 (transitive) -> 0.46.2 — CVE-2024-24049 (HIGH 5.5) Verified with docker test image rebuild + unit subset all green; both packages confirmed installed at the patched versions.
morpheus review — omi#7127Verdict: Approved ✅ 5 atomic commits, single file ( Checks runLeaks: N/A — no code changes. Correctness — cascade verification:
Risk notes (not blocking):
Scope: Langchain ecosystem (0.3→1.x + langgraph-checkpoint 2→3) correctly deferred to a 3rd PR with API migration. Net with PR #7126: 1 CRITICAL + 23 HIGH closed across 15 atomic commits. No blockers. |
Greptile SummaryThis PR bumps 7 backend Python dependencies to close 7 HIGH CVEs in the deployed image, with associated cascade updates for Confidence Score: 4/5Safe to merge with low risk; all cascade deps have been verified compatible with their headline bumps, and the two P2 observations are non-blocking. All seven CVE-targeted bumps land on package versions that were specifically patched for the relevant compatibility concerns. The only issues found are P2: wheel as a runtime dep and the FastAPI 0.118 yield/StreamingResponse lifecycle change. No P0 or P1 findings. backend/requirements.txt — review the wheel runtime pin and validate StreamingResponse+yield endpoints against the FastAPI 0.118 behavior change. Important Files Changed
Reviews (1): Last reviewed commit: "Pin jaraco.context + wheel for CVE-2026-..." | Re-trigger Greptile |
| # CVE-2024-24049). Not imported by app code; ship in the venv via setuptools. | ||
| jaraco.context==6.1.0 | ||
| wheel==0.46.2 |
There was a problem hiding this comment.
Build tool pinned as runtime dependency
wheel is a build-time package. Pinning it in requirements.txt installs it into every production venv/container image, which is the standard vector for "build tool in prod" supply-chain risk. The comment acknowledges it isn't imported by app code; a cleaner way to satisfy scanner findings without polluting the runtime venv is to add it to a separate requirements-build.txt (or a pip install wheel==0.46.2 step in the Dockerfile before the main pip install -r requirements.txt), so the pin is visible to the scanner while staying out of the application layer.
| executing==2.0.1 | ||
| fal_client==0.4.1 | ||
| fastapi==0.112.0 | ||
| fastapi==0.118.0 |
There was a problem hiding this comment.
FastAPI 0.118 changes yield-dependency cleanup timing for StreamingResponse
FastAPI 0.118.0 reverted a prior behavior change so that exit code in yield-based dependencies now runs after the full response is sent (including streamed bytes), rather than before. The backend has StreamingResponse returns in chat.py, tts.py, sync.py, and mcp_sse.py. For those endpoints, any Depends-injected generator that performs cleanup (e.g. closing a resource, releasing a lock) will now complete later in the request lifecycle than it did under 0.112.0. For streaming endpoints this is the correct and intended behavior (the generator can still supply data to the stream), but the team should verify that no endpoint assumes cleanup completes before the HTTP response is flushed to the client.
Changelog audit — major bumps in this PRPer the request to enumerate breaking/removed/deprecated items per upstream changelog (no codebase grep). Verdicts below are upstream-surface judgement only — applicability to this repo still needs eyes on review. cryptography 43.0.0 → 46.0.5 (three majors)Removed
Breaking
Deprecated (future removal)
Verdict: NEEDS REVIEW if backend uses x509 verification, custom pillow 10.4.0 → 12.2.0 (two majors)Removed
Breaking default change
Deprecated (future removal)
Verdict: NEEDS REVIEW for any I;16 image resampling (output will differ), ImageCms color management, JPEG huffman customisation, or BGR mode handling. Standard JPEG/PNG/WebP open/save/resize is unaffected. protobuf 4.25.8 → 5.29.6 (one major; upstream
|
| Package | Verdict |
|---|---|
| cryptography 43→46 | NEEDS REVIEW (x509 / cert verifier APIs) |
| pillow 10→12 | NEEDS REVIEW (I;16 resampling, ImageCms, BGR modes) |
| protobuf 4→5 | NEEDS REVIEW (UnknownFields, RegisterExtension, JSON options) |
| starlette 0.37→0.40 | NEEDS REVIEW (multipart 1 MB part cap is the CVE fix) |
| fastapi 0.112→0.118 | MODERATE (yield-exit timing change) |
| cffi 1→2 | LOW |
| grpcio 1.62→1.66 | LOW |
| firestore 2.17→2.20 | LOW |
| streamlit/altair/narwhals | LOW (ops-script only) |
| jaraco.context, wheel | LOW (build-tool transitive) |
The 5 NEEDS REVIEW / MODERATE items are the ones to spot-check before merge — if morpheus or you want, I can do the codebase grep next and post hit-by-hit results.
starlette 0.40 (which we just bumped to in the prior commit for CVE-2024-47874) added a default 1 MB cap per multipart form part. This breaks every backend endpoint that accepts file uploads larger than 1 MB: - /v2/voice-message/transcribe (chat.py: PCM up to 200 MB) - /v2/files (chat.py: arbitrary chat attachments) - /v1/users/profile (speech_profile.py: voice samples) - /v2/sync (sync.py: audio batches) - /v1/apps + /v1/personas + /thumbnail (apps.py: image uploads) - /v1/imports (imports.py: import payloads) - /v1/calls (phone_calls.py: form_data) Match the existing _MAX_PCM_BODY_BYTES ceiling (200 MB) by setting MultiPartParser.max_part_size at startup. starlette 0.45+ added a per-request override; once we move to that version we can plumb the limit through Request.form() per-route instead.
Codebase grep audit — follow-up to the changelog auditYou were right to push back. Did the actual grep this round; results below. cryptography 43→46 — CLEAR (with one fix already in)
pillow 10→12 — CLEAR
protobuf 4→5 — CLEAR
starlette 0.37→0.40 — HIT — fixed in 25d89c6The new 1 MB default
Pushed a follow-up commit (25d89c6) that monkey-patches fastapi 0.112→0.118 — CLEAR
Cascades (streamlit/altair/narwhals/grpcio/firestore/cffi/jaraco.context/wheel)Already classed as LOW above. No code-level grep needed: streamlit only used in Net for this PR
|
Per Greptile P2 — pinning build-time tools as runtime deps mixes concerns. Restructure so the upgrade happens once in the builder stage (alongside pip / setuptools) rather than appearing as application-level pins: - Dockerfile: 'pip install --upgrade pip setuptools wheel jaraco.context' before the main 'pip install -r requirements.txt'. Patched versions (wheel >=0.46.2, jaraco.context >=6.1) land in /opt/venv and clear the scanner findings the same way the explicit pins did. - requirements.txt: explicit pins removed. Verified: image rebuild via Dockerfile.test still green; wheel 0.47.0 and jaraco.context 6.1.2 confirmed in the resulting venv.
|
Greptile P2 #1 (wheel as runtime dep) — addressed in ffd266e. Moved Greptile P2 #2 (fastapi 0.118 yield-dependency timing for StreamingResponse) — already covered in the codebase grep audit (#7127 (comment)): zero |
…kerfile" This reverts commit ffd266e.
wheel is purely a packaging tool — pip uses it to install sdists at build time. The deployed uvicorn process never imports wheel, so the ReDoS in wheel filename parsing (CVE-2024-24049) has no exploit path in our runtime image. Pinning it adds a build-only tool to the application requirements list with no actual runtime benefit. The scanner will still flag wheel in /opt/venv (because pip vendors it) — accepting that finding as noise rather than chasing it with a top-level pin. jaraco.context pin retained — it's a regular Python library that can be loaded transitively at runtime via pkg tooling, so the pin actually clears the scan finding for code that could conceivably execute.
|
Dropped the wheel pin in eaaae56. wheel is purely a build-time tool (pip uses it for sdist installs) — the deployed runtime never imports it, so CVE-2024-24049 (ReDoS in wheel filename parsing) has no exploit surface in the running image. The scanner finding for wheel will persist after this PR but it's noise. Kept the jaraco.context pin since that one is a regular library that can be loaded transitively by pkg/setuptools tooling — the pin actually clears a runtime-reachable code path. |
Same reasoning as the wheel pin removal: jaraco.context is a build-time helper used by setuptools tooling. Its CVE-2026-23949 (path traversal in resource walking helpers) only triggers if the runtime app calls jaraco.context APIs against attacker-controlled paths. We don't import jaraco.context anywhere in app code. The scanner finding will persist after this PR — accepted as noise rather than carrying a build-tool pin in the application requirements list.
|
Dropped jaraco.context too in 0951a98 per the call. PR is now down to 6 commits closing 6 HIGH CVEs that are all in actual runtime code paths (cryptography/pillow/protobuf/cffi/starlette/fastapi). Scanner findings for wheel + jaraco.context will persist after this lands but neither has an exploit path in our deployed image. Net for this PR: 6 HIGH CVEs closed (was 7 with the build-tool pins). Combined with PR #7126: 1 CRITICAL + 22 HIGH closed, all in code we actually execute. |
Summary
5 atomic commits, each a major (or multi-major) bump of a backend Python dep that closes Artifact Registry HIGH CVEs against the deployed image (
gcr.io/based-hardware/backend@sha256:a0ef24d4…). Built and tested behind PR #7126 (the safe-bumps PR); this PR is the major-version cascade work split out as requested.Net: 7 HIGH CVEs closed in this PR.
Cascade summary
All cascade bumps held to the minimum version that resolves the conflict — no opportunistic-latest moves.
Deferred
The langchain ecosystem (langchain-core 0.3 → 1.x + langgraph-checkpoint 2 → 3) is going into its own follow-up PR. Those bumps require API migration in
backend/utils/llm/andbackend/utils/retrieval/— too invasive to bundle here.Test plan
For each commit:
requirements.txtDockerfile.test(Python 3.11-slim, requirements.txt minus the source-builtlc3py) — only the pip install layer rebuildsAll 5 commits individually green. Each cascade was discovered by attempting the headline bump, reading the resolver error, and pinning the smallest version that resolves it.
Verifying after deploy
/health, an auth route, and an LLM-powered route to surface any runtime API regressions in fastapi/starlette/grpc.