From 6d412da5c8a8f80b33635c34d5f0192ee5c541ee Mon Sep 17 00:00:00 2001 From: "Gary T. Giesen" Date: Sat, 4 Jul 2026 16:13:55 -0400 Subject: [PATCH 1/2] Fix mysql.db_remove protection for information_schema The system-database guard in db_remove compared the name against the misspelled "information_scheme", so the check never matched and the real information_schema database was not protected from removal. Fixes #54938 --- changelog/54938.fixed.md | 1 + salt/modules/mysql.py | 2 +- tests/pytests/unit/modules/test_mysql.py | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 changelog/54938.fixed.md diff --git a/changelog/54938.fixed.md b/changelog/54938.fixed.md new file mode 100644 index 000000000000..4977bc950fb1 --- /dev/null +++ b/changelog/54938.fixed.md @@ -0,0 +1 @@ +Fixed mysql.db_remove so it correctly refuses to drop the information_schema system database, which was previously misspelled as information_scheme. diff --git a/salt/modules/mysql.py b/salt/modules/mysql.py index 55ba7fde0737..141b48bf73de 100644 --- a/salt/modules/mysql.py +++ b/salt/modules/mysql.py @@ -1364,7 +1364,7 @@ def db_remove(name, **connection_args): log.info("DB '%s' does not exist", name) return False - if name in ("mysql", "information_scheme"): + if name in ("mysql", "information_schema"): log.info("DB '%s' may not be removed", name) return False diff --git a/tests/pytests/unit/modules/test_mysql.py b/tests/pytests/unit/modules/test_mysql.py index 2023f649877f..cdd5d92dc170 100644 --- a/tests/pytests/unit/modules/test_mysql.py +++ b/tests/pytests/unit/modules/test_mysql.py @@ -483,6 +483,21 @@ def test_db_remove(): _test_call(mysql.db_remove, "DROP DATABASE `test``'\" db`;", "test`'\" db") +def test_db_remove_system_db(): + """ + Test that MySQL db_remove refuses to drop the protected system databases + and never issues a DROP for them (regression test for #54938 where + "information_schema" was misspelled as "information_scheme"). + """ + for name in ("mysql", "information_schema"): + connect_mock = MagicMock() + with patch.object( + mysql, "db_exists", MagicMock(return_value=True) + ), patch.object(mysql, "_connect", connect_mock): + assert mysql.db_remove(name) is False + connect_mock.assert_not_called() + + def test_db_tables(): """ Test MySQL db_tables function in mysql exec module From fb0ebed1b807a92efc8e00e85a1d8134e3bc0e9e Mon Sep 17 00:00:00 2001 From: "Gary T. Giesen" Date: Sun, 5 Jul 2026 23:19:15 -0400 Subject: [PATCH 2/2] Add direct and inverse regression tests for mysql.db_remove system-database guard The direct test verifies db_remove issues DROP DATABASE for a user database literally named information_scheme (the misspelling formerly stuck in the guard), called with just the database name and empty connection_args exactly as the production caller mysql_database.absent() does; it fails when the source fix is reverted. The inverse test guards against overcorrection by asserting databases whose names merely resemble the protected system databases (mysql_backup, information_schema_old) are still dropped, and passes with and without the fix. Claude-Session: https://claude.ai/code/session_01MF2AuQNhBZg4HDt1x6xxCu --- tests/pytests/unit/modules/test_mysql.py | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/pytests/unit/modules/test_mysql.py b/tests/pytests/unit/modules/test_mysql.py index cdd5d92dc170..93af2d042552 100644 --- a/tests/pytests/unit/modules/test_mysql.py +++ b/tests/pytests/unit/modules/test_mysql.py @@ -498,6 +498,33 @@ def test_db_remove_system_db(): connect_mock.assert_not_called() +def test_db_remove_allows_db_named_information_scheme_54938(): + """ + Test that db_remove issues DROP DATABASE for a user database literally + named "information_scheme" (the misspelling that used to sit in the + system-database guard before #54938 was fixed). Called the same way the + production caller mysql_database.absent() calls it: just the database + name, with connection_args empty. + """ + with patch.object(mysql, "db_exists", MagicMock(return_value=True)): + _test_call( + mysql.db_remove, "DROP DATABASE `information_scheme`;", "information_scheme" + ) + + +def test_db_remove_does_not_block_similar_names_54938(): + """ + Guard against overcorrection of the #54938 fix: db_remove must still + issue DROP DATABASE for user databases whose names merely resemble the + protected system databases. This test passes with and without the fix + applied. Like the production caller mysql_database.absent(), db_remove + is called with just the database name (connection_args empty). + """ + for name in ("mysql_backup", "information_schema_old"): + with patch.object(mysql, "db_exists", MagicMock(return_value=True)): + _test_call(mysql.db_remove, f"DROP DATABASE `{name}`;", name) + + def test_db_tables(): """ Test MySQL db_tables function in mysql exec module