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

jscheffl 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 5151f6cd203 Fix SecretsMasker merge round-trip for Kubernetes env vars 
(#67122)
5151f6cd203 is described below

commit 5151f6cd2033bfcc3ffdb356330333de8a6591d5
Author: Henry Chen <[email protected]>
AuthorDate: Thu May 28 01:08:33 2026 +0800

    Fix SecretsMasker merge round-trip for Kubernetes env vars (#67122)
---
 .../secrets_masker/secrets_masker.py               |  8 +++++
 .../tests/secrets_masker/test_secrets_masker.py    | 35 ++++++++++++++++++++++
 2 files changed, 43 insertions(+)

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 6ae5343ec00..03198bf48a8 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
@@ -445,12 +445,20 @@ class SecretsMasker(logging.Filter):
             # Determine if we should treat this as sensitive
             is_sensitive = force_sensitive or (name is not None and 
self.should_hide_value_for_key(name))
 
+            v1_env_var_name = None
+            if isinstance(new_item, dict) and _is_v1_env_var(old_item):
+                # redact(V1EnvVar) returns a dict, so merge against the old 
object's serialized shape.
+                old_item = old_item.to_dict()
+                v1_env_var_name = old_item.get("name")
+
             if isinstance(new_item, dict) and isinstance(old_item, dict):
                 merged = {}
                 for key in new_item.keys():
                     if key in old_item:
                         # For dicts, pass the key as name unless we're in 
sensitive mode
                         child_name = None if is_sensitive else key
+                        if key == "value" and v1_env_var_name:
+                            child_name = v1_env_var_name
                         merged[key] = self._merge(
                             new_item[key],
                             old_item[key],
diff --git a/shared/secrets_masker/tests/secrets_masker/test_secrets_masker.py 
b/shared/secrets_masker/tests/secrets_masker/test_secrets_masker.py
index 1e8b50522e2..1f107fee07e 100644
--- a/shared/secrets_masker/tests/secrets_masker/test_secrets_masker.py
+++ b/shared/secrets_masker/tests/secrets_masker/test_secrets_masker.py
@@ -1482,6 +1482,41 @@ class TestSecretsMaskerMerge:
         assert result == new_enum
         assert isinstance(result, MyEnum)
 
+    def test_merge_round_trip_kubernetes_env_var(self):
+        class MockV1EnvVar:
+            def __init__(self, name, value, value_from=None):
+                self.name = name
+                self.value = value
+                self.value_from = value_from
+
+            def to_dict(self):
+                return {"name": self.name, "value": self.value, "value_from": 
self.value_from}
+
+        original_env_var = MockV1EnvVar("password", "original_password", 
"original_source")
+        normal_env_var = MockV1EnvVar("app_name", "original_app")
+
+        with patch(
+            "airflow_shared.secrets_masker.secrets_masker._is_v1_env_var",
+            side_effect=lambda item: isinstance(item, MockV1EnvVar),
+        ):
+            redacted_env_var = self.masker.redact(original_env_var)
+            redacted_env_var["value_from"] = "***"
+
+            merged = self.masker.merge(redacted_env_var, original_env_var)
+
+            updated_env_var = {**redacted_env_var, "value": "updated_password"}
+            merged_updated = self.masker.merge(updated_env_var, 
original_env_var)
+
+            normal_merged = self.masker.merge({"name": "app_name", "value": 
"***"}, normal_env_var)
+
+        assert merged == {
+            "name": "password",
+            "value": "original_password",
+            "value_from": "***",
+        }
+        assert merged_updated["value"] == "updated_password"
+        assert normal_merged["value"] == "***"
+
     def test_merge_round_trip(self):
         # Original data with sensitive information
         original_config = {

Reply via email to