Skip to content

Authorize POST /assets/events on the asset named in the body - #73007

Merged
vincbeck merged 2 commits into
apache:mainfrom
henry3260:fix-create-asset-event-authz
Sep 14, 2026
Merged

vincbeck merged 2 commits into
apache:mainfrom
henry3260:fix-create-asset-event-authz

Conversation

@henry3260

Copy link
Copy Markdown
Contributor

Why

POST /assets/events is guarded by requires_access_asset(method="POST"), but that dependency reads the asset id from the URL path. This endpoint carries the id in the request body, so the dependency runs before the body is parsed and passes an empty AssetDetails(id=None) to the auth manager. The only question the auth manager can answer is whether the caller may post to any asset at all.

An auth manager that scopes assets by id, name, or uri, the granularity added in #72682, therefore cannot deny an event for an asset the caller may not touch, and the response still returns that asset's name and uri. The built-in Simple and FAB auth managers ignore details, so they are unaffected. POST /assets/{asset_id}/materialize is also unaffected because its id is in the path.

What

  • airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py: after create_asset_event resolves the AssetModel from the body, it calls is_authorized_asset with the full AssetDetails(id, name, uri) and raises 403 when denied. This mirrors the explicit second authorization materialize_asset performs on the Dag it resolves. The dependency-level check stays in place.

Was generative AI tooling used to co-author this PR?
  • Yes — Claude Code (Fable 5.1)

The route dependency reads the asset id from the path, but this endpoint
carries it in the request body, so the auth manager was only asked whether
the caller may post to any asset at all. An auth manager that scopes assets
by id, name, or uri could not deny an event for an asset the caller may not
touch, and the response still returned that asset's name and uri.
Comment thread airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py Outdated
Checking the asset both in the dependency and in the route authorized the same
request twice, and dropping the dependency would have made the generated
permission docs list the endpoint as public. Reading the asset id from the
validated body in the dependency keeps a single per-asset check that the docs
still see.
@vincbeck
vincbeck merged commit 9636fcb into apache:main Sep 14, 2026
79 checks passed
@github-actions github-actions Bot added this to the Airflow 3.3.2 milestone Sep 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi maintainer, this PR was merged without a milestone set.
We've automatically set the milestone to Airflow 3.3.2 based on: backport label targeting v3-3-test
If this milestone is not correct, please update it to the appropriate milestone.

This comment was generated by Milestone Tag Assistant.

@github-actions

Copy link
Copy Markdown
Contributor

Backport failed to create: v3-3-test. View the failure log Run details

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

Status Branch Result
❌ v3-3-test Commit Link

You can attempt to backport this manually by running:

cherry_picker 9636fcb v3-3-test

This should apply the commit to the v3-3-test branch and leave the commit in conflict state marking
the files that need manual conflict resolution.

After you have resolved the conflicts, you can continue the backport process by running:

cherry_picker --continue

If you don't have cherry-picker installed, see the installation guide.

vincbeck added a commit that referenced this pull request Sep 14, 2026
…#73139)

The route dependency reads the asset id from the path, but this endpoint
carries it in the request body, so the auth manager was only asked whether
the caller may post to any asset at all. An auth manager that scopes assets
by id, name, or uri could not deny an event for an asset the caller may not
touch, and the response still returned that asset's name and uri.

Co-authored-by: Henry Chen <henrychen@apache.org>
vatsrahul1001 pushed a commit that referenced this pull request Sep 18, 2026
…#73139)

The route dependency reads the asset id from the path, but this endpoint
carries it in the request body, so the auth manager was only asked whether
the caller may post to any asset at all. An auth manager that scopes assets
by id, name, or uri could not deny an event for an asset the caller may not
touch, and the response still returned that asset's name and uri.

Co-authored-by: Henry Chen <henrychen@apache.org>
vatsrahul1001 pushed a commit that referenced this pull request Sep 21, 2026
…#73139)

The route dependency reads the asset id from the path, but this endpoint
carries it in the request body, so the auth manager was only asked whether
the caller may post to any asset at all. An auth manager that scopes assets
by id, name, or uri could not deny an event for an asset the caller may not
touch, and the response still returned that asset's name and uri.

Co-authored-by: Henry Chen <henrychen@apache.org>
potiuk added a commit that referenced this pull request Sep 22, 2026
…73458)

* [v3-3-test] Clarify that AccessView.JOBS is the Edge worker management permission (#72627) (#73073)

The Edge UI plugin docs say that "can read on Plugins" and "can read on
Jobs" let you view the UI and manage the workers, but they do not say how
the two permissions differ, and they do not mention what the default
Viewer role already holds.

Both gaps matter, because the endpoints and the navigation are gated
differently:

- The worker management endpoints under /edge_worker/ui/ check only
  AccessView.JOBS, and the check is not method-aware -- the same
  dependency guards the GET reads and the POST/PATCH/DELETE mutations.
- "can read on Plugins" only controls whether the plugin shows up in the
  UI navigation. It is not required in order to call the endpoints.

So "can read on Jobs" alone is enough to shut down, delete, re-queue and
retune Edge workers, whether or not the plugin is visible to that user.

That is intentional -- AccessView.JOBS is the management permission for
the plugin rather than a read-only grant -- but it reads as surprising
from the code alone, where a permission named "can read" guards mutating
routes. It is more surprising in a default Flask AppBuilder setup, where
the Viewer role includes (ACTION_CAN_READ, RESOURCE_JOB) but not the
Plugins read: such a user cannot see the Edge plugin and can still reach
its management endpoints.

Adds a warning to the UI plugin docs stating the intent, the split
between the two permissions, the consequence for the default Viewer role,
and the concrete action for deployments where Viewers must not manage
workers. Points at the existing "fine granular access control" entry in
architecture.rst rather than restating it.

Documentation only; no behaviour change.
(cherry picked from commit 1391b09)

* [v3-3-test] Keep the Gradle wrapper jar out of the source release (#69444) (#73108)

ASF policy does not permit compiled binaries in a source release and the
Gradle wrapper is not among the exempted build tools (LEGAL-570), so main
stopped shipping java-sdk/gradle/wrapper/gradle-wrapper.jar in #69444.

Only half of that change reached this branch. The breeze side already
restores gradlew and gradlew.bat after `git archive` drops them, but the
java-sdk/.gitattributes side never followed, so nothing is actually dropped
and the 43 KB jar is still in the 3.3.2rc1 source tarball.

Carrying the rest of the file over also starts shipping java-sdk/.editorconfig,
which the root .gitattributes strips today even though ktlint reads it at
build time and the build task requires that lint to pass.

gradle-wrapper.properties stays in the tarball on purpose: it carries the
pinned Gradle version and distribution checksum a verifier needs to
regenerate the wrapper.

Generated-by: Claude Opus 5
Claude-Session: https://claude.ai/code/session_01DCUVuZ8CCeER1QLhVEKAaZ

* Authorize POST /assets/events on the asset named in the body (#73007) (#73139)

The route dependency reads the asset id from the path, but this endpoint
carries it in the request body, so the auth manager was only asked whether
the caller may post to any asset at all. An auth manager that scopes assets
by id, name, or uri could not deny an event for an asset the caller may not
touch, and the response still returned that asset's name and uri.

Co-authored-by: Henry Chen <henrychen@apache.org>

* Bump the 3-3-fab-ui-package-updates group across 1 directory with 3 updates (#73208)

Bumps the 3-3-fab-ui-package-updates group with 3 updates in the /providers/fab/src/airflow/providers/fab/www directory: [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core), [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) and [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env).


Updates `@babel/core` from 8.0.1 to 8.0.5
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v8.0.5/packages/babel-core)

Updates `@babel/eslint-parser` from 8.0.1 to 8.0.5
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v8.0.5/eslint/babel-eslint-parser)

Updates `@babel/preset-env` from 8.0.2 to 8.0.5
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v8.0.5/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-version: 8.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: 3-3-fab-ui-package-updates
- dependency-name: "@babel/eslint-parser"
  dependency-version: 8.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: 3-3-fab-ui-package-updates
- dependency-name: "@babel/preset-env"
  dependency-version: 8.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: 3-3-fab-ui-package-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [v3-3-test] Reject non-numeric --limit values in airflow dags list-jobs (#72891) (#73214)

The --limit option had no argparse type, so a value such as "abc" or "-1"
was passed straight into the SQLAlchemy query and surfaced as a raw
ValueError traceback instead of a usage error. Validating it at parse
time gives the standard argparse message and exit code 2, while keeping
0 and positive integers working exactly as before.


(cherry picked from commit 022ff83)

Co-authored-by: Y-C <easoneason0905@gmail.com>
Co-authored-by: Eason09053360 <185830721+Eason09053360@users.noreply.github.com>

* [v3-3-test] Fix DAG.cli() crashing on dags test --show-dagrun (#72810) (#73216)

* Fix DAG.cli() crashing on dags test --show-dagrun

A Dag file run as a script goes through DAG.cli(), whose parser drops the
dag_id positional and passes the Dag object to the handler instead. dag_test
never needed dag_id from the parsed arguments until it rendered the run: the
task instance query read args.dag_id, so --show-dagrun, --save-dagrun and
--imgcat-dagrun raised AttributeError after the Dag had already run, while
the plain command and the regular airflow dags test path worked.

Filtering on the resolved Dag's id is the same value on the regular path and
the only one available from DAG.cli().

* Update airflow-core/tests/unit/cli/commands/test_dag_command.py



* Update airflow-core/tests/unit/cli/commands/test_dag_command.py

---------
(cherry picked from commit 63fc32f)

Co-authored-by: Y-C <easoneason0905@gmail.com>
Co-authored-by: Eason09053360 <185830721+Eason09053360@users.noreply.github.com>
Co-authored-by: rjgoyln <151457491+rjgoyln@users.noreply.github.com>
Co-authored-by: Henry Chen <henryhenry0512@gmail.com>

* Split the api_fastapi common parameters module into a package (#73239)

The parameters module had grown to ~1800 lines mixing generic query
helpers with hundreds of domain-specific query-parameter aliases, making
it hard to navigate and maintain. Grouping the machinery (base, search,
filter, sort, range) and the per-domain aliases (dag, dag_run,
task_instance, xcom, asset, misc) into focused submodules keeps each file
readable. The package __init__ re-exports the names consumed elsewhere in
the codebase so existing import paths keep working.

Backport of #72795 to v3-3-test. Because v3-3-test's parameters.py has a
smaller set of query-parameter definitions than main (some PRs were not
backported), this was re-created by running the same mechanical split on
v3-3-test's file rather than cherry-picked: every definition is
byte-identical to the original, and __init__ re-exports only the names
consumed outside the package.

* [v3-3-test] Document that plugin names must be unique (#73197) (#73203)

(cherry picked from commit 49674b7)

Co-authored-by: Aaron Chen <nailo2c@gmail.com>

* [v3-3-test] Decode deadline alert interval and callback without generic deserialization (#72651) (#73304)

* Decode deadline alert interval and callback without generic deserialization

decode_deadline_alert passed the Dag-author controlled interval and callback to
airflow.sdk.serde.deserialize, which imports the class named in the payload and
instantiates it with the encoded arguments. These decoders run in the scheduler
and the API server whenever a serialized Dag is loaded, so any class under the
airflow.* allow list could be constructed there.

The security model says a Dag author reaches those processes only through
registered plugins and providers, and the codebase enforces that at decode time
for timetables, priority weight strategies and operator extra links. Deadline
fields had no equivalent gate.

Both fields are now rebuilt from their encoded form directly:

* interval accepts a number, a timedelta payload, or a variable-interval payload
  carrying a key, each reconstructed from primitives.
* callback accepts AsyncCallback or SyncCallback, selected from a fixed map
  rather than imported by name, with path as a string, queue/executor as
  optional strings, and unexpected fields refused.

Neither reaches serde.deserialize, so the class a Dag author names in either
field is never imported.

Filtering in front of deserialize was tried first and was not sufficient. serde
normalises the legacy {__type, __var} shape into __classname__ *inside*
deserialize, so a payload inspected beforehand carries no class name to reject.
Payloads are normalised before inspection here, and that case is tested.

Known residual, deliberately not closed here: callback kwargs are still passed
through generic deserialization, so a legitimate callback can carry an arbitrary
allow-listed class under its kwargs. Deferring that decode to the process that
runs the callback would close it, but the kwargs are consumed through two paths
using two different encodings, and getting either wrong hands user code an
encoded dict in place of its argument. The residual is not specific to
deadlines -- it is the general property of deserializing Dag-author data, shared
with every other serde call site. A test asserts the current behaviour so the
gap stays visible and any change to it has to be deliberate.

Tests assert the class is never constructed rather than that an error is raised.
Against unpatched sources the callback case reports DID NOT RAISE and the
interval case names the instance that had already been built.

* Accept pre-3.2 callback paths and validate fields per callback class

Review feedback on the deadline decoding gate.

Callbacks moved out of airflow.sdk.definitions.deadline in 3.2, so alerts
serialized by an earlier version name the old module. The allow list only held
the current path, which would have made those rows undecodable on upgrade --
the same backward-compatibility case the interval allow list already covers.

The permitted callback fields were a hardcoded set covering both subclasses at
once, so a payload could carry queue on a SyncCallback or executor on an
AsyncCallback. The set is per class, and each class already declares its own
via serialized_fields(), so ask it rather than restating the answer here and
letting the two drift. That also turns a TypeError raised from inside the
rebuild into the intended refusal, and the check now runs before anything is
reconstructed from the payload.
(cherry picked from commit c614c57)


Generated-by: Claude Opus 5
Claude-Session: https://claude.ai/code/session_012zrnHJHPchB83FtwrYRf5q

Co-authored-by: Jarek Potiuk <jarek@potiuk.com>

* Bump swagger-ui-dist (#73332)

Bumps the 3-3-registry-package-updates group with 1 update in the /registry directory: [swagger-ui-dist](https://github.com/swagger-api/swagger-ui).


Updates `swagger-ui-dist` from 5.32.14 to 5.32.15
- [Release notes](https://github.com/swagger-api/swagger-ui/releases)
- [Commits](swagger-api/swagger-ui@v5.32.14...v5.32.15)

---
updated-dependencies:
- dependency-name: swagger-ui-dist
  dependency-version: 5.32.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: 3-3-registry-package-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump webpack (#73333)

Bumps the 3-3-fab-ui-package-updates group with 1 update in the /providers/fab/src/airflow/providers/fab/www directory: [webpack](https://github.com/webpack/webpack).


Updates `webpack` from 5.110.3 to 5.111.0
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](webpack/webpack@v5.110.3...v5.111.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-version: 5.111.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: 3-3-fab-ui-package-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [v3-3-test] Skip provider dependency generation for help command in breeze cli (#71689) (#73330)

(cherry picked from commit 60633c8)

Co-authored-by: feberbo <felipeboralli@gmail.com>

* Bump the github-actions-updates group with 5 updates (#73344)

Bumps the github-actions-updates group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [actions/setup-java](https://github.com/actions/setup-java) | `6.0.0` | `6.0.1` |
| [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) | `10.0.1` | `10.1.0` |
| [github/codeql-action/init](https://github.com/github/codeql-action) | `4.37.9` | `4.38.0` |
| [github/codeql-action/autobuild](https://github.com/github/codeql-action) | `4.37.9` | `4.38.0` |
| [github/codeql-action/analyze](https://github.com/github/codeql-action) | `4.37.9` | `4.38.0` |


Updates `actions/setup-java` from 6.0.0 to 6.0.1
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](actions/setup-java@dd06d9c...de7274f)

Updates `astral-sh/setup-uv` from 10.0.1 to 10.1.0
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](astral-sh/setup-uv@20cfd1b...bec219d)

Updates `github/codeql-action/init` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@cdf488f...b96794f)

Updates `github/codeql-action/autobuild` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@cdf488f...b96794f)

Updates `github/codeql-action/analyze` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@cdf488f...b96794f)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions-updates
- dependency-name: astral-sh/setup-uv
  dependency-version: 10.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-updates
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-updates
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-updates
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [v3-3-test] Make the constraints check follow the cooldown rules the constraints use (#73316) (#73385)

After every provider release wave the `Deps *:constraints` jobs doubled for
three or four days and then recovered on their own. The constraints are
resolved under uv's `exclude-newer` cooldown with the per-package overrides
in the root pyproject.toml, where Airflow's own distributions are exempt, so
a fresh provider wave lands in the constraints the same day. The check had
its own 4-day cooldown applied to every package, so for those days the pin
was newer than "latest" and a plain equality check counted it as outdated.
With `--explain-why` every such package then cost a full `uv sync` that
tried to pin it to the *older* version and reported that the pin did not
take effect. On 2026-09-15 that was 37 providers and about 12 extra minutes
per job, with nothing to act on.

The check now reads `[tool.uv.exclude-newer-package]` and applies the same
rules: no cooldown for exempt distributions, a moved cutoff where one is
configured. A pin that is still ahead of "latest" counts as up to date, so
no explanation runs for it either.
(cherry picked from commit 7da73c8)


Generated-by: Claude Opus 5

Co-authored-by: Jarek Potiuk <jarek@potiuk.com>

* Stop shipping broken agent-skill symlinks in the source release (#73107) (#73448)

Excluding .agents from the source tarball (#68851) left .claude behind.
Everything under .claude/skills is a relay symlink into .agents/skills, so
export-ignore strips the targets while the links themselves still ship:
unpacking the source release yields broken symlinks, and .claude/ arrives
holding nothing but those dead links.

Found while verifying 3.3.2rc1, whose tarball carries five of them. The
released 3.3.1 carries the same ones, so this is long-standing rather than
something a recent change introduced.

.github needs no equivalent entry: its own skills relays are already
covered by the existing .github export-ignore.

(cherry picked from commit 4b0eb8e)

* [v3-3-test] Run the scheduled CI upgrade check on v3-3-test (#73318)

3.3.x is the release branch under active maintenance, so its pinned uv,
prek and image versions drift the same way main's do — but nothing has
been refreshing them, because the only caller targets main. v3-2-test
carried the same caller until it stopped taking releases.
(cherry picked from commit 39c9a5d)

Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Code (Opus 5)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Vincent <97131062+vincbeck@users.noreply.github.com>
Co-authored-by: Henry Chen <henrychen@apache.org>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Y-C <easoneason0905@gmail.com>
Co-authored-by: Eason09053360 <185830721+Eason09053360@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rjgoyln <151457491+rjgoyln@users.noreply.github.com>
Co-authored-by: Henry Chen <henryhenry0512@gmail.com>
Co-authored-by: Pierre Jeambrun <pierrejbrun@gmail.com>
Co-authored-by: Aaron Chen <nailo2c@gmail.com>
Co-authored-by: feberbo <felipeboralli@gmail.com>
potiuk added a commit that referenced this pull request Sep 22, 2026
#73449) (#73459)

* [v3-3-test] Clarify that AccessView.JOBS is the Edge worker management permission (#72627) (#73073)

The Edge UI plugin docs say that "can read on Plugins" and "can read on
Jobs" let you view the UI and manage the workers, but they do not say how
the two permissions differ, and they do not mention what the default
Viewer role already holds.

Both gaps matter, because the endpoints and the navigation are gated
differently:

- The worker management endpoints under /edge_worker/ui/ check only
  AccessView.JOBS, and the check is not method-aware -- the same
  dependency guards the GET reads and the POST/PATCH/DELETE mutations.
- "can read on Plugins" only controls whether the plugin shows up in the
  UI navigation. It is not required in order to call the endpoints.

So "can read on Jobs" alone is enough to shut down, delete, re-queue and
retune Edge workers, whether or not the plugin is visible to that user.

That is intentional -- AccessView.JOBS is the management permission for
the plugin rather than a read-only grant -- but it reads as surprising
from the code alone, where a permission named "can read" guards mutating
routes. It is more surprising in a default Flask AppBuilder setup, where
the Viewer role includes (ACTION_CAN_READ, RESOURCE_JOB) but not the
Plugins read: such a user cannot see the Edge plugin and can still reach
its management endpoints.

Adds a warning to the UI plugin docs stating the intent, the split
between the two permissions, the consequence for the default Viewer role,
and the concrete action for deployments where Viewers must not manage
workers. Points at the existing "fine granular access control" entry in
architecture.rst rather than restating it.

Documentation only; no behaviour change.
(cherry picked from commit 1391b09)

* [v3-3-test] Keep the Gradle wrapper jar out of the source release (#69444) (#73108)

ASF policy does not permit compiled binaries in a source release and the
Gradle wrapper is not among the exempted build tools (LEGAL-570), so main
stopped shipping java-sdk/gradle/wrapper/gradle-wrapper.jar in #69444.

Only half of that change reached this branch. The breeze side already
restores gradlew and gradlew.bat after `git archive` drops them, but the
java-sdk/.gitattributes side never followed, so nothing is actually dropped
and the 43 KB jar is still in the 3.3.2rc1 source tarball.

Carrying the rest of the file over also starts shipping java-sdk/.editorconfig,
which the root .gitattributes strips today even though ktlint reads it at
build time and the build task requires that lint to pass.

gradle-wrapper.properties stays in the tarball on purpose: it carries the
pinned Gradle version and distribution checksum a verifier needs to
regenerate the wrapper.

Generated-by: Claude Opus 5
Claude-Session: https://claude.ai/code/session_01DCUVuZ8CCeER1QLhVEKAaZ

* Authorize POST /assets/events on the asset named in the body (#73007) (#73139)

The route dependency reads the asset id from the path, but this endpoint
carries it in the request body, so the auth manager was only asked whether
the caller may post to any asset at all. An auth manager that scopes assets
by id, name, or uri could not deny an event for an asset the caller may not
touch, and the response still returned that asset's name and uri.

Co-authored-by: Henry Chen <henrychen@apache.org>

* Bump the 3-3-fab-ui-package-updates group across 1 directory with 3 updates (#73208)

Bumps the 3-3-fab-ui-package-updates group with 3 updates in the /providers/fab/src/airflow/providers/fab/www directory: [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core), [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) and [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env).


Updates `@babel/core` from 8.0.1 to 8.0.5
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v8.0.5/packages/babel-core)

Updates `@babel/eslint-parser` from 8.0.1 to 8.0.5
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v8.0.5/eslint/babel-eslint-parser)

Updates `@babel/preset-env` from 8.0.2 to 8.0.5
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v8.0.5/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-version: 8.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: 3-3-fab-ui-package-updates
- dependency-name: "@babel/eslint-parser"
  dependency-version: 8.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: 3-3-fab-ui-package-updates
- dependency-name: "@babel/preset-env"
  dependency-version: 8.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: 3-3-fab-ui-package-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [v3-3-test] Reject non-numeric --limit values in airflow dags list-jobs (#72891) (#73214)

The --limit option had no argparse type, so a value such as "abc" or "-1"
was passed straight into the SQLAlchemy query and surfaced as a raw
ValueError traceback instead of a usage error. Validating it at parse
time gives the standard argparse message and exit code 2, while keeping
0 and positive integers working exactly as before.


(cherry picked from commit 022ff83)

Co-authored-by: Y-C <easoneason0905@gmail.com>
Co-authored-by: Eason09053360 <185830721+Eason09053360@users.noreply.github.com>

* [v3-3-test] Fix DAG.cli() crashing on dags test --show-dagrun (#72810) (#73216)

* Fix DAG.cli() crashing on dags test --show-dagrun

A Dag file run as a script goes through DAG.cli(), whose parser drops the
dag_id positional and passes the Dag object to the handler instead. dag_test
never needed dag_id from the parsed arguments until it rendered the run: the
task instance query read args.dag_id, so --show-dagrun, --save-dagrun and
--imgcat-dagrun raised AttributeError after the Dag had already run, while
the plain command and the regular airflow dags test path worked.

Filtering on the resolved Dag's id is the same value on the regular path and
the only one available from DAG.cli().

* Update airflow-core/tests/unit/cli/commands/test_dag_command.py



* Update airflow-core/tests/unit/cli/commands/test_dag_command.py

---------
(cherry picked from commit 63fc32f)

Co-authored-by: Y-C <easoneason0905@gmail.com>
Co-authored-by: Eason09053360 <185830721+Eason09053360@users.noreply.github.com>
Co-authored-by: rjgoyln <151457491+rjgoyln@users.noreply.github.com>
Co-authored-by: Henry Chen <henryhenry0512@gmail.com>

* Split the api_fastapi common parameters module into a package (#73239)

The parameters module had grown to ~1800 lines mixing generic query
helpers with hundreds of domain-specific query-parameter aliases, making
it hard to navigate and maintain. Grouping the machinery (base, search,
filter, sort, range) and the per-domain aliases (dag, dag_run,
task_instance, xcom, asset, misc) into focused submodules keeps each file
readable. The package __init__ re-exports the names consumed elsewhere in
the codebase so existing import paths keep working.

Backport of #72795 to v3-3-test. Because v3-3-test's parameters.py has a
smaller set of query-parameter definitions than main (some PRs were not
backported), this was re-created by running the same mechanical split on
v3-3-test's file rather than cherry-picked: every definition is
byte-identical to the original, and __init__ re-exports only the names
consumed outside the package.

* [v3-3-test] Document that plugin names must be unique (#73197) (#73203)

(cherry picked from commit 49674b7)

Co-authored-by: Aaron Chen <nailo2c@gmail.com>

* [v3-3-test] Decode deadline alert interval and callback without generic deserialization (#72651) (#73304)

* Decode deadline alert interval and callback without generic deserialization

decode_deadline_alert passed the Dag-author controlled interval and callback to
airflow.sdk.serde.deserialize, which imports the class named in the payload and
instantiates it with the encoded arguments. These decoders run in the scheduler
and the API server whenever a serialized Dag is loaded, so any class under the
airflow.* allow list could be constructed there.

The security model says a Dag author reaches those processes only through
registered plugins and providers, and the codebase enforces that at decode time
for timetables, priority weight strategies and operator extra links. Deadline
fields had no equivalent gate.

Both fields are now rebuilt from their encoded form directly:

* interval accepts a number, a timedelta payload, or a variable-interval payload
  carrying a key, each reconstructed from primitives.
* callback accepts AsyncCallback or SyncCallback, selected from a fixed map
  rather than imported by name, with path as a string, queue/executor as
  optional strings, and unexpected fields refused.

Neither reaches serde.deserialize, so the class a Dag author names in either
field is never imported.

Filtering in front of deserialize was tried first and was not sufficient. serde
normalises the legacy {__type, __var} shape into __classname__ *inside*
deserialize, so a payload inspected beforehand carries no class name to reject.
Payloads are normalised before inspection here, and that case is tested.

Known residual, deliberately not closed here: callback kwargs are still passed
through generic deserialization, so a legitimate callback can carry an arbitrary
allow-listed class under its kwargs. Deferring that decode to the process that
runs the callback would close it, but the kwargs are consumed through two paths
using two different encodings, and getting either wrong hands user code an
encoded dict in place of its argument. The residual is not specific to
deadlines -- it is the general property of deserializing Dag-author data, shared
with every other serde call site. A test asserts the current behaviour so the
gap stays visible and any change to it has to be deliberate.

Tests assert the class is never constructed rather than that an error is raised.
Against unpatched sources the callback case reports DID NOT RAISE and the
interval case names the instance that had already been built.

* Accept pre-3.2 callback paths and validate fields per callback class

Review feedback on the deadline decoding gate.

Callbacks moved out of airflow.sdk.definitions.deadline in 3.2, so alerts
serialized by an earlier version name the old module. The allow list only held
the current path, which would have made those rows undecodable on upgrade --
the same backward-compatibility case the interval allow list already covers.

The permitted callback fields were a hardcoded set covering both subclasses at
once, so a payload could carry queue on a SyncCallback or executor on an
AsyncCallback. The set is per class, and each class already declares its own
via serialized_fields(), so ask it rather than restating the answer here and
letting the two drift. That also turns a TypeError raised from inside the
rebuild into the intended refusal, and the check now runs before anything is
reconstructed from the payload.
(cherry picked from commit c614c57)


Generated-by: Claude Opus 5
Claude-Session: https://claude.ai/code/session_012zrnHJHPchB83FtwrYRf5q

Co-authored-by: Jarek Potiuk <jarek@potiuk.com>

* Bump swagger-ui-dist (#73332)

Bumps the 3-3-registry-package-updates group with 1 update in the /registry directory: [swagger-ui-dist](https://github.com/swagger-api/swagger-ui).


Updates `swagger-ui-dist` from 5.32.14 to 5.32.15
- [Release notes](https://github.com/swagger-api/swagger-ui/releases)
- [Commits](swagger-api/swagger-ui@v5.32.14...v5.32.15)

---
updated-dependencies:
- dependency-name: swagger-ui-dist
  dependency-version: 5.32.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: 3-3-registry-package-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump webpack (#73333)

Bumps the 3-3-fab-ui-package-updates group with 1 update in the /providers/fab/src/airflow/providers/fab/www directory: [webpack](https://github.com/webpack/webpack).


Updates `webpack` from 5.110.3 to 5.111.0
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](webpack/webpack@v5.110.3...v5.111.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-version: 5.111.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: 3-3-fab-ui-package-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [v3-3-test] Skip provider dependency generation for help command in breeze cli (#71689) (#73330)

(cherry picked from commit 60633c8)

Co-authored-by: feberbo <felipeboralli@gmail.com>

* Bump the github-actions-updates group with 5 updates (#73344)

Bumps the github-actions-updates group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [actions/setup-java](https://github.com/actions/setup-java) | `6.0.0` | `6.0.1` |
| [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) | `10.0.1` | `10.1.0` |
| [github/codeql-action/init](https://github.com/github/codeql-action) | `4.37.9` | `4.38.0` |
| [github/codeql-action/autobuild](https://github.com/github/codeql-action) | `4.37.9` | `4.38.0` |
| [github/codeql-action/analyze](https://github.com/github/codeql-action) | `4.37.9` | `4.38.0` |


Updates `actions/setup-java` from 6.0.0 to 6.0.1
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](actions/setup-java@dd06d9c...de7274f)

Updates `astral-sh/setup-uv` from 10.0.1 to 10.1.0
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](astral-sh/setup-uv@20cfd1b...bec219d)

Updates `github/codeql-action/init` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@cdf488f...b96794f)

Updates `github/codeql-action/autobuild` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@cdf488f...b96794f)

Updates `github/codeql-action/analyze` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@cdf488f...b96794f)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions-updates
- dependency-name: astral-sh/setup-uv
  dependency-version: 10.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-updates
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-updates
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-updates
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [v3-3-test] Make the constraints check follow the cooldown rules the constraints use (#73316) (#73385)

After every provider release wave the `Deps *:constraints` jobs doubled for
three or four days and then recovered on their own. The constraints are
resolved under uv's `exclude-newer` cooldown with the per-package overrides
in the root pyproject.toml, where Airflow's own distributions are exempt, so
a fresh provider wave lands in the constraints the same day. The check had
its own 4-day cooldown applied to every package, so for those days the pin
was newer than "latest" and a plain equality check counted it as outdated.
With `--explain-why` every such package then cost a full `uv sync` that
tried to pin it to the *older* version and reported that the pin did not
take effect. On 2026-09-15 that was 37 providers and about 12 extra minutes
per job, with nothing to act on.

The check now reads `[tool.uv.exclude-newer-package]` and applies the same
rules: no cooldown for exempt distributions, a moved cutoff where one is
configured. A pin that is still ahead of "latest" counts as up to date, so
no explanation runs for it either.
(cherry picked from commit 7da73c8)


Generated-by: Claude Opus 5

Co-authored-by: Jarek Potiuk <jarek@potiuk.com>

* Stop shipping broken agent-skill symlinks in the source release (#73107) (#73448)

Excluding .agents from the source tarball (#68851) left .claude behind.
Everything under .claude/skills is a relay symlink into .agents/skills, so
export-ignore strips the targets while the links themselves still ship:
unpacking the source release yields broken symlinks, and .claude/ arrives
holding nothing but those dead links.

Found while verifying 3.3.2rc1, whose tarball carries five of them. The
released 3.3.1 carries the same ones, so this is long-standing rather than
something a recent change introduced.

.github needs no equivalent entry: its own skills relays are already
covered by the existing .github export-ignore.

(cherry picked from commit 4b0eb8e)

* [v3-3-test] Skip --explain-why for Python 3.10 in the deps summary job (#73449)

Python 3.10 is losing support in more and more of our dependencies, so the
constraints check has far more outdated packages to explain there, and each
explanation costs a dependency resolution. That pushed the 3.10 job from
~18 to ~27 minutes on main. With 3.10 support being dropped in weeks, the
explanations are not worth the CI time for that version.
(cherry picked from commit b7a50ec)

Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 5

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Vincent <97131062+vincbeck@users.noreply.github.com>
Co-authored-by: Henry Chen <henrychen@apache.org>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Y-C <easoneason0905@gmail.com>
Co-authored-by: Eason09053360 <185830721+Eason09053360@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rjgoyln <151457491+rjgoyln@users.noreply.github.com>
Co-authored-by: Henry Chen <henryhenry0512@gmail.com>
Co-authored-by: Pierre Jeambrun <pierrejbrun@gmail.com>
Co-authored-by: Aaron Chen <nailo2c@gmail.com>
Co-authored-by: feberbo <felipeboralli@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:API Airflow's REST/HTTP API

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants