CVE-2026-46763LowCVE withdrawn; fixed in Airflow 3.3.03 min read
A missing ti:self scope let any Airflow worker read another task instance's reschedule time
Airflow's Execution API pins each worker to its own task instance with a ti:self JWT scope, but GET /execution/task-reschedules/{ti}/start_date shipped without that check. Any authenticated worker could read the first reschedule timestamp of any task instance by passing its UUID. Fixed in Airflow 3.3.0.
- Vendor
- Apache Airflow
- Product
- Airflow Execution API
- Weakness
- CWE-862 / CWE-639
- Affected
- Before 3.3.0
- Fixed in
- 3.3.0
Disclosure timeline
May 12, 2026
Reported privately: the task-reschedules start_date route was missing the ti:self scope its sibling routes enforce.
Jun 2026
Fix merged as apache/airflow#67628, assigned to the 3.3.0 milestone. The PR credits the report by name.
Jun 2026
CVE-2026-46763 was assigned, then withdrawn after a low-impact review. The hardening still shipped in 3.3.0.
Airflow 3 splits its internal API in two. The public REST API is what users and the UI talk to. The Execution API is the narrow interface running workers use to talk back to the scheduler: report task state, fetch connections, record reschedule times, and so on. Because a worker only ever acts on its own task instance, the Execution API authenticates each worker with a short-lived JWT whose scope is pinned to a single task instance. Routes that read or write per-task-instance data require the ti:self scope, which ties the caller to the specific task instance named in its token instead of letting it address any task instance in the deployment.
That check is applied route by route. Three of the task-instance routers already declared it: task_instances.py, hitl.py, and task_state.py. One did not.
The bug
GET /execution/task-reschedules/{task_instance_id}/start_date returns the first reschedule time recorded for a task instance. The route read task_instance_id straight from the URL path but never enforced ti:self, so it trusted any valid Execution API token regardless of which task instance that token was minted for. Any authenticated worker could read the first reschedule timestamp of any task instance in the deployment by putting that instance's UUID in the path.
The fix
apache/airflow#67628 adds the same guard the sibling routers already use, declared on the task-reschedules router:
dependencies=[Security(require_auth, scopes=["ti:self"])]
With the scope enforced, a worker's token only resolves for its own task instance, and a request for someone else's UUID is rejected. The PR also adds a structural test that walks every Execution API route carrying a {task_instance_id} path parameter and asserts it either enforces ti:self or is named on an explicit allowlist, so a future route cannot quietly ship without the check the way this one did. The change is in Airflow 3.3.0 and is listed in the release notes.
Impact
This is a low-severity finding, and it is worth being plain about why. The caller already has to hold a valid Execution API token, so this is not an unauthenticated hole. What leaks is a single value, the first reschedule start_date, for a task instance the caller was not scoped to. There is no write path and no credential exposure. The real value in the fix is closing a gap in an otherwise consistent authorization model: every neighboring route pinned the caller to its own task instance, and this one silently did not, which is exactly the drift the added structural test is meant to catch.
On the CVE
A CVE, CVE-2026-46763, was assigned for this and then withdrawn after a low-impact review. That is the right call. The scope enforcement was worth adding and shipped in 3.3.0, but the practical impact did not clear the bar for a tracked vulnerability. I am noting the withdrawal here rather than leaving a dangling identifier pointing at nothing.
If you run Airflow
Upgrade to 3.3.0 or later. There is nothing to rotate, since no secret was exposed. If you build against the Execution API, the durable takeaway is the structural test: authorization applied route by route drifts unless something mechanically checks that every route in a family carries the same guard.