This is an automated email from the ASF dual-hosted git repository.
vatsrahul1001 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new d8865dd4249 Fix log server path extraction to use removeprefix (#66749)
d8865dd4249 is described below
commit d8865dd42499d1ded98640ddf9d88f37a8209904
Author: Jarek Potiuk <[email protected]>
AuthorDate: Tue May 12 16:43:17 2026 +0200
Fix log server path extraction to use removeprefix (#66749)
The log server uses request.url.path.lstrip("/log/") to extract the
requested filename from the URL path. str.lstrip() strips any
combination of the argument characters (here {/, l, o, g}) from the
left of the string -- it does not remove the literal prefix "/log/".
This is a documented Python pitfall.
Switch to str.removeprefix("/log/") (Python 3.9+, already required by
Airflow) so the filename extracted for JWT validation matches the one
the underlying Starlette StaticFiles mount uses to locate the file on
disk.
Generated-by: Claude Opus 4.7 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
---
airflow-core/src/airflow/utils/serve_logs/log_server.py | 2 +-
airflow-core/tests/unit/utils/test_serve_logs.py | 13 +++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/airflow-core/src/airflow/utils/serve_logs/log_server.py
b/airflow-core/src/airflow/utils/serve_logs/log_server.py
index 292d7bf5b16..92178bf5a24 100644
--- a/airflow-core/src/airflow/utils/serve_logs/log_server.py
+++ b/airflow-core/src/airflow/utils/serve_logs/log_server.py
@@ -67,7 +67,7 @@ class JWTAuthStaticFiles(StaticFiles):
payload = await signer.avalidated_claims(auth)
token_filename = payload.get("filename")
# Extract filename from url path
- request_filename = request.url.path.lstrip("/log/")
+ request_filename = request.url.path.removeprefix("/log/")
if token_filename is None:
logger.warning("The payload does not contain 'filename' key:
%s.", payload)
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
diff --git a/airflow-core/tests/unit/utils/test_serve_logs.py
b/airflow-core/tests/unit/utils/test_serve_logs.py
index b6c7805386a..ebd8d602eb4 100644
--- a/airflow-core/tests/unit/utils/test_serve_logs.py
+++ b/airflow-core/tests/unit/utils/test_serve_logs.py
@@ -124,6 +124,19 @@ class TestServeLogs:
)
assert response.status_code == 403
+ def test_forbidden_lstrip_character_overlap(self, client: TestClient,
jwt_generator):
+ # The request path and the JWT filename intersect on the set {/, l, o,
g}:
+ # str.lstrip("/log/") on "/log/log_sample.log" returns "_sample.log",
+ # which would have matched the JWT, but StaticFiles serves
"log_sample.log".
+ # removeprefix preserves the literal prefix so the two paths agree.
+ response = client.get(
+ "/log/log_sample.log",
+ headers={
+ "Authorization": jwt_generator.generate({"filename":
"_sample.log"}),
+ },
+ )
+ assert response.status_code == 403
+
def test_forbidden_expired(self, client: TestClient, jwt_generator):
with time_machine.travel("2010-01-14"):
token = jwt_generator.generate({"filename": "sample.log"})