This is an automated email from the ASF dual-hosted git repository.
amoghdesai pushed a commit to branch v3-0-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-0-test by this push:
new 7fc29e93ae4 [v3-0-test] Mask variable values in task logs only if the
variable key is sensitive (#50775) (#50927)
7fc29e93ae4 is described below
commit 7fc29e93ae47fe1913453ffdc38a44a006ba4765
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu May 22 14:55:36 2025 +0530
[v3-0-test] Mask variable values in task logs only if the variable key is
sensitive (#50775) (#50927)
(cherry picked from commit 4f94f2257ae33155de0721bdc079dc0b9bd9cd16)
Co-authored-by: Amogh Desai <[email protected]>
---
.../src/airflow/sdk/execution_time/supervisor.py | 2 +-
.../task_sdk/execution_time/test_supervisor.py | 66 ++++++++++++++++++++--
2 files changed, 62 insertions(+), 6 deletions(-)
diff --git a/task-sdk/src/airflow/sdk/execution_time/supervisor.py
b/task-sdk/src/airflow/sdk/execution_time/supervisor.py
index d0d7b7d9eb1..0328525b0c8 100644
--- a/task-sdk/src/airflow/sdk/execution_time/supervisor.py
+++ b/task-sdk/src/airflow/sdk/execution_time/supervisor.py
@@ -1034,7 +1034,7 @@ class ActivitySubprocess(WatchedSubprocess):
var = self.client.variables.get(msg.key)
if isinstance(var, VariableResponse):
if var.value:
- mask_secret(var.value)
+ mask_secret(var.value, var.key)
var_result = VariableResult.from_variable_response(var)
resp = var_result
dump_opts = {"exclude_unset": True}
diff --git a/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
b/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
index 1e6aec6bed0..1d19d19021e 100644
--- a/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
+++ b/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
@@ -92,7 +92,6 @@ from airflow.sdk.execution_time.comms import (
VariableResult,
XComResult,
)
-from airflow.sdk.execution_time.secrets_masker import SecretsMasker
from airflow.sdk.execution_time.supervisor import (
BUFFER_SIZE,
ActivitySubprocess,
@@ -977,9 +976,17 @@ class TestHandleRequest:
return subprocess, read_end
- @patch("airflow.sdk.execution_time.secrets_masker._secrets_masker")
+ @patch("airflow.sdk.execution_time.supervisor.mask_secret")
@pytest.mark.parametrize(
- ["message", "expected_buffer", "client_attr_path", "method_arg",
"method_kwarg", "mock_response"],
+ [
+ "message",
+ "expected_buffer",
+ "client_attr_path",
+ "method_arg",
+ "method_kwarg",
+ "mock_response",
+ "mask_secret_args",
+ ],
[
pytest.param(
GetConnection(conn_id="test_conn"),
@@ -988,8 +995,19 @@ class TestHandleRequest:
("test_conn",),
{},
ConnectionResult(conn_id="test_conn", conn_type="mysql"),
+ None,
id="get_connection",
),
+ pytest.param(
+ GetConnection(conn_id="test_conn"),
+
b'{"conn_id":"test_conn","conn_type":"mysql","password":"password","type":"ConnectionResult"}\n',
+ "connections.get",
+ ("test_conn",),
+ {},
+ ConnectionResult(conn_id="test_conn", conn_type="mysql",
password="password"),
+ ["password"],
+ id="get_connection_with_password",
+ ),
pytest.param(
GetConnection(conn_id="test_conn"),
b'{"conn_id":"test_conn","conn_type":"mysql","schema":"mysql","type":"ConnectionResult"}\n',
@@ -997,6 +1015,7 @@ class TestHandleRequest:
("test_conn",),
{},
ConnectionResult(conn_id="test_conn", conn_type="mysql",
schema="mysql"), # type: ignore[call-arg]
+ None,
id="get_connection_with_alias",
),
pytest.param(
@@ -1006,6 +1025,7 @@ class TestHandleRequest:
("test_key",),
{},
VariableResult(key="test_key", value="test_value"),
+ ["test_value", "test_key"],
id="get_variable",
),
pytest.param(
@@ -1015,6 +1035,7 @@ class TestHandleRequest:
("test_key", "test_value", "test_description"),
{},
OKResponse(ok=True),
+ None,
id="set_variable",
),
pytest.param(
@@ -1024,6 +1045,7 @@ class TestHandleRequest:
("test_key",),
{},
OKResponse(ok=True),
+ None,
id="delete_variable",
),
pytest.param(
@@ -1033,6 +1055,7 @@ class TestHandleRequest:
(TI_ID, DeferTask(next_method="execute_callback",
classpath="my-classpath")),
{},
"",
+ None,
id="patch_task_instance_to_deferred",
),
pytest.param(
@@ -1051,6 +1074,7 @@ class TestHandleRequest:
),
{},
"",
+ None,
id="patch_task_instance_to_up_for_reschedule",
),
pytest.param(
@@ -1060,6 +1084,7 @@ class TestHandleRequest:
("test_dag", "test_run", "test_task", "test_key", None, False),
{},
XComResult(key="test_key", value="test_value"),
+ None,
id="get_xcom",
),
pytest.param(
@@ -1071,6 +1096,7 @@ class TestHandleRequest:
("test_dag", "test_run", "test_task", "test_key", 2, False),
{},
XComResult(key="test_key", value="test_value"),
+ None,
id="get_xcom_map_index",
),
pytest.param(
@@ -1080,6 +1106,7 @@ class TestHandleRequest:
("test_dag", "test_run", "test_task", "test_key", None, False),
{},
XComResult(key="test_key", value=None, type="XComResult"),
+ None,
id="get_xcom_not_found",
),
pytest.param(
@@ -1095,6 +1122,7 @@ class TestHandleRequest:
("test_dag", "test_run", "test_task", "test_key", None, True),
{},
XComResult(key="test_key", value=None, type="XComResult"),
+ None,
id="get_xcom_include_prior_dates",
),
pytest.param(
@@ -1118,6 +1146,7 @@ class TestHandleRequest:
),
{},
OKResponse(ok=True),
+ None,
id="set_xcom",
),
pytest.param(
@@ -1142,6 +1171,7 @@ class TestHandleRequest:
),
{},
OKResponse(ok=True),
+ None,
id="set_xcom_with_map_index",
),
pytest.param(
@@ -1167,6 +1197,7 @@ class TestHandleRequest:
),
{},
OKResponse(ok=True),
+ None,
id="set_xcom_with_map_index_and_mapped_length",
),
pytest.param(
@@ -1188,6 +1219,7 @@ class TestHandleRequest:
),
{},
OKResponse(ok=True),
+ None,
id="delete_xcom",
),
# we aren't adding all states under TaskInstanceState here,
because this test's scope is only to check
@@ -1199,6 +1231,7 @@ class TestHandleRequest:
(),
{},
"",
+ None,
id="patch_task_instance_to_skipped",
),
pytest.param(
@@ -1214,6 +1247,7 @@ class TestHandleRequest:
"rendered_map_index": "test retry task",
},
"",
+ None,
id="up_for_retry",
),
pytest.param(
@@ -1223,6 +1257,7 @@ class TestHandleRequest:
(TI_ID, {"field1": "rendered_value1", "field2":
"rendered_value2"}),
{},
OKResponse(ok=True),
+ None,
id="set_rtif",
),
pytest.param(
@@ -1232,6 +1267,7 @@ class TestHandleRequest:
[],
{"name": "asset"},
AssetResult(name="asset", uri="s3://bucket/obj",
group="asset"),
+ None,
id="get_asset_by_name",
),
pytest.param(
@@ -1241,6 +1277,7 @@ class TestHandleRequest:
[],
{"uri": "s3://bucket/obj"},
AssetResult(name="asset", uri="s3://bucket/obj",
group="asset"),
+ None,
id="get_asset_by_uri",
),
pytest.param(
@@ -1263,6 +1300,7 @@ class TestHandleRequest:
)
]
),
+ None,
id="get_asset_events_by_uri_and_name",
),
pytest.param(
@@ -1285,6 +1323,7 @@ class TestHandleRequest:
)
]
),
+ None,
id="get_asset_events_by_uri",
),
pytest.param(
@@ -1307,6 +1346,7 @@ class TestHandleRequest:
)
]
),
+ None,
id="get_asset_events_by_name",
),
pytest.param(
@@ -1329,6 +1369,7 @@ class TestHandleRequest:
)
]
),
+ None,
id="get_asset_events_by_asset_alias",
),
pytest.param(
@@ -1346,6 +1387,7 @@ class TestHandleRequest:
"rendered_map_index": "test success task",
},
"",
+ None,
id="succeed_task",
),
pytest.param(
@@ -1364,6 +1406,7 @@ class TestHandleRequest:
data_interval_start=timezone.parse("2025-01-10T12:00:00Z"),
data_interval_end=timezone.parse("2025-01-10T14:00:00Z"),
),
+ None,
id="get_prev_successful_dagrun",
),
pytest.param(
@@ -1379,6 +1422,7 @@ class TestHandleRequest:
("test_dag", "test_run", {"key": "value"},
timezone.datetime(2025, 1, 1), True),
{},
OKResponse(ok=True),
+ None,
id="dag_run_trigger",
),
pytest.param(
@@ -1388,6 +1432,7 @@ class TestHandleRequest:
("test_dag", "test_run", None, None, False),
{},
ErrorResponse(error=ErrorType.DAGRUN_ALREADY_EXISTS),
+ None,
id="dag_run_trigger_already_exists",
),
pytest.param(
@@ -1397,6 +1442,7 @@ class TestHandleRequest:
("test_dag", "test_run"),
{},
DagRunStateResult(state=DagRunState.RUNNING),
+ None,
id="get_dag_run_state",
),
pytest.param(
@@ -1406,6 +1452,7 @@ class TestHandleRequest:
(TI_ID, 1),
{},
TaskRescheduleStartDate(start_date=timezone.parse("2024-10-31T12:00:00Z")),
+ None,
id="get_task_reschedule_start_date",
),
pytest.param(
@@ -1423,6 +1470,7 @@ class TestHandleRequest:
"task_ids": ["task1", "task2"],
},
TICount(count=2),
+ None,
id="get_ti_count",
),
pytest.param(
@@ -1437,6 +1485,7 @@ class TestHandleRequest:
"states": ["success", "failed"],
},
DRCount(count=2),
+ None,
id="get_dr_count",
),
pytest.param(
@@ -1453,6 +1502,7 @@ class TestHandleRequest:
"task_group_id": "test_group",
},
TaskStatesResult(task_states={"run_id": {"task1": "success",
"task2": "failed"}}),
+ None,
id="get_task_states",
),
pytest.param(
@@ -1468,6 +1518,7 @@ class TestHandleRequest:
("test_dag", "test_run", "test_task", "test_key", 0),
{},
XComResult(key="test_key", value="test_value"),
+ None,
id="get_xcom_seq_item",
),
pytest.param(
@@ -1483,13 +1534,14 @@ class TestHandleRequest:
("test_dag", "test_run", "test_task", "test_key", 2),
{},
ErrorResponse(error=ErrorType.XCOM_NOT_FOUND),
+ None,
id="get_xcom_seq_item_not_found",
),
],
)
def test_handle_requests(
self,
- mock_secrets_masker,
+ mock_mask_secret,
watched_subprocess,
mocker,
time_machine,
@@ -1499,6 +1551,7 @@ class TestHandleRequest:
method_arg,
method_kwarg,
mock_response,
+ mask_secret_args,
):
"""
Test handling of different messages to the subprocess. For any new
message type, add a
@@ -1511,7 +1564,6 @@ class TestHandleRequest:
3. Checks that the buffer is updated with the expected response.
4. Verifies that the response is correctly decoded.
"""
- mock_secrets_masker.return_value = SecretsMasker()
watched_subprocess, read_socket = watched_subprocess
# Mock the client method. E.g. `client.variables.get` or
`client.connections.get`
@@ -1524,6 +1576,10 @@ class TestHandleRequest:
next(generator)
msg = message.model_dump_json().encode() + b"\n"
generator.send(msg)
+
+ if mask_secret_args:
+ mock_mask_secret.assert_called_with(*mask_secret_args)
+
time_machine.move_to(timezone.datetime(2024, 10, 31), tick=False)
# Verify the correct client method was called