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(