Ejemplo n.º 1
0
    def test_with_extra_arguments(self, mock_scan_commands):
        # Given a server to scan with a scan command
        server_scan = ServerScanRequest(
            server_info=ServerConnectivityInfoFactory.create(),
            scan_commands={ScanCommandForTests.MOCK_COMMAND_1},
            # And the command takes an extra argument
            scan_commands_extra_arguments={
                ScanCommandForTests.MOCK_COMMAND_1:
                MockPlugin1ExtraArguments(extra_field="test")
            },
        )

        # When running the scan
        scanner = Scanner()
        scanner.start_scans([server_scan])

        # It succeeds
        all_results = []
        for result in scanner.get_results():
            all_results.append(result)
        assert len(all_results) == 1

        # And the extra argument was taken into account
        assert all_results[
            0].scan_commands_extra_arguments == server_scan.scan_commands_extra_arguments
Ejemplo n.º 2
0
    def test(self, mock_scan_commands):
        # Given a server to scan
        server_scan = ServerScanRequest(
            server_info=ServerConnectivityInfoFactory.create(),
            scan_commands={
                ScanCommandForTests.MOCK_COMMAND_1,
                ScanCommandForTests.MOCK_COMMAND_2
            },
        )

        # When running the scan
        scanner = Scanner()
        scanner.start_scans([server_scan])

        # It succeeds
        all_results = []
        for result in scanner.get_results():
            all_results.append(result)
        assert len(all_results) == 1

        # And the right result is returned
        result = all_results[0]
        assert result.server_info == server_scan.server_info
        assert result.scan_commands == server_scan.scan_commands
        assert result.scan_commands_extra_arguments == server_scan.scan_commands_extra_arguments
        assert len(result.scan_commands_results) == 2

        assert type(result.scan_commands_results[
            ScanCommandForTests.MOCK_COMMAND_1]) == MockPlugin1ScanResult
        assert type(result.scan_commands_results[
            ScanCommandForTests.MOCK_COMMAND_2]) == MockPlugin2ScanResult

        # And the Scanner instance is all done and cleaned up
        assert not scanner._are_server_scans_ongoing
Ejemplo n.º 3
0
    def test_exception_when_processing_jobs(self):
        # Given a server to scan
        server_scan = ServerScanRequest(
            server_info=ServerConnectivityInfoFactory.create(),
            scan_commands={
                ScanCommandForTests.MOCK_COMMAND_1,
                # And one of the scan commands will trigger an exception when processing the completed scan jobs
                ScanCommandForTests.
                MOCK_COMMAND_EXCEPTION_WHEN_PROCESSING_JOBS,
            },
        )

        # When queuing the scan
        scanner = Scanner()
        scanner.queue_scan(server_scan)

        # It succeeds
        all_results = []
        for result in scanner.get_results():
            all_results.append(result)

            assert result.server_info == server_scan.server_info
            assert result.scan_commands == server_scan.scan_commands
            assert result.scan_commands_extra_arguments == server_scan.scan_commands_extra_arguments
            assert len(result.scan_commands_results) == 1

            # And the exception was properly caught and returned
            assert len(result.scan_commands_errors) == 1
            error = result.scan_commands_errors[
                ScanCommandForTests.
                MOCK_COMMAND_EXCEPTION_WHEN_PROCESSING_JOBS]
            assert ScanCommandErrorReasonEnum.BUG_IN_SSLYZE == error.reason
            assert error.exception_trace

        assert len(all_results) == 1
Ejemplo n.º 4
0
    def test_error_bug_in_sslyze_when_processing_job_results(
            self, mock_scan_commands):
        # Given a server to scan with some scan commands
        server_scan = ServerScanRequest(
            server_info=ServerConnectivityInfoFactory.create(),
            scan_commands={
                ScanCommandForTests.MOCK_COMMAND_1,
                ScanCommandForTests.MOCK_COMMAND_2
            },
        )

        # And the first scan command will trigger an error when processing the completed scan jobs
        with mock.patch.object(MockPlugin1Implementation,
                               "_scan_job_work_function",
                               side_effect=RuntimeError):
            # When running the scan
            scanner = Scanner()
            scanner.start_scans([server_scan])

            # It succeeds
            all_results = []
            for result in scanner.get_results():
                all_results.append(result)
            assert len(all_results) == 1

            # And the exception was properly caught and returned
            result = all_results[0]
            assert len(result.scan_commands_errors) == 1
            error = result.scan_commands_errors[
                ScanCommandForTests.MOCK_COMMAND_1]
            assert ScanCommandErrorReasonEnum.BUG_IN_SSLYZE == error.reason
            assert error.exception_trace
Ejemplo n.º 5
0
    def test(self, mock_scan_commands):
        # Given a server to scan
        server_scan = ServerScanRequest(
            server_info=ServerConnectivityInfoFactory.create(),
            scan_commands={ScanCommandForTests.MOCK_COMMAND_1, ScanCommandForTests.MOCK_COMMAND_2},
        )

        # When queuing the scan
        scanner = Scanner()
        scanner.queue_scan(server_scan)

        # It succeeds
        all_results = []
        for result in scanner.get_results():
            all_results.append(result)

            # And the right result is returned
            assert result.server_info == server_scan.server_info
            assert result.scan_commands == server_scan.scan_commands
            assert result.scan_commands_extra_arguments == server_scan.scan_commands_extra_arguments
            assert len(result.scan_commands_results) == 2

            assert type(result.scan_commands_results[ScanCommandForTests.MOCK_COMMAND_1]) == MockPlugin1ScanResult
            assert type(result.scan_commands_results[ScanCommandForTests.MOCK_COMMAND_2]) == MockPlugin2ScanResult

        assert len(all_results) == 1
Ejemplo n.º 6
0
    def test_emergency_shutdown(self, mock_scan_commands):
        # Given a lot of servers to scan
        total_server_scans_count = 100
        server_scans = [
            ServerScanRequest(
                server_info=ServerConnectivityInfoFactory.create(),
                scan_commands={ScanCommandForTests.MOCK_COMMAND_1, ScanCommandForTests.MOCK_COMMAND_2},
            )
            for _ in range(total_server_scans_count)
        ]

        # And the scans get queued
        scanner = Scanner()
        for scan in server_scans:
            scanner.queue_scan(scan)

        # When trying to quickly shutdown the scanner, it succeeds
        scanner.emergency_shutdown()

        # And all the queued jobs were done or cancelled
        all_queued_futures = []
        for server_scan in scanner._queued_server_scans:
            all_queued_futures.extend(server_scan.all_queued_scan_jobs)
        for completed_future in as_completed(all_queued_futures):
            assert completed_future.done()
Ejemplo n.º 7
0
    def test_error_server_connectivity_issue_handshake_timeout(self, mock_scan_commands):
        # Given a server to scan with some commands
        server_scan = ServerScanRequest(
            server_info=ServerConnectivityInfoFactory.create(),
            scan_commands={ScanCommandForTests.MOCK_COMMAND_1, ScanCommandForTests.MOCK_COMMAND_2},
        )

        # And the first scan command will trigger a handshake timeout with the server
        with mock.patch.object(
            MockPlugin1Implementation,
            "_scan_job_work_function",
            side_effect=TlsHandshakeTimedOut(
                server_location=server_scan.server_info.server_location,
                network_configuration=server_scan.server_info.network_configuration,
                error_message="error",
            ),
        ):
            # When queuing the scan
            scanner = Scanner()
            scanner.queue_scan(server_scan)

        # It succeeds
        for result in scanner.get_results():
            # And the error was properly caught and returned
            assert len(result.scan_commands_errors) == 1
            error = result.scan_commands_errors[ScanCommandForTests.MOCK_COMMAND_1]
            assert ScanCommandErrorReasonEnum.CONNECTIVITY_ISSUE == error.reason
            assert error.exception_trace
Ejemplo n.º 8
0
    def test(self, mock_scan_commands):
        # Given a lot of servers to scan
        total_server_scans_count = 100
        server_scans = [
            ServerScanRequest(
                server_info=ServerConnectivityInfoFactory.create(),
                scan_commands={ScanCommandForTests.MOCK_COMMAND_1, ScanCommandForTests.MOCK_COMMAND_2},
            )
            for _ in range(total_server_scans_count)
        ]

        # And a scanner with specifically chosen network settings
        per_server_concurrent_connections_limit = 4
        concurrent_server_scans_limit = 20
        scanner = Scanner(per_server_concurrent_connections_limit, concurrent_server_scans_limit)

        # When queuing the scans, it succeeds
        for scan in server_scans:
            scanner.queue_scan(scan)

        # And the right number of scans was performed
        assert total_server_scans_count == len(scanner._queued_server_scans)

        # And the chosen network settings were used
        assert concurrent_server_scans_limit == len(scanner._thread_pools)
        for pool in scanner._thread_pools:
            assert per_server_concurrent_connections_limit == pool._max_workers

        # And the server scans were evenly distributed among the thread pools to maximize performance
        expected_server_scans_per_pool = int(total_server_scans_count / concurrent_server_scans_limit)
        thread_pools_used = [server_scan.queued_on_thread_pool_at_index for server_scan in scanner._queued_server_scans]
        server_scans_per_pool_count = Counter(thread_pools_used)
        for pool_count in server_scans_per_pool_count.values():
            assert expected_server_scans_per_pool == pool_count
Ejemplo n.º 9
0
    def test_with_extra_arguments(self):
        # Given a server to scan
        server_scan = ServerScanRequest(
            server_info=ServerConnectivityInfoFactory.create(),
            scan_commands={ScanCommandForTests.MOCK_COMMAND_1},
            # With an extra argument for one command
            scan_commands_extra_arguments={
                ScanCommandForTests.MOCK_COMMAND_1:
                MockPlugin1ExtraArguments(extra_field="test")
            },
        )

        # When queuing the scan
        scanner = Scanner()
        scanner.queue_scan(server_scan)

        # It succeeds
        all_results = []
        for result in scanner.get_results():
            all_results.append(result)

            # And the extra argument was taken into account
            assert result.scan_commands_extra_arguments == server_scan.scan_commands_extra_arguments

        assert len(all_results) == 1
Ejemplo n.º 10
0
 def test_with_extra_arguments_but_no_corresponding_scan_command(self):
     # When trying to queue a scan for a server
     with pytest.raises(ValueError):
         ServerScanRequest(
             server_info=ServerConnectivityInfoFactory.create(),
             # With an extra argument for one command
             scan_commands_extra_arguments={
                 ScanCommandForTests.MOCK_COMMAND_1: MockPlugin1ExtraArguments(extra_field="test")
             },
             # But that specific scan command was not queued
             scan_commands={ScanCommandForTests.MOCK_COMMAND_2},
         )
Ejemplo n.º 11
0
    def test_duplicate_server(self, mock_scan_commands):
        # Given a server to scan
        server_info = ServerConnectivityInfoFactory.create()

        # When trying to queue two scans for this server
        server_scan1 = ServerScanRequest(server_info=server_info, scan_commands={ScanCommandForTests.MOCK_COMMAND_1})
        server_scan2 = ServerScanRequest(server_info=server_info, scan_commands={ScanCommandForTests.MOCK_COMMAND_2})
        scanner = Scanner()
        scanner.queue_scan(server_scan1)

        # It fails
        with pytest.raises(ValueError):
            scanner.queue_scan(server_scan2)
Ejemplo n.º 12
0
    def test_server_connectivity_test_succeeded(self):
        # Given a server to scan to which sslyze was able to connect
        server_info = ServerConnectivityInfoFactory.create()

        # When generating the console output for this
        with StringIO() as file_out:
            console_gen = ConsoleOutputGenerator(file_to=file_out)
            console_gen.server_connectivity_test_succeeded(server_info)
            final_output = file_out.getvalue()

        # It succeeds and the server is displayed
        assert final_output
        assert server_info.server_location.hostname in final_output
Ejemplo n.º 13
0
    def test_server_connectivity_test_succeeded_with_http_tunneling(self):
        # Given a server to scan to which sslyze was able to connect
        server_info = ServerConnectivityInfoFactory.create(
            # And sslyze connected to it via an HTTP proxy
            server_location=ServerNetworkLocationViaHttpProxyFactory.create())

        # When generating the console output for this
        with StringIO() as file_out:
            console_gen = ConsoleOutputGenerator(file_to=file_out)
            console_gen.server_connectivity_test_succeeded(server_info)
            final_output = file_out.getvalue()

        # It succeeds and the fact that an HTTP proxy was used was displayed
        assert final_output
        assert "proxy" in final_output
Ejemplo n.º 14
0
    def test_error_bug_in_sslyze_when_scheduling_jobs(self, mock_scan_commands):
        # Given a server to scan with some scan commands
        server_scan = ServerScanRequest(
            server_info=ServerConnectivityInfoFactory.create(),
            scan_commands={ScanCommandForTests.MOCK_COMMAND_1, ScanCommandForTests.MOCK_COMMAND_2},
        )

        # And the first scan command will trigger an error when generating scan jobs
        with mock.patch.object(MockPlugin1Implementation, "scan_jobs_for_scan_command", side_effect=RuntimeError):
            # When queuing the scan
            scanner = Scanner()
            scanner.queue_scan(server_scan)

        # It succeeds
        for result in scanner.get_results():
            # And the exception was properly caught and returned
            assert len(result.scan_commands_errors) == 1
            error = result.scan_commands_errors[ScanCommandForTests.MOCK_COMMAND_1]
            assert ScanCommandErrorReasonEnum.BUG_IN_SSLYZE == error.reason
            assert error.exception_trace
Ejemplo n.º 15
0
    def test_server_connectivity_test_succeeded_with_required_client_auth(
            self):
        # Given a server to scan to which sslyze was able to connect
        server_info = ServerConnectivityInfoFactory.create(
            tls_probing_result=ServerTlsProbingResult(
                highest_tls_version_supported=TlsVersionEnum.TLS_1_2,
                cipher_suite_supported="AES",
                # And the server requires client authentication
                client_auth_requirement=ClientAuthRequirementEnum.REQUIRED,
            ))

        # When generating the console output for this
        with StringIO() as file_out:
            console_gen = ConsoleOutputGenerator(file_to=file_out)
            console_gen.server_connectivity_test_succeeded(server_info)
            final_output = file_out.getvalue()

        # It succeeds and the fact that the server requires client auth was displayed
        assert final_output
        assert "Server REQUIRED client authentication" in final_output
Ejemplo n.º 16
0
    def test_enforces_per_server_concurrent_connections_limit(
            self, mock_scan_commands):
        # Given a server to scan with a scan command that requires multiple connections/jobs to the server
        server_scan = ServerScanRequest(
            server_info=ServerConnectivityInfoFactory.create(),
            scan_commands={ScanCommandForTests.MOCK_COMMAND_1},
        )

        # And a scanner configured to only perform one concurrent connection per server scan
        scanner = Scanner(per_server_concurrent_connections_limit=1)

        # And the scan command will notify us when more than one connection is being performed concurrently
        # Test internals: setup plumbing to detect when more than one thread are running at the same time
        # We use a Barrier that waits for 2 concurrent threads, and puts True in a queue if that ever happens
        queue = Queue()

        def flag_concurrent_threads_running():
            # Only called when two threads are running at the same time
            queue.put(True)

        barrier = threading.Barrier(parties=2,
                                    action=flag_concurrent_threads_running,
                                    timeout=1)

        def scan_job_work_function(arg1: str, arg2: int):
            barrier.wait()

        with mock.patch.object(MockPlugin1Implementation,
                               "_scan_job_work_function",
                               scan_job_work_function):
            # When running the scan
            scanner.start_scans([server_scan])

            # It succeeds
            all_results = []
            for result in scanner.get_results():
                all_results.append(result)
            assert len(all_results) == 1

            # And there never was more than one thread (=1 job/connection) running at the same time
            assert queue.empty()
Ejemplo n.º 17
0
    def test_server_scan_completed_with_proxy(self):
        # Given a completed scan for a server
        server_info = ServerConnectivityInfoFactory.create(
            # And sslyze connected to the server via an HTTP proxy
            server_location=ServerNetworkLocationViaHttpProxyFactory.create())
        scan_results = {
            ScanCommand.TLS_COMPRESSION:
            CompressionScanResult(supports_compression=True)
        }
        scan_result = ServerScanResultFactory.create(
            server_info=server_info, scan_commands_results=scan_results)

        # When generating the console output for this server scan
        with StringIO() as file_out:
            console_gen = ConsoleOutputGenerator(file_to=file_out)
            console_gen.server_scan_completed(scan_result)
            final_output = file_out.getvalue()

        # It succeeds and mentions the HTTP proxy
        assert final_output
        assert "HTTP PROXY" in final_output
        assert "Compression" in final_output