Require starlette>=1.0.1 for Host header parsing fix#67326
Conversation
|
Quickest fix: git fetch upstream main && git rebase upstream/main
rm uv.lock && uv lock
git add uv.lock && git rebase --continue
git push --force-with-leaseAutomated nudge — ignore if you're not ready to rebase. This comment is updated in place on future |
6b5f873 to
c0dd9ee
Compare
|
@potiuk @jscheffl I had to rebase as there was conflict in pyproject.toml after #67383 , Also noticed CI was breaking CI fix — bumped httpx floor: |
|
Nice! thanks ! - I was just looking :) |
Starlette 1.0.1 carries a Host-header parsing fix (Kludex/starlette#3279): when the `Host` header contains characters that are invalid per RFC 9110 §7.2 (`/`, `?`, `#`, `@`, `\`, spaces, ...), the URL string Starlette builds before calling `urlsplit` would push parts of `scope["path"]` into the netloc / query / fragment, leaving `request.url.path` disagreeing with the ASGI `scope["path"]` that downstream apps and `StaticFiles` actually serve. Airflow has two places that authorise off `request.url.path` and dispatch off `scope["path"]`: - `airflow-core/src/airflow/utils/serve_logs/log_server.py` — `JWTAuthStaticFiles.validate_jwt_token` compares `request.url.path` against the JWT's `filename` claim; the `StaticFiles` superclass then serves the file at `scope["path"]`. A malformed `Host` header makes those two disagree, letting a holder of any valid log-read token read any other task log on the same worker. - `providers/edge3/src/airflow/providers/edge3/worker_api/auth.py` — `jwt_token_authorization_rest` derives the called "method" from `request.url.path` while FastAPI routes by `scope["path"]`. Same shape of bypass on the Edge3 worker control plane. Bumping the floor to 1.0.1 closes both. A matching `[tool.uv.exclude-newer-package]` override is added so the security floor can be resolved before 1.0.1 ages past the project's global 4-day cooldown — the next commit teaches `upgrade_important_versions.py` to retire that override automatically once the cooldown catches up.
… script `upgrade_important_versions.py` enforced its own 4-day PyPI cooldown (`COOLDOWN_DAYS = 4`), which mirrored the root pyproject.toml's global `exclude-newer = "4 days"`. When a per-package override was added under `[tool.uv.exclude-newer-package]` (e.g. `uv = "12 hours"`) to let a freshly-published release through the global window, the script kept applying its broader cooldown and would pick a stale version that disagreed with what `uv lock` would resolve against pyproject.toml. This change makes the script: 1. Parse manual override blocks (the lines after the "# End of automatically generated …" sentinels under `[tool.uv.exclude-newer-package]` and `[tool.uv.pip.exclude-newer-package]`) and use any duration-shaped override as the per-package cooldown when checking PyPI. 2. Sweep up overrides whose target package is already older than the global 4-day window — the entry, plus its `# REMOVE BY …` markers, are removed from pyproject.toml so the workaround retires itself without anyone having to remember the calendar date in the comment. The "Manual overrides" header and broader context comments are left in place on purpose — the diff makes them obviously orphaned for a reviewer to prune in the same PR, but the script doesn't try to guess which surrounding lines belonged to which entry.
c0dd9ee to
6991fe8
Compare
|
Pervious understating of bumping httpx was not right. CI was still failling, Below seems correct Actual CI fix: cadwyn>=6.1.1 Bumped cc: @potiuk |
Backport failed to create: v3-2-test. View the failure log Run detailsNote: As of Merging PRs targeted for Airflow 3.X In matter of doubt please ask in #release-management Slack channel.
You can attempt to backport this manually by running: cherry_picker 518eadf v3-2-testThis should apply the commit to the v3-2-test branch and leave the commit in conflict state marking After you have resolved the conflicts, you can continue the backport process by running: cherry_picker --continueIf you don't have cherry-picker installed, see the installation guide. |
Summary
starlettefloor inairflow-core/pyproject.tomlfrom>=0.45.0to>=1.0.1to pick up the Host-header parsing fix inKludex/starlette#3279.
[tool.uv.exclude-newer-package]override(
starlette = "6 hours") so the bump can resolve before 1.0.1ages past the project's global 4-day cooldown.
scripts/ci/prek/upgrade_important_versions.pyto honourmanual cooldown overrides when checking PyPI and to retire those
overrides automatically once the global window catches up, so each
workaround line — and its
# REMOVE BY …marker — clean themselvesout without anyone watching the calendar.
Why this matters for Airflow
The upstream PR closes a case where
request.url.pathcould divergefrom the ASGI
scope["path"]when theHostheader containscharacters that are invalid per RFC 9110 §7.2 (
/,?,#,@,\, space). Airflow has authorisation paths that compare againstrequest.url.pathwhile the downstream app serves the file atscope["path"]:airflow-core/src/airflow/utils/serve_logs/log_server.py(
JWTAuthStaticFiles)providers/edge3/src/airflow/providers/edge3/worker_api/auth.py(
jwt_token_authorization_rest)Bumping the floor closes the underlying divergence. Defence-in-depth
follow-ups (comparing
scope["path"]directly instead ofrequest.url.path) can come in a separate PR.Test plan
uv run --project scripts pytest scripts/tests/ci/prek/test_upgrade_important_versions.py— 19 passed (5 existing + 14 new covering_parse_duration_hours,_parse_manual_overrides,_remove_override_entry, and the per-package cooldown branch).prek run --files …on every touched file — all hooks pass (ruff, ruff-format, mypy-scripts, license headers, codespell, uv.lock sync, …).uv lockresolvesstarlette==1.0.1once the per-package override is in place; verified by diffinguv.lock.Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code (Opus 4.7) following the guidelines
Drafted-by: Claude Code (Opus 4.7); reviewed by @potiuk before posting