This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-1-test by this push:
new 2504bd660de [v3-1-test] Prevent client secrets and proxy credentials
from being logged in Microsoft Graph hook logs (#59688) (#59792)
2504bd660de is described below
commit 2504bd660dec4e2958016984dfe4900eb900b280
Author: Ankit Chaurasia <[email protected]>
AuthorDate: Fri Dec 26 05:01:41 2025 +0545
[v3-1-test] Prevent client secrets and proxy credentials from being logged
in Microsoft Graph hook logs (#59688) (#59792)
Prevent client secrets and proxy credentials from being logged in Microsoft
Graph hook logs (#59688)
(cherry picked from commit a9dea6d0b81d67843c2507bd85af772fde9dd738)
---
airflow-core/newsfragments/59688.improvement.rst | 1 +
.../providers/microsoft/azure/hooks/msgraph.py | 10 ++++++----
.../unit/microsoft/azure/hooks/test_msgraph.py | 21 +++++++++++++++++++++
.../airflow_shared/secrets_masker/secrets_masker.py | 2 ++
task-sdk/src/airflow/sdk/log.py | 4 +---
5 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/airflow-core/newsfragments/59688.improvement.rst
b/airflow-core/newsfragments/59688.improvement.rst
new file mode 100644
index 00000000000..d3c592cfcc3
--- /dev/null
+++ b/airflow-core/newsfragments/59688.improvement.rst
@@ -0,0 +1 @@
+Add ``proxy`` and ``proxies`` to ``DEFAULT_SENSITIVE_FIELDS`` in
secrets_masker to treat proxy configurations as sensitive by default
diff --git
a/providers/microsoft/azure/src/airflow/providers/microsoft/azure/hooks/msgraph.py
b/providers/microsoft/azure/src/airflow/providers/microsoft/azure/hooks/msgraph.py
index 958a3b8f437..775f0097602 100644
---
a/providers/microsoft/azure/src/airflow/providers/microsoft/azure/hooks/msgraph.py
+++
b/providers/microsoft/azure/src/airflow/providers/microsoft/azure/hooks/msgraph.py
@@ -63,6 +63,8 @@ if TYPE_CHECKING:
from airflow.models import Connection
+from airflow_shared.secrets_masker import redact
+
class DefaultResponseHandler(ResponseHandler):
"""DefaultResponseHandler returns JSON payload or content in bytes or
response headers."""
@@ -272,7 +274,7 @@ class KiotaRequestAdapterHook(BaseHook):
self.log.info("Host: %s", host)
self.log.info("Base URL: %s", base_url)
self.log.info("Client id: %s", client_id)
- self.log.info("Client secret: %s", client_secret)
+ self.log.info("Client secret: %s", redact(client_secret,
name="client_secret"))
self.log.info("API version: %s", api_version)
self.log.info("Scope: %s", scopes)
self.log.info("Verify: %s", verify)
@@ -280,8 +282,8 @@ class KiotaRequestAdapterHook(BaseHook):
self.log.info("Trust env: %s", trust_env)
self.log.info("Authority: %s", authority)
self.log.info("Allowed hosts: %s", allowed_hosts)
- self.log.info("Proxies: %s", proxies)
- self.log.info("HTTPX Proxies: %s", httpx_proxies)
+ self.log.info("Proxies: %s", redact(proxies, name="proxies"))
+ self.log.info("HTTPX Proxies: %s", redact(httpx_proxies,
name="proxies"))
credentials = self.get_credentials(
login=connection.login,
password=connection.password,
@@ -391,7 +393,7 @@ class KiotaRequestAdapterHook(BaseHook):
self.log.info("Certificate data: %s", certificate_data is not None)
self.log.info("Authority: %s", authority)
self.log.info("Disable instance discovery: %s",
disable_instance_discovery)
- self.log.info("MSAL Proxies: %s", msal_proxies)
+ self.log.info("MSAL Proxies: %s", redact(msal_proxies, name="proxies"))
if certificate_path or certificate_data:
return CertificateCredential(
tenant_id=tenant_id,
diff --git
a/providers/microsoft/azure/tests/unit/microsoft/azure/hooks/test_msgraph.py
b/providers/microsoft/azure/tests/unit/microsoft/azure/hooks/test_msgraph.py
index affe190b359..157d75b3230 100644
--- a/providers/microsoft/azure/tests/unit/microsoft/azure/hooks/test_msgraph.py
+++ b/providers/microsoft/azure/tests/unit/microsoft/azure/hooks/test_msgraph.py
@@ -387,6 +387,27 @@ class TestKiotaRequestAdapterHook:
error_code =
actual.get_child_node("error").get_child_node("code").get_str_value()
assert error_code == "TenantThrottleThresholdExceeded"
+ @pytest.mark.asyncio
+ async def test_build_request_adapter_masks_secrets(self):
+ """Test that sensitive data is masked when building request adapter."""
+ with patch(
+ f"{BASEHOOK_PATCH_PATH}.get_connection",
+ side_effect=lambda conn_id: get_airflow_connection(
+ conn_id=conn_id,
+ password="my_secret_password",
+ proxies={"http": "http://user:pass@proxy:3128"},
+ ),
+ ):
+ with
patch("airflow.providers.microsoft.azure.hooks.msgraph.redact") as mock_redact:
+ mock_redact.side_effect = lambda x, name=None: "***" if x else
x
+
+ hook = KiotaRequestAdapterHook(conn_id="msgraph_api")
+ await hook.get_async_conn()
+
+ assert mock_redact.call_count >= 3
+ mock_redact.assert_any_call({"http":
"http://user:pass@proxy:3128"}, name="proxies")
+ mock_redact.assert_any_call("my_secret_password",
name="client_secret")
+
class TestResponseHandler:
def test_default_response_handler_when_json(self):
diff --git
a/shared/secrets_masker/src/airflow_shared/secrets_masker/secrets_masker.py
b/shared/secrets_masker/src/airflow_shared/secrets_masker/secrets_masker.py
index 5b7ff4e1c81..c78e39ca65a 100644
--- a/shared/secrets_masker/src/airflow_shared/secrets_masker/secrets_masker.py
+++ b/shared/secrets_masker/src/airflow_shared/secrets_masker/secrets_masker.py
@@ -58,6 +58,8 @@ DEFAULT_SENSITIVE_FIELDS = frozenset(
"passwd",
"password",
"private_key",
+ "proxy",
+ "proxies",
"secret",
"token",
"keyfile_dict",
diff --git a/task-sdk/src/airflow/sdk/log.py b/task-sdk/src/airflow/sdk/log.py
index 6e81a96b254..1ee7b1ed1f0 100644
--- a/task-sdk/src/airflow/sdk/log.py
+++ b/task-sdk/src/airflow/sdk/log.py
@@ -36,12 +36,10 @@ if TYPE_CHECKING:
from airflow.sdk.types import Logger, RuntimeTaskInstanceProtocol as
RuntimeTI
-__all__ = ["configure_logging", "reset_logging", "mask_secret"]
+from airflow.sdk._shared.secrets_masker import redact
def mask_logs(logger: Any, method_name: str, event_dict: EventDict) ->
EventDict:
- from airflow.sdk._shared.secrets_masker import redact
-
event_dict = redact(event_dict) # type: ignore[assignment]
return event_dict