requires_access_backfill resolves the Dag for the authorization decision from the backfill_id path parameter. When that id is present and parses, but no Backfill row matches, dag_id stays None and the dependency falls through to reading dag_id from the request body — so the decision is made against a Dag the request names rather than the backfill in the path.
This is not exploitable for a cross-Dag action: the handler answers 404 before acting, so nothing is done to another Dag's backfill. What it does leak is existence. A caller can tell "no such backfill" (404) apart from "backfill exists on a Dag you cannot see" (403), which is an existence oracle over backfill ids across Dags.
Why the fallback is there
requires_access_backfill is not only on /{backfill_id} routes — it also guards GET /backfills, POST /backfills and POST /backfills/dry_run, where there is no backfill_id at all and the Dag legitimately comes from the body. Any fix has to keep those working.
Suggested shape
Distinguish "no backfill_id in the path" from "backfill_id given but not found". The body fallback should apply only to the first. In the second the dependency should decide without consulting the body, and the response for an unknown backfill should be indistinguishable from the unauthorized one.
Acceptance criteria
- A request with a well-formed but unknown
backfill_id never has its authorization decided by a body-supplied dag_id.
- An unauthorized caller cannot distinguish an unknown backfill id from one on a Dag they cannot access.
- The three body-based routes above keep authorizing off the body.
Raised in review of #70889, which deliberately scoped itself to the parser divergence: #70889 (comment)
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
requires_access_backfillresolves the Dag for the authorization decision from thebackfill_idpath parameter. When that id is present and parses, but noBackfillrow matches,dag_idstaysNoneand the dependency falls through to readingdag_idfrom the request body — so the decision is made against a Dag the request names rather than the backfill in the path.This is not exploitable for a cross-Dag action: the handler answers 404 before acting, so nothing is done to another Dag's backfill. What it does leak is existence. A caller can tell "no such backfill" (404) apart from "backfill exists on a Dag you cannot see" (403), which is an existence oracle over backfill ids across Dags.
Why the fallback is there
requires_access_backfillis not only on/{backfill_id}routes — it also guardsGET /backfills,POST /backfillsandPOST /backfills/dry_run, where there is nobackfill_idat all and the Dag legitimately comes from the body. Any fix has to keep those working.Suggested shape
Distinguish "no
backfill_idin the path" from "backfill_idgiven but not found". The body fallback should apply only to the first. In the second the dependency should decide without consulting the body, and the response for an unknown backfill should be indistinguishable from the unauthorized one.Acceptance criteria
backfill_idnever has its authorization decided by a body-supplieddag_id.Raised in review of #70889, which deliberately scoped itself to the parser divergence: #70889 (comment)
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting