This is an automated email from the ASF dual-hosted git repository.

vincbeck pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 6f9cc5f8fb4 Forbid accessing team secrets with environment variable as 
global secret (#62588)
6f9cc5f8fb4 is described below

commit 6f9cc5f8fb4186ffc66d9a4f9e25613e63fe5050
Author: Vincent <[email protected]>
AuthorDate: Mon Mar 2 10:04:04 2026 -0500

    Forbid accessing team secrets with environment variable as global secret 
(#62588)
---
 .../src/airflow/secrets/environment_variables.py    | 11 +++++++++++
 airflow-core/tests/unit/always/test_secrets.py      | 21 +++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/airflow-core/src/airflow/secrets/environment_variables.py 
b/airflow-core/src/airflow/secrets/environment_variables.py
index ff67f1de2a8..5fb85732d71 100644
--- a/airflow-core/src/airflow/secrets/environment_variables.py
+++ b/airflow-core/src/airflow/secrets/environment_variables.py
@@ -20,6 +20,7 @@
 from __future__ import annotations
 
 import os
+import re
 
 from airflow.secrets import BaseSecretsBackend
 
@@ -31,6 +32,9 @@ class EnvironmentVariablesBackend(BaseSecretsBackend):
     """Retrieves Connection object and Variable from environment variable."""
 
     def get_conn_value(self, conn_id: str, team_name: str | None = None) -> 
str | None:
+        if self._is_team_specific_accessed_as_global(conn_id, team_name):
+            return None
+
         if team_name and (
             team_var := 
os.environ.get(f"{CONN_ENV_PREFIX}_{team_name.upper()}___" + conn_id.upper())
         ):
@@ -47,6 +51,9 @@ class EnvironmentVariablesBackend(BaseSecretsBackend):
         :param team_name: Team name associated to the task trying to access 
the variable (if any)
         :return: Variable Value
         """
+        if self._is_team_specific_accessed_as_global(key, team_name):
+            return None
+
         if team_name and (
             team_var := 
os.environ.get(f"{VAR_ENV_PREFIX}_{team_name.upper()}___" + key.upper())
         ):
@@ -54,3 +61,7 @@ class EnvironmentVariablesBackend(BaseSecretsBackend):
             return team_var
 
         return os.environ.get(VAR_ENV_PREFIX + key.upper())
+
+    @staticmethod
+    def _is_team_specific_accessed_as_global(secret_id: str, team_name: str | 
None = None) -> bool:
+        return team_name is None and bool(re.fullmatch(r"_[^_]+___.+", 
secret_id))
diff --git a/airflow-core/tests/unit/always/test_secrets.py 
b/airflow-core/tests/unit/always/test_secrets.py
index 3aabc5d19a1..dfa39352b92 100644
--- a/airflow-core/tests/unit/always/test_secrets.py
+++ b/airflow-core/tests/unit/always/test_secrets.py
@@ -24,6 +24,7 @@ import pytest
 from airflow.configuration import ensure_secrets_loaded, 
initialize_secrets_backends
 from airflow.models import Connection, Variable
 from airflow.sdk import SecretCache
+from airflow.sdk.exceptions import AirflowNotFoundException
 
 from tests_common.test_utils.config import conf_vars
 from tests_common.test_utils.db import clear_db_variables
@@ -119,6 +120,17 @@ class TestConnectionsFromSecrets:
 
         assert conn.get_uri() == "mysql://airflow:airflow@host:5432/airflow"
 
+    @pytest.mark.db_test
+    @mock.patch.dict(
+        "os.environ",
+        {
+            "AIRFLOW_CONN__TEAM___TEST_MYSQL": 
"mysql://airflow:airflow@host:5432/airflow",
+        },
+    )
+    def test_connection_env_var_do_not_access_team_specific(self):
+        with pytest.raises(AirflowNotFoundException, match=r"The conn_id 
`_team___test_mysql` isn't defined"):
+            
Connection.get_connection_from_secrets(conn_id="_team___test_mysql")
+
 
 @skip_if_force_lowest_dependencies_marker
 @pytest.mark.db_test
@@ -204,3 +216,12 @@ class TestVariableFromSecrets:
 
         mock_secret_get.return_value = "a_secret_value"
         assert Variable.get(key="not_myvar") == "a_secret_value"
+
+    @mock.patch.dict(
+        "os.environ",
+        {
+            "AIRFLOW_VAR__TEAM___MYVAR": "value",
+        },
+    )
+    def test_variable_env_var_do_not_access_team_specific(self):
+        assert Variable.get_variable_from_secrets(key="_team___myvar") is None

Reply via email to