Example #1
0
    def test_suppresses_and_retries_on_ssh_error_exchange_identification(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh run command raises error the first time, succeeds the second time
        # noinspection PyUnusedLocal
        def ssh_run_command(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count < 2:
                raise SshcpError(
                    "ssh_exchange_identification: read: Connection reset by peer"
                )
            else:
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_run_command

        scanner.scan()
        self.assertEqual(2, self.mock_ssh.shell.call_count)
Example #2
0
    def test_raises_nonrecoverable_error_on_failed_scan(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh run command raises error the first time, succeeds the second time
        # noinspection PyUnusedLocal
        def ssh_shell(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count == 1:
                # md5sum check
                return b''
            elif self.ssh_run_command_count == 2:
                # first try
                raise SshcpError("SystemScannerError: something failed")
            else:
                # later tries
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_shell

        with self.assertRaises(ScannerError) as ctx:
            scanner.scan()
        self.assertEqual(
            Localization.Error.REMOTE_SERVER_SCAN.format(
                "SystemScannerError: something failed"), str(ctx.exception))
        self.assertFalse(ctx.exception.recoverable)
Example #3
0
    def test_appends_script_name_to_remote_path(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan")

        self.ssh_run_command_count = 0

        # Ssh returns error for md5sum check, empty pickle dump for later commands
        def ssh_shell(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count == 1:
                # first try
                raise SshcpError("an ssh error")
            else:
                # later tries
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_shell

        scanner.scan()
        # check for appended path ('script')
        self.mock_ssh.copy.assert_called_once_with(
            local_path=TestRemoteScanner.temp_scan_script,
            remote_path="/remote/path/to/scan/script")
Example #4
0
    def test_calls_correct_ssh_md5sum_command(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh returns error for md5sum check, empty pickle dump for later commands
        def ssh_shell(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count == 1:
                # first try
                raise SshcpError("an ssh error")
            else:
                # later tries
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_shell

        scanner.scan()
        self.assertEqual(2, self.mock_ssh.shell.call_count)
        self.mock_ssh.shell.assert_has_calls([
            call(
                "echo 'd41d8cd98f00b204e9800998ecf8427e /remote/path/to/scan/script' | md5sum -c --quiet"
            ),
            call(ANY)
        ])
Example #5
0
    def test_suppresses_and_retries_on_ssh_error_cannot_create_temp_dir(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh run command raises error the first time, succeeds the second time
        # noinspection PyUnusedLocal
        def ssh_shell(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count < 2:
                raise SshcpError(
                    "[23033] INTERNAL ERROR: cannot create temporary directory!"
                )
            else:
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_shell

        scanner.scan()
        self.assertEqual(2, self.mock_ssh.shell.call_count)
Example #6
0
    def test_installs_scan_script_on_any_md5sum_output(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh returns error for md5sum check, empty pickle dump for later commands
        def ssh_shell(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count == 1:
                # first try
                return "some output from md5sum".encode()
            else:
                # later tries
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_shell

        scanner.scan()
        self.mock_ssh.copy.assert_called_once_with(
            local_path=TestRemoteScanner.temp_scan_script,
            remote_path="/remote/path/to/scan/script")
        self.mock_ssh.copy.reset_mock()
Example #7
0
    def test_raises_app_error_on_failed_ssh(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh run command raises error the first time, succeeds the second time
        # noinspection PyUnusedLocal
        def ssh_shell(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count < 2:
                raise SshcpError("an ssh error")
            else:
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_shell

        with self.assertRaises(AppError) as ctx:
            scanner.scan()
        self.assertEqual(Localization.Error.REMOTE_SERVER_SCAN,
                         str(ctx.exception))
Example #8
0
    def test_raises_nonrecoverable_error_on_md5sum_error(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh returns error for md5sum check, empty pickle dump for later commands
        def ssh_shell(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count == 1:
                # md5sum check
                raise SshcpError("an ssh error")
            else:
                # later tries
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_shell

        with self.assertRaises(ScannerError) as ctx:
            scanner.scan()
        self.assertEqual(
            Localization.Error.REMOTE_SERVER_INSTALL.format("an ssh error"),
            str(ctx.exception))
        self.assertFalse(ctx.exception.recoverable)
Example #9
0
    def test_calls_correct_ssh_scan_command(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh returns error for md5sum check, empty pickle dump for later commands
        def ssh_shell(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count == 1:
                # md5sum check
                return b''
            else:
                # later tries
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_shell

        scanner.scan()
        self.assertEqual(2, self.mock_ssh.shell.call_count)
        self.mock_ssh.shell.assert_called_with(
            "'/remote/path/to/scan/script' '/remote/path/to/scan'")
Example #10
0
    def test_calls_correct_ssh_md5sum_command(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh returns error for md5sum check, empty pickle dump for later commands
        def ssh_shell(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count == 1:
                # first try
                return "".encode()
            else:
                # later tries
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_shell

        scanner.scan()
        self.assertEqual(2, self.mock_ssh.shell.call_count)
        self.mock_ssh.shell.assert_has_calls([
            call(
                "md5sum /remote/path/to/scan/script | awk '{print $1}' || echo"
            ),
            call(ANY)
        ])
Example #11
0
    def test_suppresses_and_retries_on_ssh_error_connection_timed_out(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh run command raises error the first time, succeeds the second time
        # noinspection PyUnusedLocal
        def ssh_shell(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count == 1:
                # md5sum check
                return b''
            elif self.ssh_run_command_count == 2:
                # first try
                raise SshcpError(
                    "connect to host host.remote.com port 2202: Connection timed out"
                )
            else:
                # later tries
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_shell

        scanner.scan()
        self.assertEqual(3, self.mock_ssh.shell.call_count)
Example #12
0
 def test_calls_correct_ssh_command(self):
     scanner = RemoteScanner(
         remote_address="my remote address",
         remote_username="******",
         remote_port=1234,
         remote_path_to_scan="/remote/path/to/scan",
         local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
         remote_path_to_scan_script="/remote/path/to/scan/script")
     scanner.scan()
     self.mock_ssh.run_command.assert_called_once_with(
         "/remote/path/to/scan/script /remote/path/to/scan")
Example #13
0
    def test_raises_app_error_on_failed_scp(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        # noinspection PyUnusedLocal
        def scp_run_command(*args, **kwargs):
            raise ScpError("an ssh error")

        self.mock_scp.copy.side_effect = scp_run_command

        with self.assertRaises(AppError) as ctx:
            scanner.scan()
        self.assertEqual(Localization.Error.REMOTE_SERVER_INSTALL,
                         str(ctx.exception))
Example #14
0
    def test_raises_nonrecoverable_error_on_mangled_output(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script"
        )

        def ssh_shell(*args):
            return "mangled data".encode()
        self.mock_ssh.shell.side_effect = ssh_shell

        with self.assertRaises(ScannerError) as ctx:
            scanner.scan()
        self.assertEqual(Localization.Error.REMOTE_SERVER_SCAN.format("Invalid pickled data"), str(ctx.exception))
        self.assertFalse(ctx.exception.recoverable)
Example #15
0
    def test_raises_nonrecoverable_error_on_failed_copy(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script"
        )

        # noinspection PyUnusedLocal
        def ssh_copy(*args, **kwargs):
            raise SshcpError("an scp error")
        self.mock_ssh.copy.side_effect = ssh_copy

        with self.assertRaises(ScannerError) as ctx:
            scanner.scan()
        self.assertEqual(Localization.Error.REMOTE_SERVER_INSTALL.format("an scp error"), str(ctx.exception))
        self.assertFalse(ctx.exception.recoverable)
Example #16
0
    def test_raises_app_error_on_mangled_output(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        # Ssh run command raises error the first time, succeeds the second time
        # noinspection PyUnusedLocal
        def ssh_shell(*args):
            return "mangled data".encode()

        self.mock_ssh.shell.side_effect = ssh_shell

        with self.assertRaises(AppError) as ctx:
            scanner.scan()
        self.assertEqual(Localization.Error.REMOTE_SERVER_SCAN,
                         str(ctx.exception))
Example #17
0
    def test_fails_after_max_retries_on_suppressed_error(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        # noinspection PyUnusedLocal
        def ssh_run_command(*args):
            raise SshError("bash: /remote/path/to/scan: Text file busy")

        self.mock_ssh.run_command.side_effect = ssh_run_command

        with self.assertRaises(AppError) as ctx:
            scanner.scan()
        self.assertEqual(Localization.Error.REMOTE_SERVER_SCAN,
                         str(ctx.exception))
        # initial try + 5 retries
        self.assertEqual(6, self.mock_ssh.run_command.call_count)
Example #18
0
    def test_skips_install_on_md5sum_match(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh returns empty on md5sum, empty pickle dump for later commands
        def ssh_shell(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count == 1:
                # first try
                return "d41d8cd98f00b204e9800998ecf8427e".encode()
            else:
                # later tries
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_shell

        scanner.scan()
        self.mock_ssh.copy.assert_not_called()
        self.mock_ssh.copy.reset_mock()

        # should not be called the second time either
        scanner.scan()
        self.mock_ssh.copy.assert_not_called()
Example #19
0
    def test_suppresses_and_retries_on_ssh_error_text_file_busy(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh run command raises error the first time, succeeds the second time
        # noinspection PyUnusedLocal
        def ssh_run_command(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count < 2:
                raise SshError("bash: /remote/path/to/scan: Text file busy")
            else:
                return pickle.dumps([])

        self.mock_ssh.run_command.side_effect = ssh_run_command

        scanner.scan()
        self.assertEqual(2, self.mock_ssh.run_command.call_count)
Example #20
0
    def test_correctly_initializes_scp(self):
        self.scp_args = {}

        def mock_scp_ctor(**kwargs):
            self.scp_args = kwargs

        self.mock_ssh_cls.side_effect = mock_scp_ctor

        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.assertIsNotNone(scanner)
        self.assertEqual("my remote address", self.scp_args["host"])
        self.assertEqual(1234, self.scp_args["port"])
        self.assertEqual("my remote user", self.scp_args["user"])
Example #21
0
    def test_recovers_from_failed_ssh(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_password="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")

        self.ssh_run_command_count = 0

        # Ssh run command succeeds first time, raises error the second time, fine after that
        # noinspection PyUnusedLocal
        def ssh_shell(*args):
            self.ssh_run_command_count += 1
            if self.ssh_run_command_count == 1:
                # md5sum check
                return b''
            elif self.ssh_run_command_count == 2:
                # first try
                return pickle.dumps([])
            elif self.ssh_run_command_count == 3:
                # second try
                raise SshcpError("an ssh error")
            else:
                # later tries
                return pickle.dumps([])

        self.mock_ssh.shell.side_effect = ssh_shell

        scanner.scan()  # no error first time
        with self.assertRaises(ScannerError):
            scanner.scan()
        scanner.scan()
        self.assertEqual(4, self.mock_ssh.shell.call_count)
Example #22
0
    def test_installs_scan_script_on_first_scan(self):
        scanner = RemoteScanner(
            remote_address="my remote address",
            remote_username="******",
            remote_port=1234,
            remote_path_to_scan="/remote/path/to/scan",
            local_path_to_scan_script=TestRemoteScanner.temp_scan_script,
            remote_path_to_scan_script="/remote/path/to/scan/script")
        scanner.scan()
        self.mock_scp.copy.assert_called_once_with(
            local_path=TestRemoteScanner.temp_scan_script,
            remote_path="/remote/path/to/scan/script")
        self.mock_scp.copy.reset_mock()

        # should not be called the second time
        scanner.scan()
        self.mock_scp.copy.assert_not_called()