Reject team names that differ only in case - #70885
Open
potiuk wants to merge 2 commits into
Open
Conversation
Refusing every id of the form `_<x>___<y>` was too broad. A team agnostic secret whose id merely looks namespaced -- `_a___b`, `_ab___x` -- was refused even though `a` and `ab` are too short to be team names, so no team namespace can be spelled that way and the lookup was safe. That blocked a legitimate lookup for no benefit. Test the leading segment against the team name rule instead. Every id that does spell a real team's namespace still has a valid team name in that position by construction, so nothing reachable is let through, while ids that cannot name a team resolve normally again. Every split is considered, since a team name may itself contain the separator, and one plausible team name is enough to refuse. This makes the guard depend on stored team names actually being valid, so `teams sync` now enforces the same rule as `teams create`. It creates teams from the dag bundle config and did not validate the names at all, which would have left the guard's assumption unbacked -- an unvalidated short name such as "a" would make the test miss and reopen the cross-team read. Generated-by: Claude Opus 5 (1M context) following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
Team names are case sensitive -- `data_eng` and `Data_Eng` are two distinct teams -- but the environment secrets backend upper-cases the team name when it builds the variable name, so both resolve `AIRFLOW_CONN__DATA_ENG___<ID>`. Two such teams share a single secrets namespace, and each reads the other's Connections and Variables. Whether the two rows can coexist at all is decided by the database's collation on a `String` primary key rather than by Airflow, so the same configuration behaves differently across backends. Rejecting the collision at creation makes the behaviour uniform and fails at the moment the name is chosen, rather than silently merging two teams' secrets later. Both creation paths are covered. `teams sync` checks the incoming names against each other as well as against the stored ones, because a bundle config can introduce both halves of a collision in the same run. `upper()` rather than `casefold()`: it is what the backend applies, and team names are restricted to ASCII by the name pattern. Generated-by: Claude Opus 5 (1M context) following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
potiuk
requested review from
ashb,
bugraoz93,
dheerajturaga,
dstandish and
henry3260
as code owners
August 1, 2026 01:31
10 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Team names are case sensitive —
data_engandData_Engare two distinct teams,both accepted by
^[a-zA-Z0-9_-]{3,50}$— but the environment secrets backendupper-cases the team name when it builds the variable name:
So both resolve
AIRFLOW_CONN__DATA_ENG___<ID>. The two teams share one secretsnamespace: each reads the other's Connections and Variables, and whichever
provisions a variable last wins.
Whether the two rows can coexist at all is decided by the database's collation
on a
Stringprimary key rather than by Airflow, so the same configurationbehaves differently across backends — on a case sensitive collation both persist
and the namespaces merge; on a case insensitive one the second
teams createfails on the primary key.
Approach
Reject the collision at creation, on both paths.
teams createrefuses a name that upper-cases onto an existing team's name.teams syncchecks the incoming bundle names against each other as well asagainst the stored ones, because one bundle config can introduce both halves of
a collision in the same run.
The alternative — dropping the
upper()fold so team identity stays casesensitive end to end — was rejected: environment variable names are conventionally
upper-case, and it would silently change which variable an existing deployment
resolves. Failing at the moment the name is chosen is the safer half of that
trade, and it makes behaviour uniform across database collations.
upper()rather thancasefold()is deliberate: it is exactly what the backendapplies, and team names are ASCII-only by the name pattern.
Behaviour change
airflow teams createandairflow teams syncnow fail with an explanatorymessage when a name differs from an existing team's only by case. Deployments that
already have such a pair are not migrated by this change — the collision
already exists there and is reported by neither command. Names differing by more
than case are unaffected.
Test plan
test_team_command.py— 23 cases passtest_team_create_rejects_a_name_differing_only_in_case— creatingdata_engthen
Data_Engfails, and no second row is writtentest_team_create_allows_a_name_differing_by_more_than_case—data_enganddata_eng2coexistruff check/ruff formatcleanWas generative AI tooling used to co-author this PR?
Generated-by: Claude Opus 5 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions