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

potiuk pushed a commit to branch v2-11-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v2-11-test by this push:
     new 819581c2787 Masking details while creating connections using json & 
uri (#61882)
819581c2787 is described below

commit 819581c2787c07e7f6ccc7225db5b420ca66b9c8
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sat Feb 14 12:21:54 2026 +0100

    Masking details while creating connections using json & uri (#61882)
---
 airflow/utils/cli.py         | 34 +++++++++++++++++++++++++++++++++-
 tests/utils/test_cli_util.py | 43 +++++++++++++++++++++++++++++++++++++------
 2 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/airflow/utils/cli.py b/airflow/utils/cli.py
index 74f0dc79f7b..fb72b667fd7 100644
--- a/airflow/utils/cli.py
+++ b/airflow/utils/cli.py
@@ -161,7 +161,39 @@ def _build_metrics(func_name, namespace):
                 for sensitive_field in sensitive_fields:
                     if command.startswith(f"{sensitive_field}="):
                         full_command[idx] = f'{sensitive_field}={"*" * 8}'
-
+    # handle conn-json and conn-uri separately as it requires different 
handling
+    if "--conn-json" in full_command:
+        import json
+
+        json_index = full_command.index("--conn-json") + 1
+        conn_json = json.loads(full_command[json_index])
+        for k in conn_json:
+            if k and should_hide_value_for_key(k):
+                conn_json[k] = "*" * 8
+        full_command[json_index] = json.dumps(conn_json)
+
+    if "--conn-uri" in full_command:
+        from urllib.parse import urlparse, urlunparse
+
+        uri_index = full_command.index("--conn-uri") + 1
+        conn_uri = full_command[uri_index]
+        parsed_uri = urlparse(conn_uri)
+        if parsed_uri.password:
+            password = "*" * 8
+            netloc = f"{parsed_uri.username}:{password}@{parsed_uri.hostname}"
+            if parsed_uri.port:
+                netloc += f":{parsed_uri.port}"
+
+        full_command[uri_index] = urlunparse(
+            (
+                parsed_uri.scheme,
+                netloc,
+                parsed_uri.path,
+                parsed_uri.params,
+                parsed_uri.query,
+                parsed_uri.fragment,
+            )
+        )
     metrics = {
         "sub_command": func_name,
         "start_datetime": timezone.utcnow(),
diff --git a/tests/utils/test_cli_util.py b/tests/utils/test_cli_util.py
index bf5fd9bacb3..a05d9ead883 100644
--- a/tests/utils/test_cli_util.py
+++ b/tests/utils/test_cli_util.py
@@ -93,47 +93,78 @@ class TestCliUtil:
             cli.get_dags(None, "foobar", True)
 
     @pytest.mark.parametrize(
-        ["given_command", "expected_masked_command"],
+        ["given_command", "expected_masked_command", "is_command_list"],
         [
             (
                 "airflow users create -u test2 -l doe -f jon -e 
[email protected] -r admin --password test",
                 "airflow users create -u test2 -l doe -f jon -e 
[email protected] -r admin --password ********",
+                False,
             ),
             (
                 "airflow users create -u test2 -l doe -f jon -e 
[email protected] -r admin -p test",
                 "airflow users create -u test2 -l doe -f jon -e 
[email protected] -r admin -p ********",
+                False,
             ),
             (
                 "airflow users create -u test2 -l doe -f jon -e 
[email protected] -r admin --password=test",
                 "airflow users create -u test2 -l doe -f jon -e 
[email protected] -r admin --password=********",
+                False,
             ),
             (
                 "airflow users create -u test2 -l doe -f jon -e 
[email protected] -r admin -p=test",
                 "airflow users create -u test2 -l doe -f jon -e 
[email protected] -r admin -p=********",
+                False,
             ),
             (
                 "airflow connections add dsfs --conn-login asd --conn-password 
test --conn-type google",
                 "airflow connections add dsfs --conn-login asd --conn-password 
******** --conn-type google",
+                False,
+            ),
+            (
+                "airflow connections add my_postgres_conn --conn-uri 
postgresql://user:my-password@localhost:5432/mydatabase",
+                "airflow connections add my_postgres_conn --conn-uri 
postgresql://user:********@localhost:5432/mydatabase",
+                False,
+            ),
+            (
+                [
+                    "airflow",
+                    "connections",
+                    "add",
+                    "my_new_conn",
+                    "--conn-json",
+                    '{"conn_type": "my-conn-type", "login": "my-login", 
"password": "my-password", "host": "my-host", "port": 1234, "schema": 
"my-schema", "extra": {"param1": "val1", "param2": "val2"}}',
+                ],
+                [
+                    "airflow",
+                    "connections",
+                    "add",
+                    "my_new_conn",
+                    "--conn-json",
+                    '{"conn_type": "my-conn-type", "login": "my-login", 
"password": "********", '
+                    '"host": "my-host", "port": 1234, "schema": "my-schema", 
"extra": {"param1": '
+                    '"val1", "param2": "val2"}}',
+                ],
+                True,
             ),
             (
                 "airflow scheduler -p",
                 "airflow scheduler -p",
+                False,
             ),
             (
                 "airflow celery flower -p 8888",
                 "airflow celery flower -p 8888",
+                False,
             ),
         ],
     )
     def test_cli_create_user_supplied_password_is_masked(
-        self, given_command, expected_masked_command, session
+        self, given_command, expected_masked_command, is_command_list, session
     ):
         # '-p' value which is not password, like 'airflow scheduler -p'
         # or 'airflow celery flower -p 8888', should not be masked
-        args = given_command.split()
-
-        expected_command = expected_masked_command.split()
-
+        args = given_command if is_command_list else given_command.split()
+        expected_command = expected_masked_command if is_command_list else 
expected_masked_command.split()
         exec_date = timezone.utcnow()
         namespace = Namespace(dag_id="foo", task_id="bar", subcommand="test", 
execution_date=exec_date)
         with mock.patch.object(sys, "argv", args), mock.patch(

Reply via email to