Refresh the inbound mail service's pinned dependencies

Python · Python · intermediate · modification

Our pins had not moved since the late-2024 compile, and PyPI's advisory data now lists 19 GitHub advisories against them (python-multipart 7, Starlette 7, anyio 2, h11, idna, certifi). This recompiles with the usual 14-day cooldown and puts floors in `requirements.in` for the ones that touch the webhook. The bump is large: Starlette 0.41 to 1.7 crosses 1.0, which removed `on_startup`/`on_shutdown`, so `app.py` moves the support-API client into a `lifespan` context manager (same base URL, token header and timeout, closed when the app shuts down). h11 goes to 0.16, the fix for GHSA-vqfr-h8mv-ghfj (it parses every request uvicorn serves us, so it gets a floor too), with httpcore 1.0.9, the first httpcore that accepts it. sniffio drops out, since neither anyio 4.15 nor httpx 0.28 depends on it any more; typing-extensions comes in through anyio and Starlette. certifi is the one exception to the cooldown: 2026.10.19 is four days old, but GHSA-6q48-6c7w-9gw2 (critical, published 2026-10-20) is about compromised intermediates under a root in the bundle our support-API client trusts, and 2026.10.19 is the fixed release. The exception is scoped to certifi and dated 2026-10-21, so it takes the fix and nothing newer. The suite passes on the new pins. `TestClient` now warns that using it with `httpx` is deprecated in favour of `httpx2`; I'll do that switch in the dev requirements as a separate PR.

A small Starlette service on the public internet: the mail provider posts each inbound support email to `POST /inbound` as a form, `inbound/webhook.py` (unchanged) reads it with `request.form(max_files=0, max_fields=40)`, checks the provider's HMAC signature and timestamp, and creates a ticket through `request.app.state.http`, the one outbound client (TLS to the support API's ASCII hostname, httpx's default verification against certifi's bundle). `GET /healthz` answers 204. The image is `python:3.12-slim` on Linux, built fresh by CI from `requirements.txt` on every merge and run with `uvicorn inbound.app:app` (plain `uvicorn`, no httptools, so h11 parses every request). Dev tools are in `requirements-dev.txt` (pytest only, compiled the same way, no package shared with the runtime set), unchanged here. The 14-day cooldown and its per-package exception rule are the team's written dependency policy.

Requirements

Files touched

--- requirements.in
 # Runtime dependencies of the inbound mail service.
 # Recompile requirements.txt after editing (see the command in its header).
-starlette
+starlette>=1.3.1
 uvicorn
-python-multipart
+python-multipart>=0.0.31
 httpx
+# Transitive (via uvicorn), floor for GHSA-vqfr-h8mv-ghfj (malformed chunked bodies, request smuggling).
+h11>=0.16.0
+# Transitive (via httpx), floor for GHSA-6q48-6c7w-9gw2 (compromised emSign intermediates).
+certifi>=2026.10.19
 
--- requirements.txt
 # This file was autogenerated by uv via the following command:
-#    uv pip compile requirements.in --generate-hashes --python-version 3.12 --exclude-newer 2024-10-18T00:00:00Z -o requirements.txt
-anyio==4.6.2.post1 \
-    --hash=sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c \
-    --hash=sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d
+#    uv pip compile requirements.in --generate-hashes --python-version 3.12 --exclude-newer 2026-10-10T00:00:00Z --exclude-newer-package certifi=2026-10-21T00:00:00Z -o requirements.txt
+anyio==4.15.1 \
+    --hash=sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101 \
+    --hash=sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94
     # via
     #   httpx
     #   starlette
-certifi==2024.8.30 \
-    --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \
-    --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9

Review this PR

Python practice