Completed
Push — master ( 22ac2f...f571c7 )
by Juan José
17s queued 12s
created

ospd.ospd.OSPDaemon.get_help_text()   A

Complexity

Conditions 5

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 17
nop 1
dl 0
loc 26
rs 9.0833
c 0
b 0
f 0
1
# Copyright (C) 2014-2020 Greenbone Networks GmbH
2
#
3
# SPDX-License-Identifier: AGPL-3.0-or-later
4
#
5
# This program is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU Affero General Public License as
7
# published by the Free Software Foundation, either version 3 of the
8
# License, or (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU Affero General Public License for more details.
14
#
15
# You should have received a copy of the GNU Affero General Public License
16
# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18
# pylint: disable=too-many-lines
19
20
""" OSP Daemon core class.
21
"""
22
23
import logging
24
import socket
25
import ssl
26
import multiprocessing
27
import time
28
import os
29
30
from typing import (
31
    List,
32
    Any,
33
    Iterator,
34
    Dict,
35
    Optional,
36
    Iterable,
37
    Tuple,
38
    Union,
39
)
40
from xml.etree.ElementTree import Element, SubElement
41
42
import defusedxml.ElementTree as secET
43
44
from deprecated import deprecated
45
46
from ospd import __version__
47
from ospd.command import get_commands
48
from ospd.errors import OspdCommandError
49
from ospd.misc import ResultType
50
from ospd.network import resolve_hostname, target_str_to_list
51
from ospd.protocol import OspRequest, OspResponse, RequestParser
52
from ospd.scan import ScanCollection, ScanStatus
53
from ospd.server import BaseServer, Stream
54
from ospd.vtfilter import VtsFilter
55
from ospd.vts import Vts
56
from ospd.xml import (
57
    elements_as_text,
58
    get_result_xml,
59
    get_elements_from_dict,
60
)
61
62
logger = logging.getLogger(__name__)
63
64
PROTOCOL_VERSION = "1.2"
65
66
SCHEDULER_CHECK_PERIOD = 10  # in seconds
67
68
BASE_SCANNER_PARAMS = {
69
    'debug_mode': {
70
        'type': 'boolean',
71
        'name': 'Debug Mode',
72
        'default': 0,
73
        'mandatory': 0,
74
        'description': 'Whether to get extra scan debug information.',
75
    },
76
    'dry_run': {
77
        'type': 'boolean',
78
        'name': 'Dry Run',
79
        'default': 0,
80
        'mandatory': 0,
81
        'description': 'Whether to dry run scan.',
82
    },
83
}  # type: Dict
84
85
86
def _terminate_process_group(process: multiprocessing.Process) -> None:
87
    os.killpg(os.getpgid(process.pid), 15)
88
89
90
class OSPDaemon:
91
92
    """ Daemon class for OSP traffic handling.
93
94
    Every scanner wrapper should subclass it and make necessary additions and
95
    changes.
96
97
    * Add any needed parameters in __init__.
98
    * Implement check() method which verifies scanner availability and other
99
      environment related conditions.
100
    * Implement process_scan_params and exec_scan methods which are
101
      specific to handling the <start_scan> command, executing the wrapped
102
      scanner and storing the results.
103
    * Implement other methods that assert to False such as get_scanner_name,
104
      get_scanner_version.
105
    * Use Call set_command_attributes at init time to add scanner command
106
      specific options eg. the w3af profile for w3af wrapper.
107
    """
108
109
    def __init__(
110
        self, *, customvtfilter=None, **kwargs
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
111
    ):  # pylint: disable=unused-argument
112
        """ Initializes the daemon's internal data. """
113
        self.scan_collection = ScanCollection()
114
        self.scan_processes = dict()
115
116
        self.daemon_info = dict()
117
        self.daemon_info['name'] = "OSPd"
118
        self.daemon_info['version'] = __version__
119
        self.daemon_info['description'] = "No description"
120
121
        self.scanner_info = dict()
122
        self.scanner_info['name'] = 'No name'
123
        self.scanner_info['version'] = 'No version'
124
        self.scanner_info['description'] = 'No description'
125
126
        self.server_version = None  # Set by the subclass.
127
128
        self.initialized = None  # Set after initialization finished
129
130
        self.scaninfo_store_time = kwargs.get('scaninfo_store_time')
131
132
        self.protocol_version = PROTOCOL_VERSION
133
134
        self.commands = {}
135
136
        for command_class in get_commands():
137
            command = command_class(self)
138
            self.commands[command.get_name()] = command
139
140
        self.scanner_params = dict()
141
142
        for name, params in BASE_SCANNER_PARAMS.items():
143
            self.set_scanner_param(name, params)
144
145
        self.vts = Vts()
146
        self.vts_version = None
147
148
        if customvtfilter:
149
            self.vts_filter = customvtfilter
150
        else:
151
            self.vts_filter = VtsFilter()
152
153
    def init(self, server: BaseServer) -> None:
154
        """ Should be overridden by a subclass if the initialization is costly.
155
156
            Will be called after check.
157
        """
158
        server.start(self.handle_client_stream)
159
        self.initialized = True
160
161
    def set_command_attributes(self, name: str, attributes: Dict) -> None:
162
        """ Sets the xml attributes of a specified command. """
163
        if self.command_exists(name):
164
            command = self.commands.get(name)
165
            command.attributes = attributes
166
167
    @deprecated(version="20.4", reason="Use set_scanner_param instead")
168
    def add_scanner_param(self, name: str, scanner_params: Dict) -> None:
169
        """ Set a scanner parameter. """
170
        self.set_scanner_param(name, scanner_params)
171
172
    def set_scanner_param(self, name: str, scanner_params: Dict) -> None:
173
        """ Set a scanner parameter. """
174
175
        assert name
176
        assert scanner_params
177
178
        self.scanner_params[name] = scanner_params
179
180
    def get_scanner_params(self) -> Dict:
181
        return self.scanner_params
182
183
    def add_vt(
184
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
185
        vt_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
186
        name: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
187
        vt_params: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
188
        vt_refs: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
189
        custom: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
190
        vt_creation_time: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
191
        vt_modification_time: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
192
        vt_dependencies: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
193
        summary: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
194
        impact: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
195
        affected: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
196
        insight: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
197
        solution: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
198
        solution_t: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
199
        solution_m: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
200
        detection: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
201
        qod_t: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
202
        qod_v: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
203
        severities: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
204
    ) -> None:
205
        """ Add a vulnerability test information.
206
207
        IMPORTANT: The VT's Data Manager will store the vts collection.
208
        If the collection is considerably big and it will be consultated
209
        intensible during a routine, consider to do a deepcopy(), since
210
        accessing the shared memory in the data manager is very expensive.
211
        At the end of the routine, the temporal copy must be set to None
212
        and deleted.
213
        """
214
        self.vts.add(
215
            vt_id,
216
            name=name,
217
            vt_params=vt_params,
218
            vt_refs=vt_refs,
219
            custom=custom,
220
            vt_creation_time=vt_creation_time,
221
            vt_modification_time=vt_modification_time,
222
            vt_dependencies=vt_dependencies,
223
            summary=summary,
224
            impact=impact,
225
            affected=affected,
226
            insight=insight,
227
            solution=solution,
228
            solution_t=solution_t,
229
            solution_m=solution_m,
230
            detection=detection,
231
            qod_t=qod_t,
232
            qod_v=qod_v,
233
            severities=severities,
234
        )
235
236
    def set_vts_version(self, vts_version: str) -> None:
237
        """ Add into the vts dictionary an entry to identify the
238
        vts version.
239
240
        Parameters:
241
            vts_version (str): Identifies a unique vts version.
242
        """
243
        if not vts_version:
244
            raise OspdCommandError(
245
                'A vts_version parameter is required', 'set_vts_version'
246
            )
247
        self.vts_version = vts_version
248
249
    def get_vts_version(self) -> Optional[str]:
250
        """Return the vts version.
251
        """
252
        return self.vts_version
253
254
    def command_exists(self, name: str) -> bool:
255
        """ Checks if a commands exists. """
256
        return name in self.commands
257
258
    def get_scanner_name(self) -> str:
259
        """ Gives the wrapped scanner's name. """
260
        return self.scanner_info['name']
261
262
    def get_scanner_version(self) -> str:
263
        """ Gives the wrapped scanner's version. """
264
        return self.scanner_info['version']
265
266
    def get_scanner_description(self) -> str:
267
        """ Gives the wrapped scanner's description. """
268
        return self.scanner_info['description']
269
270
    def get_server_version(self) -> str:
271
        """ Gives the specific OSP server's version. """
272
        assert self.server_version
273
        return self.server_version
274
275
    def get_protocol_version(self) -> str:
276
        """ Gives the OSP's version. """
277
        return self.protocol_version
278
279
    def preprocess_scan_params(self, xml_params):
280
        """ Processes the scan parameters. """
281
        params = {}
282
283
        for param in xml_params:
284
            params[param.tag] = param.text or ''
285
286
        # Set default values.
287
        for key in self.scanner_params:
288
            if key not in params:
289
                params[key] = self.get_scanner_param_default(key)
290
                if self.get_scanner_param_type(key) == 'selection':
291
                    params[key] = params[key].split('|')[0]
292
293
        # Validate values.
294
        for key in params:
295
            param_type = self.get_scanner_param_type(key)
296
            if not param_type:
297
                continue
298
299
            if param_type in ['integer', 'boolean']:
300
                try:
301
                    params[key] = int(params[key])
302
                except ValueError:
303
                    raise OspdCommandError(
304
                        'Invalid %s value' % key, 'start_scan'
305
                    )
306
307
            if param_type == 'boolean':
308
                if params[key] not in [0, 1]:
309
                    raise OspdCommandError(
310
                        'Invalid %s value' % key, 'start_scan'
311
                    )
312
            elif param_type == 'selection':
313
                selection = self.get_scanner_param_default(key).split('|')
314
                if params[key] not in selection:
315
                    raise OspdCommandError(
316
                        'Invalid %s value' % key, 'start_scan'
317
                    )
318
            if self.get_scanner_param_mandatory(key) and params[key] == '':
319
                raise OspdCommandError(
320
                    'Mandatory %s value is missing' % key, 'start_scan'
321
                )
322
323
        return params
324
325
    def process_scan_params(self, params: Dict) -> Dict:
326
        """ This method is to be overridden by the child classes if necessary
327
        """
328
        return params
329
330
    @staticmethod
331
    @deprecated(
332
        version="20.4",
333
        reason="Please use OspRequest.process_vt_params instead.",
334
    )
335
    def process_vts_params(scanner_vts) -> Dict:
336
        return OspRequest.process_vts_params(scanner_vts)
337
338
    @staticmethod
339
    @deprecated(
340
        version="20.4",
341
        reason="Please use OspRequest.process_credential_elements instead.",
342
    )
343
    def process_credentials_elements(cred_tree) -> Dict:
344
        return OspRequest.process_credentials_elements(cred_tree)
345
346
    @staticmethod
347
    @deprecated(
348
        version="20.4",
349
        reason="Please use OspRequest.process_targets_elements instead.",
350
    )
351
    def process_targets_element(scanner_target) -> List:
352
        return OspRequest.process_target_element(scanner_target)
353
354
    def stop_scan(self, scan_id: str) -> None:
355
        scan_process = self.scan_processes.get(scan_id)
356
        if not scan_process:
357
            raise OspdCommandError(
358
                'Scan not found {0}.'.format(scan_id), 'stop_scan'
359
            )
360
        if not scan_process.is_alive():
361
            raise OspdCommandError(
362
                'Scan already stopped or finished.', 'stop_scan'
363
            )
364
365
        self.set_scan_status(scan_id, ScanStatus.STOPPED)
366
367
        logger.info('%s: Scan stopping %s.', scan_id, scan_process.ident)
368
369
        self.stop_scan_cleanup(scan_id)
370
371
        try:
372
            scan_process.terminate()
373
        except AttributeError:
374
            logger.debug('%s: The scanner task stopped unexpectedly.', scan_id)
375
376
        try:
377
            _terminate_process_group(scan_process)
378
        except ProcessLookupError as e:
379
            logger.info(
380
                '%s: Scan already stopped %s.', scan_id, scan_process.pid
381
            )
382
383
        if scan_process.ident != os.getpid():
384
            scan_process.join(0)
385
386
        logger.info('%s: Scan stopped.', scan_id)
387
388
    @staticmethod
389
    def stop_scan_cleanup(scan_id: str):
390
        """ Should be implemented by subclass in case of a clean up before
391
        terminating is needed. """
392
393
    @staticmethod
394
    def target_is_finished(scan_id: str):
395
        """ Should be implemented by subclass in case of a check before
396
        stopping is needed. """
397
398
    def exec_scan(self, scan_id: str):
399
        """ Asserts to False. Should be implemented by subclass. """
400
        raise NotImplementedError
401
402
    def finish_scan(self, scan_id: str) -> None:
403
        """ Sets a scan as finished. """
404
        self.set_scan_progress(scan_id, 100)
405
        self.set_scan_status(scan_id, ScanStatus.FINISHED)
406
        logger.info("%s: Scan finished.", scan_id)
407
408
    def get_daemon_name(self) -> str:
409
        """ Gives osp daemon's name. """
410
        return self.daemon_info['name']
411
412
    def get_daemon_version(self) -> str:
413
        """ Gives osp daemon's version. """
414
        return self.daemon_info['version']
415
416
    def get_scanner_param_type(self, param: str):
417
        """ Returns type of a scanner parameter. """
418
        assert isinstance(param, str)
419
        entry = self.scanner_params.get(param)
420
        if not entry:
421
            return None
422
        return entry.get('type')
423
424
    def get_scanner_param_mandatory(self, param: str):
425
        """ Returns if a scanner parameter is mandatory. """
426
        assert isinstance(param, str)
427
        entry = self.scanner_params.get(param)
428
        if not entry:
429
            return False
430
        return entry.get('mandatory')
431
432
    def get_scanner_param_default(self, param: str):
433
        """ Returns default value of a scanner parameter. """
434
        assert isinstance(param, str)
435
        entry = self.scanner_params.get(param)
436
        if not entry:
437
            return None
438
        return entry.get('default')
439
440
    @deprecated(
441
        version="20.4",
442
        reason="Please use OspResponse.create_scanner_params_xml instead.",
443
    )
444
    def get_scanner_params_xml(self):
445
        """ Returns the OSP Daemon's scanner params in xml format. """
446
        return OspResponse.create_scanner_params_xml(self.scanner_params)
447
448
    def handle_client_stream(self, stream: Stream) -> None:
449
        """ Handles stream of data received from client. """
450
        data = b''
451
452
        request_parser = RequestParser()
453
454
        while True:
455
            try:
456
                buf = stream.read()
457
                if not buf:
458
                    break
459
460
                data += buf
461
462
                if request_parser.has_ended(buf):
463
                    break
464
            except (AttributeError, ValueError) as message:
465
                logger.error(message)
466
                return
467
            except (ssl.SSLError) as exception:
468
                logger.debug('Error: %s', exception)
469
                break
470
            except (socket.timeout) as exception:
471
                logger.debug('Request timeout: %s', exception)
472
                break
473
474
        if len(data) <= 0:
475
            logger.debug("Empty client stream")
476
            return
477
478
        response = None
479
        try:
480
            self.handle_command(data, stream)
481
        except OspdCommandError as exception:
482
            response = exception.as_xml()
483
            logger.debug('Command error: %s', exception.message)
484
        except Exception:  # pylint: disable=broad-except
485
            logger.exception('While handling client command:')
486
            exception = OspdCommandError('Fatal error', 'error')
487
            response = exception.as_xml()
488
489
        if response:
490
            stream.write(response)
491
492
        stream.close()
493
494
    def calculate_progress(self, scan_id: str) -> float:
495
        """ Calculate the total scan progress. """
496
497
        return self.scan_collection.calculate_target_progress(scan_id)
498
499
    def process_exclude_hosts(self, scan_id: str, exclude_hosts: str) -> None:
500
        """ Process the exclude hosts before launching the scans."""
501
502
        exc_hosts_list = ''
503
        if not exclude_hosts:
504
            return
505
        exc_hosts_list = target_str_to_list(exclude_hosts)
506
        self.remove_scan_hosts_from_target_progress(scan_id, exc_hosts_list)
507
508
    def process_finished_hosts(self, scan_id: str, finished_hosts: str) -> None:
509
        """ Process the finished hosts before launching the scans.
510
        Set finished hosts as finished with 100% to calculate
511
        the scan progress."""
512
513
        exc_hosts_list = ''
514
        if not finished_hosts:
515
            return
516
517
        exc_hosts_list = target_str_to_list(finished_hosts)
518
519
        for host in exc_hosts_list:
520
            self.set_scan_host_finished(scan_id, finished_hosts=host)
521
            self.set_scan_host_progress(scan_id, host=host, progress=100)
522
523
    def start_scan(self, scan_id: str, target: Dict) -> None:
524
        """ Starts the scan with scan_id. """
525
        os.setsid()
526
527
        if target is None or not target:
528
            raise OspdCommandError('Erroneous target', 'start_scan')
529
530
        logger.info("%s: Scan started.", scan_id)
531
532
        self.process_exclude_hosts(scan_id, target.get('exclude_hosts'))
533
        self.process_finished_hosts(scan_id, target.get('finished_hosts'))
534
535
        try:
536
            self.set_scan_status(scan_id, ScanStatus.RUNNING)
537
            self.exec_scan(scan_id)
538
        except Exception as e:  # pylint: disable=broad-except
539
            self.add_scan_error(
540
                scan_id,
541
                name='',
542
                host=self.get_scan_host(scan_id),
543
                value='Host process failure (%s).' % e,
544
            )
545
            logger.exception('While scanning: %s', scan_id)
546
        else:
547
            logger.info("%s: Host scan finished.", scan_id)
548
549
        if self.get_scan_status(scan_id) != ScanStatus.STOPPED:
550
            self.finish_scan(scan_id)
551
552
    def dry_run_scan(self, scan_id: str, target: Dict) -> None:
553
        """ Dry runs a scan. """
554
555
        os.setsid()
556
557
        host = resolve_hostname(target[0])
558
        if host is None:
559
            logger.info("Couldn't resolve %s.", self.get_scan_host(scan_id))
560
561
        port = self.get_scan_ports(scan_id)
562
563
        logger.info("%s:%s: Dry run mode.", host, port)
564
565
        self.add_scan_log(scan_id, name='', host=host, value='Dry run result')
566
567
        self.finish_scan(scan_id)
568
569
    def handle_timeout(self, scan_id: str, host: str) -> None:
570
        """ Handles scanner reaching timeout error. """
571
        self.add_scan_error(
572
            scan_id,
573
            host=host,
574
            name="Timeout",
575
            value="{0} exec timeout.".format(self.get_scanner_name()),
576
        )
577
578
    def remove_scan_hosts_from_target_progress(
579
        self, scan_id: str, exc_hosts_list: List
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
580
    ) -> None:
581
        """ Remove a list of hosts from the main scan progress table."""
582
        self.scan_collection.remove_hosts_from_target_progress(
583
            scan_id, exc_hosts_list
584
        )
585
586
    def set_scan_host_finished(
587
        self, scan_id: str, finished_hosts: Union[List[str], str],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
588
    ) -> None:
589
        """ Add the host in a list of finished hosts """
590
        if isinstance(finished_hosts, str):
591
            finished_hosts = [finished_hosts]
592
593
        self.scan_collection.set_host_finished(scan_id, finished_hosts)
594
595
    def set_scan_progress(self, scan_id: str, progress: int) -> None:
596
        """ Sets scan_id scan's progress which is a number
597
        between 0 and 100. """
598
        self.scan_collection.set_progress(scan_id, progress)
599
600
    def set_scan_progress_batch(
601
        self, scan_id: str, host_progress: Dict[str, int]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
602
    ):
603
        self.scan_collection.set_host_progress(scan_id, host_progress)
604
605
        scan_progress = self.calculate_progress(scan_id)
606
        self.set_scan_progress(scan_id, scan_progress)
607
608
    def set_scan_host_progress(
609
        self, scan_id: str, host: str = None, progress: int = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
610
    ) -> None:
611
        """ Sets host's progress which is part of target.
612
        Each time a host progress is updated, the scan progress
613
        is updated too.
614
        """
615
        if host and progress < 0 or progress > 100:
616
            return
617
618
        host_progress = {host: progress}
619
        self.set_scan_progress_batch(scan_id, host_progress)
620
621
    def set_scan_status(self, scan_id: str, status: ScanStatus) -> None:
622
        """ Set the scan's status."""
623
        self.scan_collection.set_status(scan_id, status)
624
625
    def get_scan_status(self, scan_id: str) -> ScanStatus:
626
        """ Get scan_id scans's status."""
627
        return self.scan_collection.get_status(scan_id)
628
629
    def scan_exists(self, scan_id: str) -> bool:
630
        """ Checks if a scan with ID scan_id is in collection.
631
632
        @return: 1 if scan exists, 0 otherwise.
633
        """
634
        return self.scan_collection.id_exists(scan_id)
635
636
    def get_help_text(self) -> str:
637
        """ Returns the help output in plain text format."""
638
639
        txt = ''
640
        for name, info in self.commands.items():
641
            description = info.get_description()
642
            attributes = info.get_attributes()
643
            elements = info.get_elements()
644
645
            command_txt = "\t{0: <22} {1}\n".format(name, description)
646
647
            if attributes:
648
                command_txt = ''.join([command_txt, "\t Attributes:\n"])
649
650
                for attrname, attrdesc in attributes.items():
651
                    attr_txt = "\t  {0: <22} {1}\n".format(attrname, attrdesc)
652
                    command_txt = ''.join([command_txt, attr_txt])
653
654
            if elements:
655
                command_txt = ''.join(
656
                    [command_txt, "\t Elements:\n", elements_as_text(elements),]
657
                )
658
659
            txt += command_txt
660
661
        return txt
662
663
    @deprecated(version="20.4", reason="Use ospd.xml.elements_as_text instead.")
664
    def elements_as_text(self, elems: Dict, indent: int = 2) -> str:
665
        """ Returns the elems dictionary as formatted plain text. """
666
        return elements_as_text(elems, indent)
667
668
    def delete_scan(self, scan_id: str) -> int:
669
        """ Deletes scan_id scan from collection.
670
671
        @return: 1 if scan deleted, 0 otherwise.
672
        """
673
        if self.get_scan_status(scan_id) == ScanStatus.RUNNING:
674
            return 0
675
676
        # Don't delete the scan until the process stops
677
        exitcode = None
678
        try:
679
            self.scan_processes[scan_id].join()
680
            exitcode = self.scan_processes[scan_id].exitcode
681
        except KeyError:
682
            logger.debug('Scan process for %s not found', scan_id)
683
684
        if exitcode or exitcode == 0:
685
            del self.scan_processes[scan_id]
686
687
        return self.scan_collection.delete_scan(scan_id)
688
689
    def get_scan_results_xml(
690
        self, scan_id: str, pop_res: bool, max_res: Optional[int]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
691
    ):
692
        """ Gets scan_id scan's results in XML format.
693
694
        @return: String of scan results in xml.
695
        """
696
        results = Element('results')
697
        for result in self.scan_collection.results_iterator(
698
            scan_id, pop_res, max_res
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
699
        ):
700
            results.append(get_result_xml(result))
701
702
        logger.debug('Returning %d results', len(results))
703
        return results
704
705
    @deprecated(
706
        version="20.4",
707
        reason="Please use ospd.xml.get_elements_from_dict instead.",
708
    )
709
    def get_xml_str(self, data: Dict) -> List:
710
        """ Creates a string in XML Format using the provided data structure.
711
712
        @param: Dictionary of xml tags and their elements.
713
714
        @return: String of data in xml format.
715
        """
716
        return get_elements_from_dict(data)
717
718
    def get_scan_xml(
719
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
720
        scan_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
721
        detailed: bool = True,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
722
        pop_res: bool = False,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
723
        max_res: int = 0,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
724
    ):
725
        """ Gets scan in XML format.
726
727
        @return: String of scan in XML format.
728
        """
729
        if not scan_id:
730
            return Element('scan')
731
732
        target = self.get_scan_host(scan_id)
733
        progress = self.get_scan_progress(scan_id)
734
        status = self.get_scan_status(scan_id)
735
        start_time = self.get_scan_start_time(scan_id)
736
        end_time = self.get_scan_end_time(scan_id)
737
        response = Element('scan')
738
        for name, value in [
739
            ('id', scan_id),
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
740
            ('target', target),
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
741
            ('progress', progress),
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
742
            ('status', status.name.lower()),
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
743
            ('start_time', start_time),
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
744
            ('end_time', end_time),
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
745
        ]:
746
            response.set(name, str(value))
747
        if detailed:
748
            response.append(
749
                self.get_scan_results_xml(scan_id, pop_res, max_res)
750
            )
751
        return response
752
753
    @staticmethod
754
    def get_custom_vt_as_xml_str(  # pylint: disable=unused-argument
755
        vt_id: str, custom: Dict
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
756
    ) -> str:
757
        """ Create a string representation of the XML object from the
758
        custom data object.
759
        This needs to be implemented by each ospd wrapper, in case
760
        custom elements for VTs are used.
761
762
        The custom XML object which is returned will be embedded
763
        into a <custom></custom> element.
764
765
        @return: XML object as string for custom data.
766
        """
767
        return ''
768
769
    @staticmethod
770
    def get_params_vt_as_xml_str(  # pylint: disable=unused-argument
771
        vt_id: str, vt_params
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
772
    ) -> str:
773
        """ Create a string representation of the XML object from the
774
        vt_params data object.
775
        This needs to be implemented by each ospd wrapper, in case
776
        vt_params elements for VTs are used.
777
778
        The params XML object which is returned will be embedded
779
        into a <params></params> element.
780
781
        @return: XML object as string for vt parameters data.
782
        """
783
        return ''
784
785
    @staticmethod
786
    def get_refs_vt_as_xml_str(  # pylint: disable=unused-argument
787
        vt_id: str, vt_refs
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
788
    ) -> str:
789
        """ Create a string representation of the XML object from the
790
        refs data object.
791
        This needs to be implemented by each ospd wrapper, in case
792
        refs elements for VTs are used.
793
794
        The refs XML object which is returned will be embedded
795
        into a <refs></refs> element.
796
797
        @return: XML object as string for vt references data.
798
        """
799
        return ''
800
801
    @staticmethod
802
    def get_dependencies_vt_as_xml_str(  # pylint: disable=unused-argument
803
        vt_id: str, vt_dependencies
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
804
    ) -> str:
805
        """ Create a string representation of the XML object from the
806
        vt_dependencies data object.
807
        This needs to be implemented by each ospd wrapper, in case
808
        vt_dependencies elements for VTs are used.
809
810
        The vt_dependencies XML object which is returned will be embedded
811
        into a <dependencies></dependencies> element.
812
813
        @return: XML object as string for vt dependencies data.
814
        """
815
        return ''
816
817
    @staticmethod
818
    def get_creation_time_vt_as_xml_str(  # pylint: disable=unused-argument
819
        vt_id: str, vt_creation_time
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
820
    ) -> str:
821
        """ Create a string representation of the XML object from the
822
        vt_creation_time data object.
823
        This needs to be implemented by each ospd wrapper, in case
824
        vt_creation_time elements for VTs are used.
825
826
        The vt_creation_time XML object which is returned will be embedded
827
        into a <vt_creation_time></vt_creation_time> element.
828
829
        @return: XML object as string for vt creation time data.
830
        """
831
        return ''
832
833
    @staticmethod
834
    def get_modification_time_vt_as_xml_str(  # pylint: disable=unused-argument
835
        vt_id: str, vt_modification_time
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
836
    ) -> str:
837
        """ Create a string representation of the XML object from the
838
        vt_modification_time data object.
839
        This needs to be implemented by each ospd wrapper, in case
840
        vt_modification_time elements for VTs are used.
841
842
        The vt_modification_time XML object which is returned will be embedded
843
        into a <vt_modification_time></vt_modification_time> element.
844
845
        @return: XML object as string for vt references data.
846
        """
847
        return ''
848
849
    @staticmethod
850
    def get_summary_vt_as_xml_str(  # pylint: disable=unused-argument
851
        vt_id: str, summary
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
852
    ) -> str:
853
        """ Create a string representation of the XML object from the
854
        summary data object.
855
        This needs to be implemented by each ospd wrapper, in case
856
        summary elements for VTs are used.
857
858
        The summary XML object which is returned will be embedded
859
        into a <summary></summary> element.
860
861
        @return: XML object as string for summary data.
862
        """
863
        return ''
864
865
    @staticmethod
866
    def get_impact_vt_as_xml_str(  # pylint: disable=unused-argument
867
        vt_id: str, impact
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
868
    ) -> str:
869
        """ Create a string representation of the XML object from the
870
        impact data object.
871
        This needs to be implemented by each ospd wrapper, in case
872
        impact elements for VTs are used.
873
874
        The impact XML object which is returned will be embedded
875
        into a <impact></impact> element.
876
877
        @return: XML object as string for impact data.
878
        """
879
        return ''
880
881
    @staticmethod
882
    def get_affected_vt_as_xml_str(  # pylint: disable=unused-argument
883
        vt_id: str, affected
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
884
    ) -> str:
885
        """ Create a string representation of the XML object from the
886
        affected data object.
887
        This needs to be implemented by each ospd wrapper, in case
888
        affected elements for VTs are used.
889
890
        The affected XML object which is returned will be embedded
891
        into a <affected></affected> element.
892
893
        @return: XML object as string for affected data.
894
        """
895
        return ''
896
897
    @staticmethod
898
    def get_insight_vt_as_xml_str(  # pylint: disable=unused-argument
899
        vt_id: str, insight
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
900
    ) -> str:
901
        """ Create a string representation of the XML object from the
902
        insight data object.
903
        This needs to be implemented by each ospd wrapper, in case
904
        insight elements for VTs are used.
905
906
        The insight XML object which is returned will be embedded
907
        into a <insight></insight> element.
908
909
        @return: XML object as string for insight data.
910
        """
911
        return ''
912
913
    @staticmethod
914
    def get_solution_vt_as_xml_str(  # pylint: disable=unused-argument
915
        vt_id: str, solution, solution_type=None, solution_method=None
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
916
    ) -> str:
917
        """ Create a string representation of the XML object from the
918
        solution data object.
919
        This needs to be implemented by each ospd wrapper, in case
920
        solution elements for VTs are used.
921
922
        The solution XML object which is returned will be embedded
923
        into a <solution></solution> element.
924
925
        @return: XML object as string for solution data.
926
        """
927
        return ''
928
929
    @staticmethod
930
    def get_detection_vt_as_xml_str(  # pylint: disable=unused-argument
931
        vt_id: str, detection=None, qod_type=None, qod=None
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
932
    ) -> str:
933
        """ Create a string representation of the XML object from the
934
        detection data object.
935
        This needs to be implemented by each ospd wrapper, in case
936
        detection elements for VTs are used.
937
938
        The detection XML object which is returned is an element with
939
        tag <detection></detection> element
940
941
        @return: XML object as string for detection data.
942
        """
943
        return ''
944
945
    @staticmethod
946
    def get_severities_vt_as_xml_str(  # pylint: disable=unused-argument
947
        vt_id: str, severities
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
948
    ) -> str:
949
        """ Create a string representation of the XML object from the
950
        severities data object.
951
        This needs to be implemented by each ospd wrapper, in case
952
        severities elements for VTs are used.
953
954
        The severities XML objects which are returned will be embedded
955
        into a <severities></severities> element.
956
957
        @return: XML object as string for severities data.
958
        """
959
        return ''
960
961
    def get_vt_iterator(
962
        self, vt_selection: List[str] = None, details: bool = True
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
963
    ) -> Iterator[Tuple[str, Dict]]:
964
        """ Return iterator object for getting elements
965
        from the VTs dictionary. """
966
        return self.vts.items()
967
968
    def get_vt_xml(self, single_vt: Tuple[str, Dict]) -> Element:
969
        """ Gets a single vulnerability test information in XML format.
970
971
        @return: String of single vulnerability test information in XML format.
972
        """
973
        if not single_vt:
974
            return Element('vt')
975
976
        vt_id, vt = single_vt
977
978
        name = vt.get('name')
979
        vt_xml = Element('vt')
980
        vt_xml.set('id', vt_id)
981
982
        for name, value in [('name', name)]:
983
            elem = SubElement(vt_xml, name)
984
            elem.text = str(value)
985
986
        if vt.get('vt_params'):
987
            params_xml_str = self.get_params_vt_as_xml_str(
988
                vt_id, vt.get('vt_params')
989
            )
990
            vt_xml.append(secET.fromstring(params_xml_str))
991
992
        if vt.get('vt_refs'):
993
            refs_xml_str = self.get_refs_vt_as_xml_str(vt_id, vt.get('vt_refs'))
994
            vt_xml.append(secET.fromstring(refs_xml_str))
995
996
        if vt.get('vt_dependencies'):
997
            dependencies = self.get_dependencies_vt_as_xml_str(
998
                vt_id, vt.get('vt_dependencies')
999
            )
1000
            vt_xml.append(secET.fromstring(dependencies))
1001
1002
        if vt.get('creation_time'):
1003
            vt_ctime = self.get_creation_time_vt_as_xml_str(
1004
                vt_id, vt.get('creation_time')
1005
            )
1006
            vt_xml.append(secET.fromstring(vt_ctime))
1007
1008
        if vt.get('modification_time'):
1009
            vt_mtime = self.get_modification_time_vt_as_xml_str(
1010
                vt_id, vt.get('modification_time')
1011
            )
1012
            vt_xml.append(secET.fromstring(vt_mtime))
1013
1014
        if vt.get('summary'):
1015
            summary_xml_str = self.get_summary_vt_as_xml_str(
1016
                vt_id, vt.get('summary')
1017
            )
1018
            vt_xml.append(secET.fromstring(summary_xml_str))
1019
1020
        if vt.get('impact'):
1021
            impact_xml_str = self.get_impact_vt_as_xml_str(
1022
                vt_id, vt.get('impact')
1023
            )
1024
            vt_xml.append(secET.fromstring(impact_xml_str))
1025
1026
        if vt.get('affected'):
1027
            affected_xml_str = self.get_affected_vt_as_xml_str(
1028
                vt_id, vt.get('affected')
1029
            )
1030
            vt_xml.append(secET.fromstring(affected_xml_str))
1031
1032
        if vt.get('insight'):
1033
            insight_xml_str = self.get_insight_vt_as_xml_str(
1034
                vt_id, vt.get('insight')
1035
            )
1036
            vt_xml.append(secET.fromstring(insight_xml_str))
1037
1038
        if vt.get('solution'):
1039
            solution_xml_str = self.get_solution_vt_as_xml_str(
1040
                vt_id,
1041
                vt.get('solution'),
1042
                vt.get('solution_type'),
1043
                vt.get('solution_method'),
1044
            )
1045
            vt_xml.append(secET.fromstring(solution_xml_str))
1046
1047
        if vt.get('detection') or vt.get('qod_type') or vt.get('qod'):
1048
            detection_xml_str = self.get_detection_vt_as_xml_str(
1049
                vt_id, vt.get('detection'), vt.get('qod_type'), vt.get('qod')
1050
            )
1051
            vt_xml.append(secET.fromstring(detection_xml_str))
1052
1053
        if vt.get('severities'):
1054
            severities_xml_str = self.get_severities_vt_as_xml_str(
1055
                vt_id, vt.get('severities')
1056
            )
1057
            vt_xml.append(secET.fromstring(severities_xml_str))
1058
1059
        if vt.get('custom'):
1060
            custom_xml_str = self.get_custom_vt_as_xml_str(
1061
                vt_id, vt.get('custom')
1062
            )
1063
            vt_xml.append(secET.fromstring(custom_xml_str))
1064
1065
        return vt_xml
1066
1067
    def get_vts_selection_list(
1068
        self, vt_id: str = None, filtered_vts: Dict = None
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1069
    ) -> Iterable[str]:
1070
        """
1071
        Get list of VT's OID.
1072
        If vt_id is specified, the collection will contain only this vt, if
1073
        found.
1074
        If no vt_id is specified or filtered_vts is None (default), the
1075
        collection will contain all vts. Otherwise those vts passed
1076
        in filtered_vts or vt_id are returned. In case of both vt_id and
1077
        filtered_vts are given, filtered_vts has priority.
1078
1079
        Arguments:
1080
            vt_id (vt_id, optional): ID of the vt to get.
1081
            filtered_vts (list, optional): Filtered VTs collection.
1082
1083
        Return:
1084
            List of selected VT's OID.
1085
        """
1086
        vts_xml = []
1087
        if not self.vts:
1088
            return vts_xml
1089
1090
        # No match for the filter
1091
        if filtered_vts is not None and len(filtered_vts) == 0:
1092
            return vts_xml
1093
1094
        if filtered_vts:
1095
            vts_list = filtered_vts
1096
        elif vt_id:
1097
            vts_list = [vt_id]
1098
        else:
1099
            vts_list = self.vts.keys()
1100
1101
        return vts_list
1102
1103
    def handle_command(self, data: bytes, stream: Stream) -> None:
1104
        """ Handles an osp command in a string.
1105
        """
1106
        try:
1107
            tree = secET.fromstring(data)
1108
        except secET.ParseError:
1109
            logger.debug("Erroneous client input: %s", data)
1110
            raise OspdCommandError('Invalid data')
1111
1112
        command_name = tree.tag
1113
1114
        logger.debug('Handling %s command request.', command_name)
1115
1116
        command = self.commands.get(command_name, None)
1117
        if not command and command_name != "authenticate":
1118
            raise OspdCommandError('Bogus command name')
1119
1120
        if not self.initialized and command.must_be_initialized:
1121
            exception = OspdCommandError(
1122
                '%s is still starting' % self.daemon_info['name'], 'error'
1123
            )
1124
            response = exception.as_xml()
1125
            stream.write(response)
1126
            return
1127
1128
        response = command.handle_xml(tree)
1129
1130
        if isinstance(response, bytes):
1131
            stream.write(response)
1132
        else:
1133
            for data in response:
1134
                stream.write(data)
1135
1136
    def check(self):
1137
        """ Asserts to False. Should be implemented by subclass. """
1138
        raise NotImplementedError
1139
1140
    def run(self) -> None:
1141
        """ Starts the Daemon, handling commands until interrupted.
1142
        """
1143
1144
        try:
1145
            while True:
1146
                time.sleep(SCHEDULER_CHECK_PERIOD)
1147
                self.scheduler()
1148
                self.clean_forgotten_scans()
1149
                self.wait_for_children()
1150
        except KeyboardInterrupt:
1151
            logger.info("Received Ctrl-C shutting-down ...")
1152
1153
    def scheduler(self):
1154
        """ Should be implemented by subclass in case of need
1155
        to run tasks periodically. """
1156
1157
    def wait_for_children(self):
1158
        """ Join the zombie process to releases resources."""
1159
        for scan_id in self.scan_processes:
1160
            self.scan_processes[scan_id].join(0)
1161
1162
    def create_scan(
1163
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1164
        scan_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1165
        targets: Dict,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1166
        options: Optional[Dict],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1167
        vt_selection: Dict,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1168
    ) -> Optional[str]:
1169
        """ Creates a new scan.
1170
1171
        @target: Target to scan.
1172
        @options: Miscellaneous scan options.
1173
1174
        @return: New scan's ID. None if the scan_id already exists and the
1175
                 scan status is RUNNING or FINISHED.
1176
        """
1177
        status = None
1178
        scan_exists = self.scan_exists(scan_id)
1179
        if scan_id and scan_exists:
1180
            status = self.get_scan_status(scan_id)
1181
1182
        if scan_exists and status == ScanStatus.STOPPED:
1183
            logger.info("Scan %s exists. Resuming scan.", scan_id)
1184
        elif scan_exists and (
1185
            status == ScanStatus.RUNNING or status == ScanStatus.FINISHED
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1186
        ):
1187
            logger.info(
1188
                "Scan %s exists with status %s.", scan_id, status.name.lower()
1189
            )
1190
            return
1191
        return self.scan_collection.create_scan(
1192
            scan_id, targets, options, vt_selection
1193
        )
1194
1195
    def get_scan_options(self, scan_id: str) -> str:
1196
        """ Gives a scan's list of options. """
1197
        return self.scan_collection.get_options(scan_id)
1198
1199
    def set_scan_option(self, scan_id: str, name: str, value: Any) -> None:
1200
        """ Sets a scan's option to a provided value. """
1201
        return self.scan_collection.set_option(scan_id, name, value)
1202
1203
    def clean_forgotten_scans(self) -> None:
1204
        """ Check for old stopped or finished scans which have not been
1205
        deleted and delete them if the are older than the set value."""
1206
1207
        if not self.scaninfo_store_time:
1208
            return
1209
1210
        for scan_id in list(self.scan_collection.ids_iterator()):
1211
            end_time = int(self.get_scan_end_time(scan_id))
1212
            scan_status = self.get_scan_status(scan_id)
1213
1214
            if (
1215
                scan_status == ScanStatus.STOPPED
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1216
                or scan_status == ScanStatus.FINISHED
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1217
            ) and end_time:
1218
                stored_time = int(time.time()) - end_time
1219
                if stored_time > self.scaninfo_store_time * 3600:
1220
                    logger.debug(
1221
                        'Scan %s is older than %d hours and seems have been '
1222
                        'forgotten. Scan info will be deleted from the '
1223
                        'scan table',
1224
                        scan_id,
1225
                        self.scaninfo_store_time,
1226
                    )
1227
                    self.delete_scan(scan_id)
1228
1229
    def check_scan_process(self, scan_id: str) -> None:
1230
        """ Check the scan's process, and terminate the scan if not alive. """
1231
        scan_process = self.scan_processes[scan_id]
1232
        progress = self.get_scan_progress(scan_id)
1233
1234
        if progress < 100 and not scan_process.is_alive():
1235
            if not self.get_scan_status(scan_id) == ScanStatus.STOPPED:
1236
                self.set_scan_status(scan_id, ScanStatus.STOPPED)
1237
                self.add_scan_error(
1238
                    scan_id, name="", host="", value="Scan process failure."
1239
                )
1240
1241
                logger.info("%s: Scan stopped with errors.", scan_id)
1242
1243
        elif progress == 100:
1244
            scan_process.join(0)
1245
1246
    def get_scan_progress(self, scan_id: str):
1247
        """ Gives a scan's current progress value. """
1248
        return self.scan_collection.get_progress(scan_id)
1249
1250
    def get_scan_host(self, scan_id: str) -> str:
1251
        """ Gives a scan's target. """
1252
        return self.scan_collection.get_host_list(scan_id)
1253
1254
    def get_scan_ports(self, scan_id: str) -> str:
1255
        """ Gives a scan's ports list. """
1256
        return self.scan_collection.get_ports(scan_id)
1257
1258
    def get_scan_exclude_hosts(self, scan_id: str):
1259
        """ Gives a scan's exclude host list. If a target is passed gives
1260
        the exclude host list for the given target. """
1261
        return self.scan_collection.get_exclude_hosts(scan_id)
1262
1263
    def get_scan_credentials(self, scan_id: str) -> Dict:
1264
        """ Gives a scan's credential list. If a target is passed gives
1265
        the credential list for the given target. """
1266
        return self.scan_collection.get_credentials(scan_id)
1267
1268
    def get_scan_target_options(self, scan_id: str) -> Dict:
1269
        """ Gives a scan's target option dict. If a target is passed gives
1270
        the credential list for the given target. """
1271
        return self.scan_collection.get_target_options(scan_id)
1272
1273
    def get_scan_vts(self, scan_id: str) -> Dict:
1274
        """ Gives a scan's vts. """
1275
        return self.scan_collection.get_vts(scan_id)
1276
1277
    def get_scan_unfinished_hosts(self, scan_id: str) -> List:
1278
        """ Get a list of unfinished hosts."""
1279
        return self.scan_collection.get_hosts_unfinished(scan_id)
1280
1281
    def get_scan_finished_hosts(self, scan_id: str) -> List:
1282
        """ Get a list of unfinished hosts."""
1283
        return self.scan_collection.get_hosts_finished(scan_id)
1284
1285
    def get_scan_start_time(self, scan_id: str) -> str:
1286
        """ Gives a scan's start time. """
1287
        return self.scan_collection.get_start_time(scan_id)
1288
1289
    def get_scan_end_time(self, scan_id: str) -> str:
1290
        """ Gives a scan's end time. """
1291
        return self.scan_collection.get_end_time(scan_id)
1292
1293
    def add_scan_log(
1294
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1295
        scan_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1296
        host: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1297
        hostname: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1298
        name: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1299
        value: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1300
        port: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1301
        test_id: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1302
        qod: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1303
    ) -> None:
1304
        """ Adds a log result to scan_id scan. """
1305
1306
        self.scan_collection.add_result(
1307
            scan_id,
1308
            ResultType.LOG,
1309
            host,
1310
            hostname,
1311
            name,
1312
            value,
1313
            port,
1314
            test_id,
1315
            '0.0',
1316
            qod,
1317
        )
1318
1319
    def add_scan_error(
1320
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1321
        scan_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1322
        host: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1323
        hostname: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1324
        name: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1325
        value: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1326
        port: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1327
        test_id='',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1328
    ) -> None:
1329
        """ Adds an error result to scan_id scan. """
1330
        self.scan_collection.add_result(
1331
            scan_id,
1332
            ResultType.ERROR,
1333
            host,
1334
            hostname,
1335
            name,
1336
            value,
1337
            port,
1338
            test_id,
1339
        )
1340
1341
    def add_scan_host_detail(
1342
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1343
        scan_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1344
        host: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1345
        hostname: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1346
        name: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1347
        value: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1348
    ) -> None:
1349
        """ Adds a host detail result to scan_id scan. """
1350
        self.scan_collection.add_result(
1351
            scan_id, ResultType.HOST_DETAIL, host, hostname, name, value
1352
        )
1353
1354
    def add_scan_alarm(
1355
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1356
        scan_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1357
        host: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1358
        hostname: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1359
        name: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1360
        value: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1361
        port: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1362
        test_id: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1363
        severity: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1364
        qod: str = '',
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1365
    ) -> None:
1366
        """ Adds an alarm result to scan_id scan. """
1367
        self.scan_collection.add_result(
1368
            scan_id,
1369
            ResultType.ALARM,
1370
            host,
1371
            hostname,
1372
            name,
1373
            value,
1374
            port,
1375
            test_id,
1376
            severity,
1377
            qod,
1378
        )
1379