Skip to content

Commit 919a753

Browse files
Fix: Mask credentials in duckdb attach for postgres, mysql logging
1 parent 725ebcc commit 919a753

File tree

2 files changed

+63
-14
lines changed

2 files changed

+63
-14
lines changed

sqlmesh/core/config/connection.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"clickhouse",
5959
}
6060
MOTHERDUCK_TOKEN_REGEX = re.compile(r"(\?|\&)(motherduck_token=)(\S*)")
61+
PASSWORD_REGEX = re.compile(r"(password=)(\S+)")
6162

6263

6364
def _get_engine_import_validator(
@@ -479,13 +480,13 @@ def create_engine_adapter(
479480
adapter = BaseDuckDBConnectionConfig._data_file_to_adapter.get(key)
480481
if adapter is not None:
481482
logger.info(
482-
f"Using existing DuckDB adapter due to overlapping data file: {self._mask_motherduck_token(key)}"
483+
f"Using existing DuckDB adapter due to overlapping data file: {self._mask_sensitive_data(key)}"
483484
)
484485
return adapter
485486

486487
if data_files:
487488
masked_files = {
488-
self._mask_motherduck_token(file if isinstance(file, str) else file.path)
489+
self._mask_sensitive_data(file if isinstance(file, str) else file.path)
489490
for file in data_files
490491
}
491492
logger.info(f"Creating new DuckDB adapter for data files: {masked_files}")
@@ -507,10 +508,14 @@ def get_catalog(self) -> t.Optional[str]:
507508
return list(self.catalogs)[0]
508509
return None
509510

510-
def _mask_motherduck_token(self, string: str) -> str:
511-
return MOTHERDUCK_TOKEN_REGEX.sub(
511+
def _mask_sensitive_data(self, string: str) -> str:
512+
# Mask MotherDuck tokens
513+
result = MOTHERDUCK_TOKEN_REGEX.sub(
512514
lambda m: f"{m.group(1)}{m.group(2)}{'*' * len(m.group(3))}", string
513515
)
516+
# Mask PostgreSQL and MySQL passwords
517+
result = PASSWORD_REGEX.sub(lambda m: f"{m.group(1)}{'*' * len(m.group(2))}", result)
518+
return result
514519

515520

516521
class MotherDuckConnectionConfig(BaseDuckDBConnectionConfig):

tests/core/test_connection_config.py

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -944,42 +944,86 @@ def test_motherduck_token_mask(make_config):
944944
assert isinstance(config_1, MotherDuckConnectionConfig)
945945
assert isinstance(config_2, MotherDuckConnectionConfig)
946946
assert isinstance(config_3, MotherDuckConnectionConfig)
947-
assert config_1._mask_motherduck_token(config_1.database) == "whodunnit"
947+
948+
# motherduck format
949+
assert config_1._mask_sensitive_data(config_1.database) == "whodunnit"
948950
assert (
949-
config_1._mask_motherduck_token(f"md:{config_1.database}?motherduck_token={config_1.token}")
951+
config_1._mask_sensitive_data(f"md:{config_1.database}?motherduck_token={config_1.token}")
950952
== "md:whodunnit?motherduck_token=*****"
951953
)
952954
assert (
953-
config_1._mask_motherduck_token(
955+
config_1._mask_sensitive_data(
954956
f"md:{config_1.database}?attach_mode=single&motherduck_token={config_1.token}"
955957
)
956958
== "md:whodunnit?attach_mode=single&motherduck_token=*****"
957959
)
958960
assert (
959-
config_2._mask_motherduck_token(f"md:{config_2.database}?motherduck_token={config_2.token}")
961+
config_2._mask_sensitive_data(f"md:{config_2.database}?motherduck_token={config_2.token}")
960962
== "md:whodunnit?motherduck_token=******************"
961963
)
962964
assert (
963-
config_3._mask_motherduck_token(f"md:?motherduck_token={config_3.token}")
965+
config_3._mask_sensitive_data(f"md:?motherduck_token={config_3.token}")
964966
== "md:?motherduck_token=**********"
965967
)
966968
assert (
967-
config_1._mask_motherduck_token("?motherduck_token=secret1235")
969+
config_1._mask_sensitive_data("?motherduck_token=secret1235")
968970
== "?motherduck_token=**********"
969971
)
970972
assert (
971-
config_1._mask_motherduck_token("md:whodunnit?motherduck_token=short")
973+
config_1._mask_sensitive_data("md:whodunnit?motherduck_token=short")
972974
== "md:whodunnit?motherduck_token=*****"
973975
)
974976
assert (
975-
config_1._mask_motherduck_token("md:whodunnit?motherduck_token=longtoken123456789")
977+
config_1._mask_sensitive_data("md:whodunnit?motherduck_token=longtoken123456789")
976978
== "md:whodunnit?motherduck_token=******************"
977979
)
978980
assert (
979-
config_1._mask_motherduck_token("md:whodunnit?motherduck_token=")
981+
config_1._mask_sensitive_data("md:whodunnit?motherduck_token=")
980982
== "md:whodunnit?motherduck_token="
981983
)
982-
assert config_1._mask_motherduck_token(":memory:") == ":memory:"
984+
assert config_1._mask_sensitive_data(":memory:") == ":memory:"
985+
986+
# postgres format
987+
assert (
988+
config_1._mask_sensitive_data(
989+
"postgres:dbname=mydb user=myuser password=secret123 host=localhost"
990+
)
991+
== "postgres:dbname=mydb user=myuser password=********* host=localhost"
992+
)
993+
994+
assert (
995+
config_1._mask_sensitive_data(
996+
"dbname=postgres user=postgres password=pg_secret host=127.0.0.1"
997+
)
998+
== "dbname=postgres user=postgres password=********* host=127.0.0.1"
999+
)
1000+
assert (
1001+
config_1._mask_sensitive_data(
1002+
"postgres:dbname=testdb password=verylongpassword123 user=admin"
1003+
)
1004+
== "postgres:dbname=testdb password=******************* user=admin"
1005+
)
1006+
assert config_1._mask_sensitive_data("postgres:password=short") == "postgres:password=*****"
1007+
assert (
1008+
config_1._mask_sensitive_data("postgres:host=localhost password=p@ssw0rd! dbname=db")
1009+
== "postgres:host=localhost password=********* dbname=db"
1010+
)
1011+
1012+
assert (
1013+
config_1._mask_sensitive_data("postgres:dbname=mydb user=myuser host=localhost")
1014+
== "postgres:dbname=mydb user=myuser host=localhost"
1015+
)
1016+
1017+
assert (
1018+
config_1._mask_sensitive_data("md:db?motherduck_token=token123 postgres:password=secret")
1019+
== "md:db?motherduck_token=******** postgres:password=******"
1020+
)
1021+
1022+
# MySQL format
1023+
assert (
1024+
config_1._mask_sensitive_data("host=localhost user=root password=mysql123 database=mydb")
1025+
== "host=localhost user=root password=******** database=mydb"
1026+
)
9831027

9841028

9851029
def test_bigquery(make_config):

0 commit comments

Comments
 (0)