[v3-2-test] Require starlette>=1.0.1 for Host header parsing fix (#67326)#67460
Merged
Conversation
potiuk
approved these changes
May 25, 2026
amoghrajesh
approved these changes
May 25, 2026
gopidesupavan
approved these changes
May 25, 2026
This was referenced May 25, 2026
Merged
* Require starlette>=1.0.1 to fix Host-header path divergence 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. * Auto-honour and retire per-package exclude-newer overrides in upgrade 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. (cherry picked from commit 518eadf)
6f33b31 to
85f0aa5
Compare
vatsrahul1001
added a commit
that referenced
this pull request
May 25, 2026
* Require starlette>=1.0.1 to fix Host-header path divergence 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. * Auto-honour and retire per-package exclude-newer overrides in upgrade 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. (cherry picked from commit 518eadf) Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
vatsrahul1001
added a commit
that referenced
this pull request
May 25, 2026
* Require starlette>=1.0.1 to fix Host-header path divergence 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. * Auto-honour and retire per-package exclude-newer overrides in upgrade 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. (cherry picked from commit 518eadf) Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
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.
Manual backport of #67326 — auto-backport failed (https://github.com/apache/airflow/actions/runs/26391687063).
Why auto-backport failed
Conflicts in
pyproject.tomlanduv.lockaround the[tool.uv.exclude-newer-package]cooldown overrides block:v3-2-teststill has the staleuv = "12 hours"override (REMOVE BY 2026-05-01 — past).Resolution
Resolved by dropping the stale
uv = "12 hours"override (it's past itsREMOVE BYdate anyway) and keeping the newstarlette = "6 hours"override that this PR adds. The drop effectively also brings #67383's cleanup intov3-2-test, so a separate backport of #67383 is not needed.What this PR ships
starlette>=1.0.1floor bump — Host-header parsing fix from Ignore malformedHostheader when constructingrequest.urlKludex/starlette#3279.cadwyn>=6.1.1floor bump — required because cadwyn 6.0.4 is incompatible with starlette 1.0.x (per cadwyn's own release notes: "Fix compatibility with starlette==1.0.0"). Without this,test_access_api_contractfails on LowestDeps withTypeError: unhashable type: 'dict'in jinja2 via cadwyn's swagger dashboard.starlette = "6 hours"uv cooldown override so the floor bump can resolve before the global 4-day cooldown.scripts/ci/prek/upgrade_important_versions.pyenhancements (auto-honor & retire per-package overrides).Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code (Opus 4.7) following the guidelines