diff --git a/airflow-core/newsfragments/59844.improvement.rst b/airflow-core/newsfragments/59844.improvement.rst new file mode 100644 index 0000000000000..8dc8fd554f60a --- /dev/null +++ b/airflow-core/newsfragments/59844.improvement.rst @@ -0,0 +1 @@ +Prevent airflow connections list from exposing sensitive connection values by default. diff --git a/airflow-core/src/airflow/cli/commands/connection_command.py b/airflow-core/src/airflow/cli/commands/connection_command.py index 7ce4e60b2481c..d8b87f71715fe 100644 --- a/airflow-core/src/airflow/cli/commands/connection_command.py +++ b/airflow-core/src/airflow/cli/commands/connection_command.py @@ -78,6 +78,18 @@ def connections_get(args): ) +def _connection_list_mapper(conn: Connection) -> dict[str, Any]: + return { + "id": conn.id, + "conn_id": conn.conn_id, + "conn_type": conn.conn_type, + "description": conn.description, + "host": conn.host, + "schema": conn.schema, + "port": conn.port, + } + + @suppress_logs_and_warning @providers_configuration_loaded def connections_list(args): @@ -89,7 +101,7 @@ def connections_list(args): AirflowConsole().print_as( data=conns, output=args.output, - mapper=_connection_mapper, + mapper=_connection_list_mapper, ) diff --git a/airflow-core/tests/unit/cli/commands/test_connection_command.py b/airflow-core/tests/unit/cli/commands/test_connection_command.py index 8a931d5efa5c2..98f15a817ef6e 100644 --- a/airflow-core/tests/unit/cli/commands/test_connection_command.py +++ b/airflow-core/tests/unit/cli/commands/test_connection_command.py @@ -16,6 +16,7 @@ # under the License. from __future__ import annotations +import argparse import json import os import re @@ -28,6 +29,7 @@ from airflow.cli import cli_config, cli_parser from airflow.cli.commands import connection_command +from airflow.cli.commands.connection_command import connections_list from airflow.exceptions import AirflowException from airflow.models import Connection from airflow.utils.db import merge_conn @@ -1023,3 +1025,38 @@ def test_cli_create_default_connections(self, mocker): )["create-default-connections"] create_default_connection_fnc(()) mock_db_create_default_connections.assert_called_once() + + +def test_connections_list_hides_sensitive_values(session, capsys): + conn = Connection( + conn_id="test_conn", + conn_type="generic", + login="user", + password="password", + host="localhost", + port=5432, + schema="db", + ) + session.add(conn) + session.commit() + + args = argparse.Namespace( + output="table", + verbose=False, + log_level="INFO", + subcommand="connections list", + cfg_path=None, + ) + + connections_list(args) + + captured = capsys.readouterr() + stdout = captured.out + + # Non-sensitive identifier should be present + assert "test_conn" in stdout + + # Sensitive values must not be present + assert "user" not in stdout + assert "password" not in stdout + assert "generic://user:password" not in stdout