VYPR
Unrated severityNVD Advisory· Published Jun 1, 2026

CVE-2026-45426

CVE-2026-45426

Description

Apache Airflow log server incorrectly uses str.lstrip() for JWT authorization, allowing authenticated workers to read logs of other Dags with similar names.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Apache Airflow log server incorrectly uses str.lstrip() for JWT authorization, allowing authenticated workers to read logs of other Dags with similar names.

Vulnerability

Apache Airflow versions 3.0.0 before 3.2.2 contain an authorization bypass in the log server. The log server uses Python's str.lstrip("/log/") to extract the requested path segment when verifying a JWT's sub claim. str.lstrip() strips any combination of the characters {/, l, o, g} from the left, not the literal prefix "/log/". This allows a JWT issued for one Dag to authorize access to logs of other Dags whose names begin with a subset of those characters (e.g., dag_a grants access to dag_attacker, aaaa_target, _dag_secret). Affected deployments are those relying on per-Dag log-access scoping, such as multi-team or shared-executor topologies [1][2].

Exploitation

An attacker must be an authenticated Airflow worker holding a valid Log-server JWT for at least one Dag. With that JWT, the attacker can request log files for any other Dag whose name shares a character prefix with the authorized Dag. For example, a JWT for dag_a can be used to request logs for dag_attacker because str.lstrip() strips d, a, g, _ characters. No additional network position or user interaction is required; the attacker simply sends a crafted request to the log server [1][2].

Impact

A successful attacker can enumerate and read worker logs of other Dags, including task output and error traces. This breaks the per-Dag isolation boundary, leaking sensitive information from other workflows. The attacker gains unauthorized read access to log data at the same privilege level as the worker, with no escalation to other systems [2].

Mitigation

Users should upgrade to apache-airflow version 3.2.2 or later, which fixes the issue by replacing str.lstrip() with str.removeprefix("/log/") (Python 3.9+). The fix was merged in pull request #66749 and is included in the 3.2.2 release [1][2]. No workarounds are available for earlier versions; upgrading is the only mitigation.

AI Insight generated on Jun 1, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

2
d8865dd42499

Fix log server path extraction to use removeprefix (#66749)

https://github.com/apache/airflowJarek PotiukMay 12, 2026via body-scan
2 files changed · +14 1
  • airflow-core/src/airflow/utils/serve_logs/log_server.py+1 1 modified
    @@ -67,7 +67,7 @@ async def validate_jwt_token(self, request: Request):
                 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)
    
  • airflow-core/tests/unit/utils/test_serve_logs.py+13 0 modified
    @@ -124,6 +124,19 @@ def test_forbidden_different_logname(self, client: TestClient, jwt_generator):
             )
             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"})
    
cde4885818be

Updating release notes for 3.2.2rc3

https://github.com/apache/airflowvatsrahul1001May 26, 2026Fixed in 3.2.2via release-tag
2 files changed · +5 4
  • RELEASE_NOTES.rst+3 2 modified
    @@ -24,7 +24,7 @@
     
     .. towncrier release notes start
     
    -Airflow 3.2.2 (2026-05-27)
    +Airflow 3.2.2 (2026-05-29)
     --------------------------
     
     Significant Changes
    @@ -81,7 +81,8 @@ Significant Changes
     
     Bug Fixes
     ^^^^^^^^^
    -
    +- Fix ``Callback.handle_event`` triggerer crash when OpenTelemetry metrics receive dict typed tag values (#67527) (#67529)
    +- UI: Rewrite ``modulepreload hrefs`` to the api-server static path (#67548) (#67556)
     - Correctly pre-allocate ``external_executor_id`` with multiple executors on PostgreSQL (#67388) (#67458)
     - Return raw import-error stacktrace when a Dag file has no registered Dag (#67465) (#67478)
     - UI: Fix Expand/Collapse All on XComs and Audit Log JSON cells (#67316) (#67361)
    
  • reproducible_build.yaml+2 2 modified
    @@ -1,2 +1,2 @@
    -release-notes-hash: 6407b48d1054fe3ce68c09bf4435d91d
    -source-date-epoch: 1779745327
    +release-notes-hash: 504288db9a9dc13a0db859232fab98d0
    +source-date-epoch: 1779811737
    

Vulnerability mechanics

No source-code context for this CVE — mechanics is only generated when we can read the actual fix diff. Without that, the four sections (root cause, attack vector, affected code, fix) would be speculation rather than analysis.

References

3

News mentions

0

No linked articles in our index yet.