This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch v3-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-3-test by this push:
new 762fd74c135 [v3-3-test] Mask the SQLite database path in airflow info
--anonymize (#73179, #73599) (#73603)
762fd74c135 is described below
commit 762fd74c1351865590c5daba66e9115057b3c2a9
Author: Henry Chen <[email protected]>
AuthorDate: Wed Sep 23 18:47:52 2026 +0800
[v3-3-test] Mask the SQLite database path in airflow info --anonymize
(#73179, #73599) (#73603)
* Mask the SQLite database path in airflow info --anonymize (#73179)
The whole point of --anonymize is that the report is safe to paste into a
public bug report. SQLite is the default backend, so the people most likely
to rely on that promise are exactly the ones it was broken for.
Co-authored-by: Eason09053360
<[email protected]>
(cherry picked from commit 41480acd26db7ef2f8ff1eab724be9b0f0840374)
* 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.
(cherry picked from commit 8b4e4df805665ec69aa99f21a85f94c4a9f131cf)
---------
Co-authored-by: Y-C <[email protected]>
Co-authored-by: Eason09053360
<[email protected]>
Co-authored-by: wiasliaw <[email protected]>
---
.../src/airflow/cli/commands/info_command.py | 7 +++-
.../tests/unit/cli/commands/test_info_command.py | 44 ++++++++++++++++++++++
2 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/airflow-core/src/airflow/cli/commands/info_command.py
b/airflow-core/src/airflow/cli/commands/info_command.py
index 79c4ae8b7f3..b5da90a7335 100644
--- a/airflow-core/src/airflow/cli/commands/info_command.py
+++ b/airflow-core/src/airflow/cli/commands/info_command.py
@@ -92,7 +92,6 @@ class PiiAnonymizer(Anonymizer):
return value
url_parts = urlsplit(value)
- netloc = None
if url_parts.netloc:
# unpack
userinfo = None
@@ -124,6 +123,12 @@ class PiiAnonymizer(Anonymizer):
netloc = host
else:
netloc = ""
+ else:
+ # A netloc-less URL is a local-file backend (SQLite is the
default), where
+ # 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 2dc3d60e4e6..98113912668 100644
--- a/airflow-core/tests/unit/cli/commands/test_info_command.py
+++ b/airflow-core/tests/unit/cli/commands/test_info_command.py
@@ -60,11 +60,55 @@ class TestPiiAnonymizer:
"postgresql+psycopg2://postgres/airflow",
"postgresql+psycopg2://postgres/airflow",
),
+ (
+ f"sqlite:///{os.path.expanduser('~/airflow/airflow.db')}",
+ "sqlite:///${HOME}/airflow/airflow.db",
+ ),
+ (
+ "sqlite:///relative.db",
+ "sqlite:///relative.db",
+ ),
],
)
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