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

henry3260 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 8b4e4df8056 Keep the URL scheme out of path masking in airflow info 
--anonymize (#73599)
8b4e4df8056 is described below

commit 8b4e4df805665ec69aa99f21a85f94c4a9f131cf
Author: wiasliaw <[email protected]>
AuthorDate: Wed Sep 23 17:24:23 2026 +0800

    Keep the URL scheme out of path masking in airflow info --anonymize (#73599)
    
    Since #73179 a netloc-less connection string is masked through the
    unanchored username substitution, so an OS username that happens to be
    a substring of the scheme (e.g. "lite" or "db") corrupts the scheme
    itself: "sqlite:" becomes "sq${USER}:". Splitting on the first colon
    puts the scheme out of the substitution's reach; non-URL fallback
    values such as "NOT AVAILABLE" stay intact for the same reason.
    
    Suggested as an optional follow-up in the review of #73179.
---
 .../src/airflow/cli/commands/info_command.py       |  6 ++--
 .../tests/unit/cli/commands/test_info_command.py   | 37 ++++++++++++++++++++++
 2 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/airflow-core/src/airflow/cli/commands/info_command.py 
b/airflow-core/src/airflow/cli/commands/info_command.py
index f5d9f22e0c1..3f9f81f56ae 100644
--- a/airflow-core/src/airflow/cli/commands/info_command.py
+++ b/airflow-core/src/airflow/cli/commands/info_command.py
@@ -120,8 +120,10 @@ class PiiAnonymizer(Anonymizer):
                 netloc = ""
         else:
             # A netloc-less URL is a local-file backend (SQLite is the 
default), where
-            # the path itself is the identifying information.
-            return self.process_path(value)
+            # the path itself is the identifying information. Split off the 
scheme so the
+            # unanchored username substitution cannot touch it ("sqlite:" -> 
"sq${USER}:").
+            scheme, sep, rest = value.partition(":")
+            return f"{scheme}{sep}{self.process_path(rest)}"
 
         return urlunsplit((url_parts.scheme, netloc, url_parts.path, 
url_parts.query, url_parts.fragment))
 
diff --git a/airflow-core/tests/unit/cli/commands/test_info_command.py 
b/airflow-core/tests/unit/cli/commands/test_info_command.py
index 7f1cba4d834..321a2ae2531 100644
--- a/airflow-core/tests/unit/cli/commands/test_info_command.py
+++ b/airflow-core/tests/unit/cli/commands/test_info_command.py
@@ -21,6 +21,7 @@ import importlib
 import logging
 import os
 from io import StringIO
+from unittest import mock
 
 import pytest
 
@@ -73,6 +74,42 @@ class TestPiiAnonymizer:
     def test_should_remove_pii_from_url(self, before, after):
         assert after == self.instance.process_url(before)
 
+    @pytest.mark.parametrize(
+        ("username", "home", "before", "after"),
+        [
+            (
+                "lite",
+                "/home/lite",
+                "sqlite:////home/lite/airflow.db",
+                "sqlite:///${HOME}/airflow.db",
+            ),
+            (
+                "db",
+                "/home/db",
+                "duckdb:////home/db/wh.duckdb",
+                "duckdb:///${HOME}/wh.duck${USER}",
+            ),
+            (
+                "AVA",
+                "/home/AVA",
+                "NOT AVAILABLE",
+                "NOT AVAILABLE",
+            ),
+        ],
+    )
+    @mock.patch("airflow.cli.commands.info_command.os.path.expanduser", 
autospec=True)
+    @mock.patch("airflow.cli.commands.info_command.getuser", autospec=True)
+    def test_should_leave_scheme_out_of_path_masking(
+        self, mock_getuser, mock_expanduser, username, home, before, after
+    ):
+        # The scheme of a netloc-less URL must stay out of reach of the 
unanchored
+        # username substitution: a user named "lite" must not turn "sqlite:" 
into
+        # "sq${USER}:". Anything before the first colon is exempt from masking,
+        # which also keeps non-URL fallback values like "NOT AVAILABLE" intact.
+        mock_getuser.return_value = username
+        mock_expanduser.return_value = home
+        assert after == info_command.PiiAnonymizer().process_url(before)
+
 
 class TestAirflowInfo:
     @classmethod

Reply via email to