What happened
airflow/api_fastapi/core_api/services/public/config.py treats the synthetic per-key secrets-backend options as sensitive by matching literal section names:
_PER_KEY_SENSITIVE_PREFIXES: dict[str, str] = {
"secrets": "backend_kwarg__",
"workers": "secrets_backend_kwarg__",
}
Both _is_per_key_sensitive_option and _mask_per_key_sensitive_options key off those exact names, so neither recognises a team-scoped spelling of the same option:
- the config-file section
[<team>=secrets], and
AIRFLOW__<TEAM>___SECRETS__BACKEND_KWARG__*, which is reported under a section named after the team rather than after secrets.
This is the same root cause as #70755 — sensitivity being decided from a section name that a team-scoped override does not use — but a different mechanism, and it was left untouched there.
Why it is not currently exploitable
_get_custom_secret_backend is not team-aware, so there is no way to configure a team-scoped backend_kwarg__* that the literal-section match could miss. Nothing leaks today. The gap becomes live the moment secrets backends gain team scoping, and at that point it fails open — a team's backend credentials would be returned in full by GET /config.
What needs doing
Resolve both spellings back to the base option before deciding sensitivity, as AirflowConfigParser.is_sensitive_option now does for registered options. The env-var spelling is the awkward half: it lands under a section named after the team, which is the shape _names_sensitive_team_env_var deals with in the shared parser. The synthetic per-key options are built separately and need their own pass over conf_dict.
Acceptance criteria
- A team-scoped
backend_kwarg__* / secrets_backend_kwarg__* value is redacted by GET /config and GET /config/section/{section}/option/{option} when display_sensitive=False, under both the [<team>=secrets] and the AIRFLOW__<TEAM>___SECRETS__... spelling.
display_sensitive=True still returns real values.
- Tests covering both spellings.
Raised in review of #70755.
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
What happened
airflow/api_fastapi/core_api/services/public/config.pytreats the synthetic per-key secrets-backend options as sensitive by matching literal section names:Both
_is_per_key_sensitive_optionand_mask_per_key_sensitive_optionskey off those exact names, so neither recognises a team-scoped spelling of the same option:[<team>=secrets], andAIRFLOW__<TEAM>___SECRETS__BACKEND_KWARG__*, which is reported under a section named after the team rather than aftersecrets.This is the same root cause as #70755 — sensitivity being decided from a section name that a team-scoped override does not use — but a different mechanism, and it was left untouched there.
Why it is not currently exploitable
_get_custom_secret_backendis not team-aware, so there is no way to configure a team-scopedbackend_kwarg__*that the literal-section match could miss. Nothing leaks today. The gap becomes live the moment secrets backends gain team scoping, and at that point it fails open — a team's backend credentials would be returned in full byGET /config.What needs doing
Resolve both spellings back to the base option before deciding sensitivity, as
AirflowConfigParser.is_sensitive_optionnow does for registered options. The env-var spelling is the awkward half: it lands under a section named after the team, which is the shape_names_sensitive_team_env_vardeals with in the shared parser. The synthetic per-key options are built separately and need their own pass overconf_dict.Acceptance criteria
backend_kwarg__*/secrets_backend_kwarg__*value is redacted byGET /configandGET /config/section/{section}/option/{option}whendisplay_sensitive=False, under both the[<team>=secrets]and theAIRFLOW__<TEAM>___SECRETS__...spelling.display_sensitive=Truestill returns real values.Raised in review of #70755.
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting