2323 decrypt_remote_file_to_string ,
2424 generate_encrypted_file_with_openssl ,
2525 generate_random_password ,
26- shell_quote_single ,
2726)
2827
2928
3029class TestEncryptionUtils :
3130 def test_generate_random_password_length (self ):
3231 pwd = generate_random_password (16 )
3332 assert len (pwd ) == 16
34- # Check characters are in allowed set
3533 allowed_chars = string .ascii_letters + string .digits + string .punctuation
36- assert ( all (c in allowed_chars for c in pwd ) ) is True
34+ assert all (c in allowed_chars for c in pwd ) is True
3735
3836 @patch ("subprocess.run" )
3937 def test_generate_encrypted_file_with_openssl_calls_subprocess (self , mock_run ):
@@ -72,16 +70,6 @@ def test_generate_encrypted_file_passphrase_not_on_argv(self, mock_run):
7270 assert not any (password in str (part ) for part in cmd ), "passphrase leaked onto argv"
7371 assert kwargs ["input" ] == f"{ password } \n " .encode ()
7472
75- def test_shell_quote_single_simple (self ):
76- s = "simple"
77- quoted = shell_quote_single (s )
78- assert quoted == "'simple'"
79-
80- def test_shell_quote_single_with_single_quote (self ):
81- s = "O'Reilly"
82- quoted = shell_quote_single (s )
83- assert quoted == "'O'\\ ''Reilly'"
84-
8573 def test_decrypt_remote_file_to_string (self ):
8674 password = "mysecret"
8775 remote_enc_file = "/remote/encrypted.enc"
@@ -91,25 +79,42 @@ def test_decrypt_remote_file_to_string(self):
9179 mock_stdin = MagicMock ()
9280 mock_stdout = MagicMock ()
9381 mock_stderr = MagicMock ()
94-
95- # Setup mock outputs and exit code
9682 mock_stdout .channel .recv_exit_status .return_value = 0
9783 mock_stdout .read .return_value = b"decrypted output"
9884 mock_stderr .read .return_value = b""
99-
10085 ssh_client .exec_command .return_value = (mock_stdin , mock_stdout , mock_stderr )
10186
10287 exit_status , output , err = decrypt_remote_file_to_string (
10388 ssh_client , remote_enc_file , password , bteq_command_str
10489 )
10590
106- quoted_password = shell_quote_single (password )
10791 expected_cmd = (
108- f"openssl enc -d -aes-256-cbc -salt -pbkdf2 -pass pass: { quoted_password } -in { remote_enc_file } | "
92+ f"openssl enc -d -aes-256-cbc -salt -pbkdf2 -pass stdin -in { remote_enc_file } | "
10993 + bteq_command_str
11094 )
111-
11295 ssh_client .exec_command .assert_called_once_with (expected_cmd )
96+ mock_stdin .write .assert_called_once_with (password + "\n " )
97+ mock_stdin .flush .assert_called_once ()
98+ mock_stdin .channel .shutdown_write .assert_called_once ()
11399 assert exit_status == 0
114100 assert output == "decrypted output"
115101 assert err == ""
102+
103+ def test_decrypt_remote_file_passphrase_not_on_argv (self ):
104+ """The passphrase is passed via stdin, never on the remote command line."""
105+ password = "s3cr3t&rm -rf ~"
106+ remote_enc_file = "/remote/encrypted.enc"
107+ ssh_client = MagicMock ()
108+ mock_stdin = MagicMock ()
109+ mock_stdout = MagicMock ()
110+ mock_stderr = MagicMock ()
111+ mock_stdout .channel .recv_exit_status .return_value = 0
112+ mock_stdout .read .return_value = b""
113+ mock_stderr .read .return_value = b""
114+ ssh_client .exec_command .return_value = (mock_stdin , mock_stdout , mock_stderr )
115+
116+ decrypt_remote_file_to_string (ssh_client , remote_enc_file , password , "bteq" )
117+
118+ cmd = ssh_client .exec_command .call_args [0 ][0 ]
119+ assert password not in cmd , "passphrase leaked onto remote command line"
120+ assert "-pass stdin" in cmd
0 commit comments