This is an automated email from the ASF dual-hosted git repository.
bugraoz 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 9d83ec938c0 Add test for sensitive config masking in airflowctl
(#60361)
9d83ec938c0 is described below
commit 9d83ec938c0cfea4b1ea2d39ff93f8722812e2f8
Author: Subham <[email protected]>
AuthorDate: Sat Jan 31 23:30:47 2026 +0530
Add test for sensitive config masking in airflowctl (#60361)
* Add unittest to add a mechanism if adding to airflowctl preserves the
API's sensitive config masking
---
.../tests/airflow_ctl/api/test_operations.py | 26 +++++++++++
.../ctl/commands/test_config_command.py | 50 ++++++++++++++++++++++
2 files changed, 76 insertions(+)
diff --git a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
index 4e8c0ab7590..bb473dbc7d1 100644
--- a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
+++ b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
@@ -522,6 +522,32 @@ class TestConfigOperations:
response = client.configs.list()
assert response == response_config
+ def test_get_masked_value(self):
+ """Verify that masked values from API are preserved in get()
operation."""
+ response_config = Config(
+ sections=[
+ ConfigSection(
+ name=self.section,
+ options=[
+ ConfigOption(
+ key="sensitive_key",
+ value="< hidden >",
+ )
+ ],
+ )
+ ]
+ )
+
+ def handle_request(request: httpx.Request) -> httpx.Response:
+ assert request.url.path ==
f"/api/v2/config/section/{self.section}/option/sensitive_key"
+ return httpx.Response(200, json=response_config.model_dump())
+
+ client = make_api_client(transport=httpx.MockTransport(handle_request))
+ response = client.configs.get(section=self.section,
option="sensitive_key")
+
+ assert response == response_config
+ assert response.sections[0].options[0].value == "< hidden >"
+
class TestConnectionsOperations:
connection_id: str = "test_connection"
diff --git a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_config_command.py
b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_config_command.py
index f87a9851a52..731e30137c7 100644
--- a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_config_command.py
+++ b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_config_command.py
@@ -361,3 +361,53 @@ class TestCliConfigCommands:
calls = [call[0][0] for call in mock_rich_print.call_args_list]
assert "[red]Found issues in your airflow.cfg:[/red]" in calls[0]
assert "This is a test suggestion." in calls[1]
+
+ @patch("airflowctl.api.client.Credentials.load")
+ @patch.dict(os.environ, {"AIRFLOW_CLI_TOKEN": "TEST_TOKEN"})
+ @patch.dict(os.environ, {"AIRFLOW_CLI_ENVIRONMENT": "TEST_CONFIG"})
+ @patch("rich.print")
+ def test_config_list_masking_preservation(
+ self, mock_rich_print, _mock_credentials, api_client_maker, capsys
+ ):
+ """
+ Verify that sensitive values masked by the API (like '< hidden >') are
preserved
+ and displayed correctly by the CLI list command.
+ """
+ response_config = Config(
+ sections=[
+ ConfigSection(
+ name="core",
+ options=[
+ ConfigOption(key="parallelism", value="32"),
+ ConfigOption(key="fernet_key", value="< hidden >"),
+ ],
+ ),
+ ConfigSection(
+ name="database",
+ options=[
+ ConfigOption(key="sql_alchemy_conn", value="< hidden
>"),
+ ],
+ ),
+ ]
+ )
+
+ api_client = api_client_maker(
+ path="/api/v2/config",
+ response_json=response_config.model_dump(),
+ expected_http_status_code=200,
+ kind=ClientKind.CLI,
+ )
+ args = self.parser.parse_args(["config", "list"])
+ args.func(
+ args,
+ api_client=api_client,
+ )
+
+ # Output is printed to stdout by AirflowConsole (using rich)
+ captured = capsys.readouterr()
+ output_str = captured.out
+
+ # Check output contains masked vales
+ assert "fernet_key" in output_str
+ assert "< hidden >" in output_str
+ assert "sql_alchemy_conn" in output_str