Explorar o código

Fix a regression in which some MariaDB/MySQL passwords were not escaped correctly (#1017).

Dan Helfman hai 3 meses
pai
achega
1d486d024b

+ 1 - 0
NEWS

@@ -3,6 +3,7 @@
    incident UI. See the documentation for more information:
    https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#pagerduty-hook
  * #936: Clarify Zabbix monitoring hook documentation about creating items.
+ * #1017: Fix a regression in which some MariaDB/MySQL passwords were not escaped correctly.
 
 1.9.13
  * #975: Add a "compression" option to the PostgreSQL database hook.

+ 3 - 1
borgmatic/hooks/data_source/mariadb.py

@@ -65,10 +65,12 @@ def make_defaults_file_options(username=None, password=None, defaults_extra_file
     Do not use the returned value for multiple different command invocations. That will not work
     because each pipe is "used up" once read.
     '''
+    escaped_password = None if password is None else password.replace('\\', '\\\\')
+
     values = '\n'.join(
         (
             (f'user={username}' if username is not None else ''),
-            (f"password='{password}'" if password is not None else ''),
+            (f'password="{escaped_password}"' if escaped_password is not None else ''),
         )
     ).strip()
 

+ 19 - 3
tests/unit/hooks/data_source/test_mariadb.py

@@ -36,7 +36,7 @@ def test_make_defaults_file_option_with_username_and_password_writes_them_to_fil
 
     flexmock(module.os).should_receive('pipe').and_return(read_descriptor, write_descriptor)
     flexmock(module.os).should_receive('write').with_args(
-        write_descriptor, b"[client]\nuser=root\npassword='trustsome1'"
+        write_descriptor, b'[client]\nuser=root\npassword="trustsome1"'
     ).once()
     flexmock(module.os).should_receive('close')
     flexmock(module.os).should_receive('set_inheritable')
@@ -46,6 +46,22 @@ def test_make_defaults_file_option_with_username_and_password_writes_them_to_fil
     )
 
 
+def test_make_defaults_file_option_escapes_password_containing_backslash():
+    read_descriptor = 99
+    write_descriptor = flexmock()
+
+    flexmock(module.os).should_receive('pipe').and_return(read_descriptor, write_descriptor)
+    flexmock(module.os).should_receive('write').with_args(
+        write_descriptor, b'[client]\nuser=root\n' + br'password="trust\\nsome1"'
+    ).once()
+    flexmock(module.os).should_receive('close')
+    flexmock(module.os).should_receive('set_inheritable')
+
+    assert module.make_defaults_file_options(username='root', password=r'trust\nsome1') == (
+        '--defaults-extra-file=/dev/fd/99',
+    )
+
+
 def test_make_defaults_file_pipe_with_only_username_writes_it_to_file_descriptor():
     read_descriptor = 99
     write_descriptor = flexmock()
@@ -68,7 +84,7 @@ def test_make_defaults_file_pipe_with_only_password_writes_it_to_file_descriptor
 
     flexmock(module.os).should_receive('pipe').and_return(read_descriptor, write_descriptor)
     flexmock(module.os).should_receive('write').with_args(
-        write_descriptor, b"[client]\npassword='trustsome1'"
+        write_descriptor, b'[client]\npassword="trustsome1"'
     ).once()
     flexmock(module.os).should_receive('close')
     flexmock(module.os).should_receive('set_inheritable')
@@ -84,7 +100,7 @@ def test_make_defaults_file_option_with_defaults_extra_filename_includes_it_in_f
 
     flexmock(module.os).should_receive('pipe').and_return(read_descriptor, write_descriptor)
     flexmock(module.os).should_receive('write').with_args(
-        write_descriptor, b"!include extra.cnf\n[client]\nuser=root\npassword='trustsome1'"
+        write_descriptor, b'!include extra.cnf\n[client]\nuser=root\npassword="trustsome1"'
     ).once()
     flexmock(module.os).should_receive('close')
     flexmock(module.os).should_receive('set_inheritable')