From e866026e0c22a1d53cc72e912b1ce74d97be2f74 Mon Sep 17 00:00:00 2001 From: Damian Krawczyk Date: Thu, 20 Feb 2025 21:21:47 +0100 Subject: [PATCH 1/3] 0.7.3 --- .github/workflows/python-package.yml | 6 +- .github/workflows/python-publish.yml | 4 +- .gitignore | 3 +- CHANGELOG.md | 23 + nessus_file_analyzer/__about__.py | 35 +- nessus_file_analyzer/__init__.py | 26 +- nessus_file_analyzer/__main__.py | 4 +- nessus_file_analyzer/_version.py | 2 +- nessus_file_analyzer/app.py | 3487 ++++++++++++++++--------- nessus_file_analyzer/dialogs/about.py | 23 +- nessus_file_analyzer/ico.py | 5 +- nessus_file_analyzer/ico_generator.py | 4 +- nessus_file_analyzer/utilities.py | 34 +- requirements.txt | 10 +- setup.py | 10 +- 15 files changed, 2442 insertions(+), 1234 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index ef15000..c0f7ce7 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -18,12 +18,12 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: [3.7, 3.8, 3.9] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install tools diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 78ba265..b020f9a 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -18,9 +18,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: '3.x' - name: Install dependencies diff --git a/.gitignore b/.gitignore index a8ca16e..50b3a07 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ test_files _build .vscode _build -*.egg-info \ No newline at end of file +*.egg-info +.DS_Store \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b2d2e22..f5ad756 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,29 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.7.3] - 2025-02-20 + +### Changed + +- code formatted with [black](https://black.readthedocs.io) +- requirements update + - from: + - chardet>=4.0.0 + - imageio>=2.9.0 + - nessus-file-reader>=0.4.1 + - PyQt5>=5.15.4 + - XlsxWriter>=3.0.1 + - to: + - chardet>=5.2.0 + - imageio>=2.37.0 + - nessus-file-reader>=0.4.3 + - PyQt5>=5.15.11 + - XlsxWriter>=3.2.2 + +- tests for python + - added: 3.10, 3.11, 3.12, 3.13 + - removed: 3.7 + ## [0.7.2] - 2022-05-13 ### Added diff --git a/nessus_file_analyzer/__about__.py b/nessus_file_analyzer/__about__.py index d845de5..ccc2e34 100644 --- a/nessus_file_analyzer/__about__.py +++ b/nessus_file_analyzer/__about__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -u""" +""" nessus file analyzer by LimberDuck (pronounced *ˈlɪm.bɚ dʌk*) is a GUI tool which enables you to parse multiple nessus files containing the results of scans performed by using Nessus by (C) Tenable, Inc. and exports parsed @@ -20,22 +20,37 @@ along with this program. If not, see . """ +import datetime + __all__ = [ - "__title__", "__icon__", "__summary__", "__uri__", "__version__", "__release_date__", "__author__", - "__email__", "__license_name__", "__license_link__", "__copyright__" + "__title__", + "__icon__", + "__summary__", + "__uri__", + "__version__", + "__release_date__", + "__author__", + "__email__", + "__license_name__", + "__license_link__", + "__copyright__", ] __title__ = "nessus file analyzer by LimberDuck" __package_name__ = "nessus-file-analyzer" __icon__ = "LimberDuck-nessus-file-analyzer.ico" -__summary__ = "nessus file analyzer by LimberDuck is a GUI tool which enables you to parse nessus scan files from " \ - "Nessus and Tenable.SC by (C) Tenable, Inc. and exports results to a Microsoft Excel Workbook for " \ - "effortless analysis." +__summary__ = ( + "nessus file analyzer by LimberDuck is a GUI tool which enables you to parse nessus scan files from " + "Nessus and Tenable.SC by (C) Tenable, Inc. and exports results to a Microsoft Excel Workbook for " + "effortless analysis." +) __uri__ = "https://limberduck.org" -__version__ = "0.7.2" -__release_date__ = "2022.05.13" -__author__ = u"Damian Krawczyk" +__version__ = "0.7.3" +__release_date__ = "2025.02.20" +__author__ = "Damian Krawczyk" __email__ = "damian.krawczyk@limberduck.org" __license_name__ = "GNU GPLv3" __license_link__ = "https://www.gnu.org/licenses/gpl-3.0.en.html" -__copyright__ = u"\N{COPYRIGHT SIGN} 2019-2022 by %s" % __author__ +__copyright__ = "\N{COPYRIGHT SIGN} 2019-{} by {}".format( + datetime.datetime.now().year, __author__ +) diff --git a/nessus_file_analyzer/__init__.py b/nessus_file_analyzer/__init__.py index 4060c5d..6c8c370 100644 --- a/nessus_file_analyzer/__init__.py +++ b/nessus_file_analyzer/__init__.py @@ -1,11 +1,29 @@ from .__about__ import ( - __title__, __icon__, __summary__, __uri__, __version__, __release_date__, __author__, - __email__, __license_name__, __license_link__, __copyright__ + __title__, + __icon__, + __summary__, + __uri__, + __version__, + __release_date__, + __author__, + __email__, + __license_name__, + __license_link__, + __copyright__, ) __all__ = [ - "__title__", "__icon__", "__summary__", "__uri__", "__version__", "__release_date__", "__author__", - "__email__", "__license_name__", "__license_link__", "__copyright__" + "__title__", + "__icon__", + "__summary__", + "__uri__", + "__version__", + "__release_date__", + "__author__", + "__email__", + "__license_name__", + "__license_link__", + "__copyright__", ] from .dialogs.about import About diff --git a/nessus_file_analyzer/__main__.py b/nessus_file_analyzer/__main__.py index 81f4b9f..adb741f 100644 --- a/nessus_file_analyzer/__main__.py +++ b/nessus_file_analyzer/__main__.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -u""" +""" nessus file analyzer by LimberDuck (pronounced *ˈlɪm.bɚ dʌk*) is a GUI tool which enables you to parse multiple nessus files containing the results of scans performed by using Nessus by (C) Tenable, Inc. and exports parsed @@ -29,6 +29,6 @@ def main(): app.main() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/nessus_file_analyzer/_version.py b/nessus_file_analyzer/_version.py index 0dd3b07..4910b9e 100644 --- a/nessus_file_analyzer/_version.py +++ b/nessus_file_analyzer/_version.py @@ -1 +1 @@ -__version__ = "0.7.2" \ No newline at end of file +__version__ = "0.7.3" diff --git a/nessus_file_analyzer/app.py b/nessus_file_analyzer/app.py index 9f35379..383ccf5 100644 --- a/nessus_file_analyzer/app.py +++ b/nessus_file_analyzer/app.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -u""" +""" nessus file analyzer by LimberDuck (pronounced *ˈlɪm.bɚ dʌk*) is a GUI tool which enables you to parse multiple nessus files containing the results of scans performed by using Nessus by (C) Tenable, Inc. and exports parsed @@ -47,88 +47,136 @@ def __init__(self, parent=None): self.__files_to_pars = [] self.__parsing_settings = {} - self.__target_directory = '' + self.__target_directory = "" self.__target_directory_changed = False self.__file_analysis_counter = 0 - self.__suffix = '' - self.update_parsing_settings('suffix', self.__suffix) - self.__suffix_template = '' - self.update_parsing_settings('suffix_template', self.__suffix_template) - self.__target_file_name_prefix = 'security_report' - self.update_parsing_settings('target_file_name_prefix', self.__target_file_name_prefix) + self.__suffix = "" + self.update_parsing_settings("suffix", self.__suffix) + self.__suffix_template = "" + self.update_parsing_settings("suffix_template", self.__suffix_template) + self.__target_file_name_prefix = "security_report" + self.update_parsing_settings( + "target_file_name_prefix", self.__target_file_name_prefix + ) # reports self.__report_scan_enabled = False - self.update_parsing_settings('report_scan_enabled', self.__report_scan_enabled) + self.update_parsing_settings("report_scan_enabled", self.__report_scan_enabled) self.__report_host_enabled = False - self.update_parsing_settings('report_host_enabled', self.__report_host_enabled) + self.update_parsing_settings("report_host_enabled", self.__report_host_enabled) self.__report_vulnerabilities_enabled = False - self.update_parsing_settings('report_vulnerabilities_enabled', self.__report_vulnerabilities_enabled) + self.update_parsing_settings( + "report_vulnerabilities_enabled", self.__report_vulnerabilities_enabled + ) self.__report_noncompliance_enabled = False - self.update_parsing_settings('report_noncompliance_enabled', self.__report_noncompliance_enabled) + self.update_parsing_settings( + "report_noncompliance_enabled", self.__report_noncompliance_enabled + ) # reports settings self.__report_scan_setting_debug_data_enabled = False - self.update_parsing_settings('report_scan_debug_data_enabled', - self.__report_scan_setting_debug_data_enabled) + self.update_parsing_settings( + "report_scan_debug_data_enabled", + self.__report_scan_setting_debug_data_enabled, + ) self.__report_host_setting_debug_data_enabled = False - self.update_parsing_settings('report_host_debug_data_enabled', - self.__report_host_setting_debug_data_enabled) + self.update_parsing_settings( + "report_host_debug_data_enabled", + self.__report_host_setting_debug_data_enabled, + ) self.__report_vulnerabilities_setting_debug_data_enabled = False - self.update_parsing_settings('report_vulnerabilities_debug_data_enabled', - self.__report_vulnerabilities_setting_debug_data_enabled) + self.update_parsing_settings( + "report_vulnerabilities_debug_data_enabled", + self.__report_vulnerabilities_setting_debug_data_enabled, + ) self.__report_vulnerabilities_setting_none_filter_out = False - self.update_parsing_settings('report_vulnerabilities_none_filter_out', - self.__report_vulnerabilities_setting_none_filter_out) + self.update_parsing_settings( + "report_vulnerabilities_none_filter_out", + self.__report_vulnerabilities_setting_none_filter_out, + ) self.__report_vulnerabilities_setting_none_skip = False - self.update_parsing_settings('report_vulnerabilities_none_skip', - self.__report_vulnerabilities_setting_none_skip) + self.update_parsing_settings( + "report_vulnerabilities_none_skip", + self.__report_vulnerabilities_setting_none_skip, + ) self.__report_noncompliance_setting_debug_data_enabled = False - self.update_parsing_settings('report_noncompliance_debug_data_enabled', - self.__report_noncompliance_enabled) + self.update_parsing_settings( + "report_noncompliance_debug_data_enabled", + self.__report_noncompliance_enabled, + ) self.__report_set_source_directory_as_target_directory_enabled = False - self.update_parsing_settings('set_source_directory_as_target_directory_enabled', - self.__report_set_source_directory_as_target_directory_enabled) - - self.parsing_thread = ParsingThread(files_to_pars=self.__files_to_pars, - target_directory=self.__target_directory, - target_directory_changed=self.__target_directory_changed, - parsing_settings=self.__parsing_settings) + self.update_parsing_settings( + "set_source_directory_as_target_directory_enabled", + self.__report_set_source_directory_as_target_directory_enabled, + ) + + self.parsing_thread = ParsingThread( + files_to_pars=self.__files_to_pars, + target_directory=self.__target_directory, + target_directory_changed=self.__target_directory_changed, + parsing_settings=self.__parsing_settings, + ) self.actionOpen_file.triggered.connect(self.open_files) self.actionOpen_directory.triggered.connect(self.open_directory) self.actionExit.triggered.connect(self.exit_application) self.actionStart_analysis.triggered.connect(self.parsing_thread_start) - self.actionChange_target_directory.triggered.connect(self.change_target_directory) + self.actionChange_target_directory.triggered.connect( + self.change_target_directory + ) self.actionOpen_target_directory.triggered.connect(self.open_target_directory) self.actionAbout.triggered.connect(self.open_dialog_about) self.checkBox_report_scan.stateChanged.connect(self.report_scan_changed) - self.checkBox_debug_data_scan.stateChanged.connect(self.report_scan_setting_debug_changed) + self.checkBox_debug_data_scan.stateChanged.connect( + self.report_scan_setting_debug_changed + ) self.checkBox_report_host.stateChanged.connect(self.report_host_changed) - self.checkBox_debug_data_host.stateChanged.connect(self.report_host_setting_debug_changed) - self.checkBox_report_vulnerabilities.stateChanged.connect(self.report_vulnerabilities_changed) + self.checkBox_debug_data_host.stateChanged.connect( + self.report_host_setting_debug_changed + ) + self.checkBox_report_vulnerabilities.stateChanged.connect( + self.report_vulnerabilities_changed + ) self.checkBox_debug_data_vulnerabilities.stateChanged.connect( - self.report_vulnerabilities_setting_debug_changed) + self.report_vulnerabilities_setting_debug_changed + ) self.checkBox_vulnerabilities_none_filter_out.stateChanged.connect( - self.report_vulnerabilities_setting_none_filter_out_changed) + self.report_vulnerabilities_setting_none_filter_out_changed + ) self.checkBox_vulnerabilities_none_skip.stateChanged.connect( - self.report_vulnerabilities_setting_none_skip_changed) - self.checkBox_report_noncompliance.stateChanged.connect(self.report_noncompliance_changed) - self.checkBox_debug_data_noncompliance.stateChanged.connect(self.report_noncompliance_setting_debug_changed) + self.report_vulnerabilities_setting_none_skip_changed + ) + self.checkBox_report_noncompliance.stateChanged.connect( + self.report_noncompliance_changed + ) + self.checkBox_debug_data_noncompliance.stateChanged.connect( + self.report_noncompliance_setting_debug_changed + ) self.checkBox_set_source_directory_as_target_directory.stateChanged.connect( - self.set_source_directory_as_target_directory_changed) - self.checkBox_suffix_timestamp.stateChanged.connect(self.suffix_timestamp_changed) + self.set_source_directory_as_target_directory_changed + ) + self.checkBox_suffix_timestamp.stateChanged.connect( + self.suffix_timestamp_changed + ) self.lineEdit_suffix_custom_value.setDisabled(True) - self.checkBox_suffix_custom.stateChanged.connect(self.check_box_suffix_state_changed) - self.lineEdit_suffix_custom_value.textChanged.connect(self.line_edit_suffix_custom_value_changed) + self.checkBox_suffix_custom.stateChanged.connect( + self.check_box_suffix_state_changed + ) + self.lineEdit_suffix_custom_value.textChanged.connect( + self.line_edit_suffix_custom_value_changed + ) # Match any character but \/:*?"<>| - reg_ex = QRegExp("[^\\\\/:*?\"<>|]+") - line_edit_suffix_custom_value_validator = QRegExpValidator(reg_ex, self.lineEdit_suffix_custom_value) - self.lineEdit_suffix_custom_value.setValidator(line_edit_suffix_custom_value_validator) + reg_ex = QRegExp('[^\\\\/:*?"<>|]+') + line_edit_suffix_custom_value_validator = QRegExpValidator( + reg_ex, self.lineEdit_suffix_custom_value + ) + self.lineEdit_suffix_custom_value.setValidator( + line_edit_suffix_custom_value_validator + ) self.pushButton_start.clicked.connect(self.parsing_thread_start) self.pushButton_target_dir_change.clicked.connect(self.change_target_directory) @@ -156,16 +204,18 @@ def __init__(self, parent=None): self.progressBar.setRange(0, 10) - self.print_log('If you don\'t know how to use particular options ' - 'hover mouse pointer on option for which you have any doubts to see tooltip. ' - 'Hover mouse pointer here, to see tooltip for progress preview.', - 'red') + self.print_log( + "If you don't know how to use particular options " + "hover mouse pointer on option for which you have any doubts to see tooltip. " + "Hover mouse pointer here, to see tooltip for progress preview.", + "red", + ) self.setAcceptDrops(True) def dragEnterEvent(self, e): - if e.mimeData().hasFormat('text/uri-list'): + if e.mimeData().hasFormat("text/uri-list"): e.accept() else: e.ignore() @@ -178,43 +228,46 @@ def print_settings(self): """ Function prints all settings specified by user. """ - print('>>>') + print(">>>") for setting, value in self.__parsing_settings.items(): if value: - print(setting, value, '++++++++++++++++++++++++++++') + print(setting, value, "++++++++++++++++++++++++++++") else: print(setting, value) - print('<<<') + print("<<<") def report_scan_changed(self): """ Function enables or disables setting which allow generation of spreadsheet with scan sum-up. """ if self.checkBox_report_scan.isChecked(): - info = 'Scan report enabled.' + info = "Scan report enabled." self.__report_scan_enabled = True self.groupBox_options_scan.setEnabled(True) else: - info = 'Scan report disabled.' + info = "Scan report disabled." self.__report_scan_enabled = False self.groupBox_options_scan.setDisabled(True) - color = 'green' + color = "green" self.print_log(info, color) - self.update_parsing_settings('report_scan_enabled', self.__report_scan_enabled) + self.update_parsing_settings("report_scan_enabled", self.__report_scan_enabled) # self.print_settings() - if (self.__report_scan_enabled + if ( + self.__report_scan_enabled or self.__report_host_enabled or self.__report_vulnerabilities_enabled - or self.__report_noncompliance_enabled) \ - and self.__files_to_pars: + or self.__report_noncompliance_enabled + ) and self.__files_to_pars: self.pushButton_start.setEnabled(True) self.actionStart_analysis.setEnabled(True) - if not self.__report_scan_enabled \ - and not self.__report_host_enabled \ - and not self.__report_vulnerabilities_enabled \ - and not self.__report_noncompliance_enabled: + if ( + not self.__report_scan_enabled + and not self.__report_host_enabled + and not self.__report_vulnerabilities_enabled + and not self.__report_noncompliance_enabled + ): self.pushButton_start.setDisabled(True) self.actionStart_analysis.setDisabled(True) @@ -224,14 +277,17 @@ def report_scan_setting_debug_changed(self): data. """ if self.checkBox_debug_data_scan.isChecked(): - info = 'Scan report debug data enabled.' + info = "Scan report debug data enabled." self.__report_scan_setting_debug_data_enabled = True else: - info = 'Scan report debug data disabled.' + info = "Scan report debug data disabled." self.__report_scan_setting_debug_data_enabled = False - color = 'green' + color = "green" self.print_log(info, color) - self.update_parsing_settings('report_scan_debug_data_enabled', self.__report_scan_setting_debug_data_enabled) + self.update_parsing_settings( + "report_scan_debug_data_enabled", + self.__report_scan_setting_debug_data_enabled, + ) # self.print_settings() def report_host_changed(self): @@ -239,30 +295,33 @@ def report_host_changed(self): Function enables or disables setting which allow generation of spreadsheet with host sum-up. """ if self.checkBox_report_host.isChecked(): - info = 'Host report enabled.' + info = "Host report enabled." self.__report_host_enabled = True self.groupBox_options_host.setEnabled(True) else: - info = 'Host report disabled.' + info = "Host report disabled." self.__report_host_enabled = False self.groupBox_options_host.setDisabled(True) - color = 'green' + color = "green" self.print_log(info, color) - self.update_parsing_settings('report_host_enabled', self.__report_host_enabled) + self.update_parsing_settings("report_host_enabled", self.__report_host_enabled) # self.print_settings() - if (self.__report_scan_enabled + if ( + self.__report_scan_enabled or self.__report_host_enabled or self.__report_vulnerabilities_enabled - or self.__report_noncompliance_enabled) \ - and self.__files_to_pars: + or self.__report_noncompliance_enabled + ) and self.__files_to_pars: self.pushButton_start.setEnabled(True) self.actionStart_analysis.setEnabled(True) - if not self.__report_scan_enabled \ - and not self.__report_host_enabled \ - and not self.__report_vulnerabilities_enabled \ - and not self.__report_noncompliance_enabled: + if ( + not self.__report_scan_enabled + and not self.__report_host_enabled + and not self.__report_vulnerabilities_enabled + and not self.__report_noncompliance_enabled + ): self.pushButton_start.setDisabled(True) self.actionStart_analysis.setDisabled(True) @@ -272,14 +331,17 @@ def report_host_setting_debug_changed(self): data. """ if self.checkBox_debug_data_host.isChecked(): - info = 'Host report debug data enabled.' + info = "Host report debug data enabled." self.__report_host_setting_debug_data_enabled = True else: - info = 'Host report debug data disabled.' + info = "Host report debug data disabled." self.__report_host_setting_debug_data_enabled = False - color = 'green' + color = "green" self.print_log(info, color) - self.update_parsing_settings('report_host_debug_data_enabled', self.__report_host_setting_debug_data_enabled) + self.update_parsing_settings( + "report_host_debug_data_enabled", + self.__report_host_setting_debug_data_enabled, + ) # self.print_settings() def report_vulnerabilities_changed(self): @@ -287,30 +349,35 @@ def report_vulnerabilities_changed(self): Function enables or disables setting which allow generation of spreadsheet with vulnerabilities sum-up. """ if self.checkBox_report_vulnerabilities.isChecked(): - info = 'Vulnerabilities report enabled.' + info = "Vulnerabilities report enabled." self.__report_vulnerabilities_enabled = True self.groupBox_options_vulnerabilities.setEnabled(True) else: - info = 'Vulnerabilities report disabled.' + info = "Vulnerabilities report disabled." self.__report_vulnerabilities_enabled = False self.groupBox_options_vulnerabilities.setDisabled(True) - color = 'green' + color = "green" self.print_log(info, color) - self.update_parsing_settings('report_vulnerabilities_enabled', self.__report_vulnerabilities_enabled) + self.update_parsing_settings( + "report_vulnerabilities_enabled", self.__report_vulnerabilities_enabled + ) # self.print_settings() - if (self.__report_scan_enabled + if ( + self.__report_scan_enabled or self.__report_host_enabled or self.__report_vulnerabilities_enabled - or self.__report_noncompliance_enabled) \ - and self.__files_to_pars: + or self.__report_noncompliance_enabled + ) and self.__files_to_pars: self.pushButton_start.setEnabled(True) self.actionStart_analysis.setEnabled(True) - if not self.__report_scan_enabled \ - and not self.__report_host_enabled \ - and not self.__report_vulnerabilities_enabled \ - and not self.__report_noncompliance_enabled: + if ( + not self.__report_scan_enabled + and not self.__report_host_enabled + and not self.__report_vulnerabilities_enabled + and not self.__report_noncompliance_enabled + ): self.pushButton_start.setDisabled(True) self.actionStart_analysis.setDisabled(True) @@ -320,15 +387,17 @@ def report_vulnerabilities_setting_debug_changed(self): additional data. """ if self.checkBox_debug_data_vulnerabilities.isChecked(): - info = 'Vulnerabilities report debug data enabled.' + info = "Vulnerabilities report debug data enabled." self.__report_vulnerabilities_setting_debug_data_enabled = True else: - info = 'Vulnerabilities report debug data disabled.' + info = "Vulnerabilities report debug data disabled." self.__report_vulnerabilities_setting_debug_data_enabled = False - color = 'green' + color = "green" self.print_log(info, color) - self.update_parsing_settings('report_vulnerabilities_debug_data_enabled', - self.__report_vulnerabilities_setting_debug_data_enabled) + self.update_parsing_settings( + "report_vulnerabilities_debug_data_enabled", + self.__report_vulnerabilities_setting_debug_data_enabled, + ) # self.print_settings() def report_vulnerabilities_setting_none_filter_out_changed(self): @@ -337,17 +406,19 @@ def report_vulnerabilities_setting_none_filter_out_changed(self): filtered out vulnerabilities with Risk Factor equal None. """ if self.checkBox_vulnerabilities_none_filter_out.isChecked(): - info = 'Vulnerabilities report None plugins filter out enabled.' + info = "Vulnerabilities report None plugins filter out enabled." self.__report_vulnerabilities_setting_none_filter_out = True if self.checkBox_vulnerabilities_none_skip.isChecked(): self.checkBox_vulnerabilities_none_skip.setChecked(False) else: - info = 'Vulnerabilities report None plugins filter out disabled.' + info = "Vulnerabilities report None plugins filter out disabled." self.__report_vulnerabilities_setting_none_filter_out = False - color = 'green' + color = "green" self.print_log(info, color) - self.update_parsing_settings('report_vulnerabilities_none_filter_out', - self.__report_vulnerabilities_setting_none_filter_out) + self.update_parsing_settings( + "report_vulnerabilities_none_filter_out", + self.__report_vulnerabilities_setting_none_filter_out, + ) # self.print_settings() def report_vulnerabilities_setting_none_skip_changed(self): @@ -356,17 +427,19 @@ def report_vulnerabilities_setting_none_skip_changed(self): vulnerabilities with Risk Factor equal None. """ if self.checkBox_vulnerabilities_none_skip.isChecked(): - info = 'Vulnerabilities report None plugins skip enabled.' + info = "Vulnerabilities report None plugins skip enabled." self.__report_vulnerabilities_setting_none_skip = True if self.checkBox_vulnerabilities_none_filter_out.isChecked(): self.checkBox_vulnerabilities_none_filter_out.setChecked(False) else: - info = 'Vulnerabilities report None plugins skip disabled.' + info = "Vulnerabilities report None plugins skip disabled." self.__report_vulnerabilities_setting_none_skip = False - color = 'green' + color = "green" self.print_log(info, color) - self.update_parsing_settings('report_vulnerabilities_none_skip', - self.__report_vulnerabilities_setting_none_skip) + self.update_parsing_settings( + "report_vulnerabilities_none_skip", + self.__report_vulnerabilities_setting_none_skip, + ) # self.print_settings() def report_noncompliance_changed(self): @@ -374,30 +447,35 @@ def report_noncompliance_changed(self): Function enables or disables setting which allow generation of spreadsheet with noncompliance sum-up. """ if self.checkBox_report_noncompliance.isChecked(): - info = 'Noncompliance report enabled.' + info = "Noncompliance report enabled." self.__report_noncompliance_enabled = True self.groupBox_options_noncompliance.setEnabled(True) else: - info = 'Noncompliance report disabled.' + info = "Noncompliance report disabled." self.__report_noncompliance_enabled = False self.groupBox_options_noncompliance.setDisabled(True) - color = 'green' + color = "green" self.print_log(info, color) - self.update_parsing_settings('report_noncompliance_enabled', self.__report_noncompliance_enabled) + self.update_parsing_settings( + "report_noncompliance_enabled", self.__report_noncompliance_enabled + ) # self.print_settings() - if (self.__report_scan_enabled + if ( + self.__report_scan_enabled or self.__report_host_enabled or self.__report_vulnerabilities_enabled - or self.__report_noncompliance_enabled) \ - and self.__files_to_pars: + or self.__report_noncompliance_enabled + ) and self.__files_to_pars: self.pushButton_start.setEnabled(True) self.actionStart_analysis.setEnabled(True) - if not self.__report_scan_enabled \ - and not self.__report_host_enabled \ - and not self.__report_vulnerabilities_enabled \ - and not self.__report_noncompliance_enabled: + if ( + not self.__report_scan_enabled + and not self.__report_host_enabled + and not self.__report_vulnerabilities_enabled + and not self.__report_noncompliance_enabled + ): self.pushButton_start.setDisabled(True) self.actionStart_analysis.setDisabled(True) @@ -407,15 +485,17 @@ def report_noncompliance_setting_debug_changed(self): additional data. """ if self.checkBox_debug_data_noncompliance.isChecked(): - info = 'noncompliance report debug data enabled.' + info = "noncompliance report debug data enabled." self.__report_noncompliance_setting_debug_data_enabled = True else: - info = 'noncompliance report debug data disabled.' + info = "noncompliance report debug data disabled." self.__report_noncompliance_setting_debug_data_enabled = False - color = 'green' + color = "green" self.print_log(info, color) - self.update_parsing_settings('report_noncompliance_debug_data_enabled', - self.__report_noncompliance_setting_debug_data_enabled) + self.update_parsing_settings( + "report_noncompliance_debug_data_enabled", + self.__report_noncompliance_setting_debug_data_enabled, + ) # self.print_settings() def set_source_directory_as_target_directory_changed(self): @@ -424,16 +504,18 @@ def set_source_directory_as_target_directory_changed(self): directory. """ if self.checkBox_set_source_directory_as_target_directory.isChecked(): - info = 'Target directory based on selected source enabled.' + info = "Target directory based on selected source enabled." self.__report_set_source_directory_as_target_directory_enabled = True else: - info = 'Target directory based on selected source disabled.' + info = "Target directory based on selected source disabled." self.__report_set_source_directory_as_target_directory_enabled = False - color = 'green' + color = "green" self.print_log(info, color) - self.update_parsing_settings('report_set_source_directory_as_target_directory_enabled', - self.__report_set_source_directory_as_target_directory_enabled) + self.update_parsing_settings( + "report_set_source_directory_as_target_directory_enabled", + self.__report_set_source_directory_as_target_directory_enabled, + ) self.set_source_directory_as_target_directory() @@ -451,9 +533,14 @@ def set_source_directory_as_target_directory(self): first_file_path_normalized = os.path.normpath(first_file_path) self.set_target_directory(first_file_path_normalized) self.lineEdit_target_directory.setText(self.__target_directory) - color = 'green' - info2 = 'Target directory changed from "' + old_target_directory + \ - '" to "' + self.__target_directory + '"' + color = "green" + info2 = ( + 'Target directory changed from "' + + old_target_directory + + '" to "' + + self.__target_directory + + '"' + ) self.print_log(info2, color) self.__target_directory_changed = True else: @@ -462,9 +549,14 @@ def set_source_directory_as_target_directory(self): cwd_normalized = os.path.normpath(cwd) self.set_target_directory(cwd_normalized) self.lineEdit_target_directory.setText(self.__target_directory) - color = 'green' - info2 = 'Target directory changed from "' + old_target_directory + \ - '" to "' + self.__target_directory + '"' + color = "green" + info2 = ( + 'Target directory changed from "' + + old_target_directory + + '" to "' + + self.__target_directory + + '"' + ) self.print_log(info2, color) self.__target_directory_changed = True @@ -472,121 +564,168 @@ def suffix_timestamp_changed(self): """ Function sets suffix appropriately if checkBox_suffix_timestamp has changed. """ - if self.checkBox_suffix_timestamp.isChecked() and not self.checkBox_suffix_custom.isChecked(): + if ( + self.checkBox_suffix_timestamp.isChecked() + and not self.checkBox_suffix_custom.isChecked() + ): time_now = datetime.datetime.now() - time_now_formatted = time_now.strftime('%Y%m%d_%H%M%S') - suffix = '_' + time_now_formatted + time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S") + suffix = "_" + time_now_formatted self.change_suffix(suffix) - self.label_target_file_name_value.setText(self.__target_file_name_prefix + suffix + '.xlsx') - self.__suffix_template = 'suffix_timestamp' - - elif not self.checkBox_suffix_timestamp.isChecked() and self.checkBox_suffix_custom.isChecked(): + self.label_target_file_name_value.setText( + self.__target_file_name_prefix + suffix + ".xlsx" + ) + self.__suffix_template = "suffix_timestamp" + + elif ( + not self.checkBox_suffix_timestamp.isChecked() + and self.checkBox_suffix_custom.isChecked() + ): suffix_custom_value = self.lineEdit_suffix_custom_value.text() if suffix_custom_value: - space = '_' - self.__suffix_template = 'suffix_custom' + space = "_" + self.__suffix_template = "suffix_custom" else: - space = '' - self.__suffix_template = 'suffix_custom_empty' + space = "" + self.__suffix_template = "suffix_custom_empty" suffix = space + suffix_custom_value self.change_suffix(suffix) - self.label_target_file_name_value.setText(self.__target_file_name_prefix + suffix + '.xlsx') - - elif self.checkBox_suffix_timestamp.isChecked() and self.checkBox_suffix_custom.isChecked(): + self.label_target_file_name_value.setText( + self.__target_file_name_prefix + suffix + ".xlsx" + ) + + elif ( + self.checkBox_suffix_timestamp.isChecked() + and self.checkBox_suffix_custom.isChecked() + ): time_now = datetime.datetime.now() - time_now_formatted = time_now.strftime('%Y%m%d_%H%M%S') + time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S") suffix_custom_value = self.lineEdit_suffix_custom_value.text() if suffix_custom_value: - space = '_' - self.__suffix_template = 'suffix_custom_timestamp' + space = "_" + self.__suffix_template = "suffix_custom_timestamp" else: - space = '' - self.__suffix_template = 'suffix_custom_empty_timestamp' - suffix = space + suffix_custom_value + '_' + time_now_formatted + space = "" + self.__suffix_template = "suffix_custom_empty_timestamp" + suffix = space + suffix_custom_value + "_" + time_now_formatted self.change_suffix(suffix) - self.label_target_file_name_value.setText(self.__target_file_name_prefix + suffix + '.xlsx') + self.label_target_file_name_value.setText( + self.__target_file_name_prefix + suffix + ".xlsx" + ) else: - self.change_suffix('') - self.label_target_file_name_value.setText(self.__target_file_name_prefix + '.xlsx') - self.__suffix_template = 'empty' + self.change_suffix("") + self.label_target_file_name_value.setText( + self.__target_file_name_prefix + ".xlsx" + ) + self.__suffix_template = "empty" def check_box_suffix_state_changed(self): """ Function enables and disables lineEdit_suffix_custom_value and sets suffix appropriately. """ - if self.checkBox_suffix_custom.isChecked() and not self.checkBox_suffix_timestamp.isChecked(): + if ( + self.checkBox_suffix_custom.isChecked() + and not self.checkBox_suffix_timestamp.isChecked() + ): self.lineEdit_suffix_custom_value.setEnabled(True) suffix_custom_value = self.lineEdit_suffix_custom_value.text() if suffix_custom_value: - space = '_' - self.__suffix_template = 'suffix_custom' + space = "_" + self.__suffix_template = "suffix_custom" else: - space = '' - self.__suffix_template = 'suffix_custom_empty' + space = "" + self.__suffix_template = "suffix_custom_empty" suffix = space + suffix_custom_value self.change_suffix(suffix) - self.label_target_file_name_value.setText(self.__target_file_name_prefix + suffix + '.xlsx') - - elif self.checkBox_suffix_custom.isChecked() and self.checkBox_suffix_timestamp.isChecked(): + self.label_target_file_name_value.setText( + self.__target_file_name_prefix + suffix + ".xlsx" + ) + + elif ( + self.checkBox_suffix_custom.isChecked() + and self.checkBox_suffix_timestamp.isChecked() + ): self.lineEdit_suffix_custom_value.setEnabled(True) time_now = datetime.datetime.now() - time_now_formatted = time_now.strftime('%Y%m%d_%H%M%S') + time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S") suffix_custom_value = self.lineEdit_suffix_custom_value.text() if suffix_custom_value: - space = '_' - self.__suffix_template = 'suffix_timestamp_custom' + space = "_" + self.__suffix_template = "suffix_timestamp_custom" else: - space = '' - self.__suffix_template = 'suffix_timestamp_custom_empty' - suffix = '_' + time_now_formatted + space + suffix_custom_value + space = "" + self.__suffix_template = "suffix_timestamp_custom_empty" + suffix = "_" + time_now_formatted + space + suffix_custom_value self.change_suffix(suffix) - self.label_target_file_name_value.setText(self.__target_file_name_prefix + suffix + '.xlsx') - - elif not self.checkBox_suffix_custom.isChecked() and self.checkBox_suffix_timestamp.isChecked(): + self.label_target_file_name_value.setText( + self.__target_file_name_prefix + suffix + ".xlsx" + ) + + elif ( + not self.checkBox_suffix_custom.isChecked() + and self.checkBox_suffix_timestamp.isChecked() + ): self.lineEdit_suffix_custom_value.setDisabled(True) time_now = datetime.datetime.now() - time_now_formatted = time_now.strftime('%Y%m%d_%H%M%S') - suffix = '_' + time_now_formatted + time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S") + suffix = "_" + time_now_formatted self.change_suffix(suffix) - self.__suffix_template = 'suffix_timestamp' - self.label_target_file_name_value.setText(self.__target_file_name_prefix + suffix + '.xlsx') - - elif not self.checkBox_suffix_custom.isChecked() and not self.checkBox_suffix_timestamp.isChecked(): + self.__suffix_template = "suffix_timestamp" + self.label_target_file_name_value.setText( + self.__target_file_name_prefix + suffix + ".xlsx" + ) + + elif ( + not self.checkBox_suffix_custom.isChecked() + and not self.checkBox_suffix_timestamp.isChecked() + ): self.lineEdit_suffix_custom_value.setDisabled(True) - self.change_suffix('') - self.label_target_file_name_value.setText(self.__target_file_name_prefix + '.xlsx') - self.__suffix_template = 'empty' + self.change_suffix("") + self.label_target_file_name_value.setText( + self.__target_file_name_prefix + ".xlsx" + ) + self.__suffix_template = "empty" def line_edit_suffix_custom_value_changed(self): """ Function sets suffix appropriately if lineEdit_suffix_custom_value has changed. """ - if self.checkBox_suffix_custom.isChecked() and not self.checkBox_suffix_timestamp.isChecked(): + if ( + self.checkBox_suffix_custom.isChecked() + and not self.checkBox_suffix_timestamp.isChecked() + ): suffix_custom_value = self.lineEdit_suffix_custom_value.text() if suffix_custom_value: - space = '_' - self.__suffix_template = 'suffix_custom' + space = "_" + self.__suffix_template = "suffix_custom" else: - space = '' - self.__suffix_template = 'suffix_custom_empty' + space = "" + self.__suffix_template = "suffix_custom_empty" suffix = space + suffix_custom_value self.change_suffix(suffix) - self.label_target_file_name_value.setText(self.__target_file_name_prefix + suffix + '.xlsx') - - elif self.checkBox_suffix_custom.isChecked() and self.checkBox_suffix_timestamp.isChecked(): + self.label_target_file_name_value.setText( + self.__target_file_name_prefix + suffix + ".xlsx" + ) + + elif ( + self.checkBox_suffix_custom.isChecked() + and self.checkBox_suffix_timestamp.isChecked() + ): time_now = datetime.datetime.now() - time_now_formatted = time_now.strftime('%Y%m%d_%H%M%S') + time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S") suffix_custom_value = self.lineEdit_suffix_custom_value.text() if suffix_custom_value: - space = '_' - self.__suffix_template = 'suffix_timestamp_custom' + space = "_" + self.__suffix_template = "suffix_timestamp_custom" else: - space = '' - self.__suffix_template = 'suffix_timestamp_custom_empty' - suffix = '_' + time_now_formatted + space + suffix_custom_value + space = "" + self.__suffix_template = "suffix_timestamp_custom_empty" + suffix = "_" + time_now_formatted + space + suffix_custom_value self.change_suffix(suffix) - self.label_target_file_name_value.setText(self.__target_file_name_prefix + suffix + '.xlsx') + self.label_target_file_name_value.setText( + self.__target_file_name_prefix + suffix + ".xlsx" + ) @staticmethod def open_dialog_about(): @@ -603,62 +742,70 @@ def parsing_thread_start(self): self.statusbar.clearMessage() self.progressBar.setVisible(True) - info = 'Analysis started.' - color = 'blue' + info = "Analysis started." + color = "blue" self.print_log(info, color=color) try: - if self.__suffix_template == 'suffix_timestamp': + if self.__suffix_template == "suffix_timestamp": time_now = datetime.datetime.now() - time_now_formatted = time_now.strftime('%Y%m%d_%H%M%S') - self.update_parsing_settings('suffix', '_' + time_now_formatted) + time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S") + self.update_parsing_settings("suffix", "_" + time_now_formatted) - elif self.__suffix_template == 'suffix_custom': + elif self.__suffix_template == "suffix_custom": suffix_custom_value = self.lineEdit_suffix_custom_value.text() - self.update_parsing_settings('suffix', '_' + suffix_custom_value) + self.update_parsing_settings("suffix", "_" + suffix_custom_value) - elif self.__suffix_template == 'suffix_custom_empty': - self.update_parsing_settings('suffix', '') + elif self.__suffix_template == "suffix_custom_empty": + self.update_parsing_settings("suffix", "") - elif self.__suffix_template == 'suffix_custom_timestamp': + elif self.__suffix_template == "suffix_custom_timestamp": time_now = datetime.datetime.now() - time_now_formatted = time_now.strftime('%Y%m%d_%H%M%S') + time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S") suffix_custom_value = self.lineEdit_suffix_custom_value.text() - self.update_parsing_settings('suffix', '_' + suffix_custom_value + '_' + time_now_formatted) + self.update_parsing_settings( + "suffix", "_" + suffix_custom_value + "_" + time_now_formatted + ) - elif self.__suffix_template == 'suffix_custom_empty_timestamp': + elif self.__suffix_template == "suffix_custom_empty_timestamp": time_now = datetime.datetime.now() - time_now_formatted = time_now.strftime('%Y%m%d_%H%M%S') - self.update_parsing_settings('suffix', '_' + time_now_formatted) + time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S") + self.update_parsing_settings("suffix", "_" + time_now_formatted) - elif self.__suffix_template == 'suffix_timestamp_custom': + elif self.__suffix_template == "suffix_timestamp_custom": time_now = datetime.datetime.now() - time_now_formatted = time_now.strftime('%Y%m%d_%H%M%S') + time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S") suffix_custom_value = self.lineEdit_suffix_custom_value.text() - self.update_parsing_settings('suffix', '_' + time_now_formatted + '_' + suffix_custom_value) + self.update_parsing_settings( + "suffix", "_" + time_now_formatted + "_" + suffix_custom_value + ) - elif self.__suffix_template == 'suffix_timestamp_custom_empty': + elif self.__suffix_template == "suffix_timestamp_custom_empty": time_now = datetime.datetime.now() - time_now_formatted = time_now.strftime('%Y%m%d_%H%M%S') - self.update_parsing_settings('suffix', '_' + time_now_formatted) - - elif self.__suffix_template == 'empty': - self.update_parsing_settings('suffix', '') - - self.parsing_thread = ParsingThread(files_to_pars=self.__files_to_pars, - target_directory=self.__target_directory, - target_directory_changed=self.__target_directory_changed, - parsing_settings=self.__parsing_settings) + time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S") + self.update_parsing_settings("suffix", "_" + time_now_formatted) + + elif self.__suffix_template == "empty": + self.update_parsing_settings("suffix", "") + + self.parsing_thread = ParsingThread( + files_to_pars=self.__files_to_pars, + target_directory=self.__target_directory, + target_directory_changed=self.__target_directory_changed, + parsing_settings=self.__parsing_settings, + ) self.parsing_thread.start() self.parsing_thread.signal.connect(self.parsing_thread_done) self.parsing_thread.progress.connect(self.analysis_progress) - self.parsing_thread.print_status_bar_info.connect(self.print_status_bar_info) + self.parsing_thread.print_status_bar_info.connect( + self.print_status_bar_info + ) except Exception as e: - color = 'red' - self.print_log('\nUps... ERROR occurred. \n\n' + str(e), color=color) + color = "red" + self.print_log("\nUps... ERROR occurred. \n\n" + str(e), color=color) traceback.print_exc() - print('>>>', e, '<<<') + print(">>>", e, "<<<") def print_status_bar_info(self, text): """ @@ -684,14 +831,14 @@ def parsing_thread_done(self, info): Function shows information from parsing threads. :param info: information to display """ - if '[action=start]' in info: - color = 'blue' + if "[action=start]" in info: + color = "blue" self.print_log(info, color) - elif '[action=end ]' in info: - color = 'green' + elif "[action=end ]" in info: + color = "green" self.print_log(info, color) else: - color = 'black' + color = "black" self.print_log(info, color) def update_parsing_settings(self, setting_name, setting_value): @@ -708,7 +855,7 @@ def set_suffix(self, suffix_value): :param suffix_value: input suffix """ self.__suffix = suffix_value - self.update_parsing_settings('suffix', suffix_value) + self.update_parsing_settings("suffix", suffix_value) def change_suffix(self, suffix_value): """ @@ -721,7 +868,7 @@ def change_suffix(self, suffix_value): self.set_suffix(new_suffix) - color = 'green' + color = "green" info = 'Suffix changed from "' + old_suffix + '" to "' + new_suffix + '"' self.print_log(info, color) @@ -755,27 +902,35 @@ def change_target_directory(self): """ old_target_directory = self.__target_directory - title = 'Choose new target directory' - starting_directory = '' + title = "Choose new target directory" + starting_directory = "" options = QFileDialog.Options() options |= QFileDialog.ShowDirsOnly file_dialog = QFileDialog() - directories = file_dialog.getExistingDirectory(None, title, starting_directory, options=options) + directories = file_dialog.getExistingDirectory( + None, title, starting_directory, options=options + ) if directories: directories = nfr.file.nessus_scan_file_name_with_path(directories) self.set_target_directory(directories) self.get_target_directory_from_file() - color = 'green' - info = 'Target directory changed from "' + old_target_directory + '" to "' + self.__target_directory + '"' + color = "green" + info = ( + 'Target directory changed from "' + + old_target_directory + + '" to "' + + self.__target_directory + + '"' + ) self.print_log(info, color) self.__target_directory_changed = True else: - info = 'Target directory not changed.' - color = 'black' + info = "Target directory not changed." + color = "black" self.print_log(info, color=color) self.__target_directory_changed = False @@ -784,22 +939,26 @@ def open_files(self): Function get list of files via dialog window. Possible to select one or more files. """ - info = 'File\\-s opening.' - color = 'black' + info = "File\\-s opening." + color = "black" self.print_log(info, color=color) - extension = ['nessus', 'zip'] + extension = ["nessus", "zip"] - title = f'Open {extension[0]} or {extension[1]} containing {extension[0]} files' - starting_directory = '' - file_filter = f'Nessus scan file (*.{extension[0]});;ZIP Archive (*.{extension[1]})' + title = f"Open {extension[0]} or {extension[1]} containing {extension[0]} files" + starting_directory = "" + file_filter = ( + f"Nessus scan file (*.{extension[0]});;ZIP Archive (*.{extension[1]})" + ) options = QFileDialog.Options() options |= QFileDialog.ReadOnly file_dialog = QFileDialog() file_dialog.setFileMode(QFileDialog.ExistingFiles) - files = file_dialog.getOpenFileNames(self, title, starting_directory, filter=file_filter, options=options) + files = file_dialog.getOpenFileNames( + self, title, starting_directory, filter=file_filter, options=options + ) files_only = [os.path.abspath(item) for item in files[0]] # print(f'File open, after map {len(files_only)} {files_only}') @@ -816,10 +975,12 @@ def open_files(self): self.list_of_files_to_pars(files_only) if len(files_only) > 0: - if (self.__report_scan_enabled - or self.__report_host_enabled - or self.__report_vulnerabilities_enabled - or self.__report_noncompliance_enabled): + if ( + self.__report_scan_enabled + or self.__report_host_enabled + or self.__report_vulnerabilities_enabled + or self.__report_noncompliance_enabled + ): self.pushButton_start.setEnabled(True) self.actionStart_analysis.setEnabled(True) else: @@ -835,7 +996,7 @@ def check_if_subdirectory_exist(main_directory): number_of_directories > 0 if there is any subdirectory, where number_of_directories is information about number of subdirectories. """ - pattern = os.path.join(main_directory, '*') + pattern = os.path.join(main_directory, "*") number_of_directories = 0 for candidate in glob.glob(pattern): if os.path.isdir(candidate): @@ -848,19 +1009,21 @@ def open_directory(self): Function gets list of files via dialog window. Possible to get files from selected directory and subdirectories. """ - info = 'Files from directory and subdirectories opening.' - color = 'black' + info = "Files from directory and subdirectories opening." + color = "black" self.print_log(info, color=color) - extension = ['nessus', 'zip'] + extension = ["nessus", "zip"] - title = f'Open {extension[0]} or {extension[1]} containing {extension[0]} files directory' - starting_directory = '' + title = f"Open {extension[0]} or {extension[1]} containing {extension[0]} files directory" + starting_directory = "" options = QFileDialog.Options() options |= QFileDialog.ShowDirsOnly file_dialog = QFileDialog() - directories = file_dialog.getExistingDirectory(None, title, starting_directory, options=options) + directories = file_dialog.getExistingDirectory( + None, title, starting_directory, options=options + ) os_separator = os.path.sep target_directory = os.path.abspath(directories) @@ -871,20 +1034,36 @@ def open_directory(self): self.get_target_directory_from_directory() # print(target_directory) - nessus_files = glob.glob(target_directory + os_separator + '**' + os_separator + '*.' + extension[0], - recursive=True) - zip_files = glob.glob(target_directory + os_separator + '**' + os_separator + '*.' + extension[1], - recursive=True) + nessus_files = glob.glob( + target_directory + + os_separator + + "**" + + os_separator + + "*." + + extension[0], + recursive=True, + ) + zip_files = glob.glob( + target_directory + + os_separator + + "**" + + os_separator + + "*." + + extension[1], + recursive=True, + ) files = nessus_files + zip_files # print(f'Dir open, {len(files)} {files}') self.list_of_files_to_pars(files) if len(files) > 0: - if (self.__report_scan_enabled - or self.__report_host_enabled - or self.__report_vulnerabilities_enabled - or self.__report_noncompliance_enabled): + if ( + self.__report_scan_enabled + or self.__report_host_enabled + or self.__report_vulnerabilities_enabled + or self.__report_noncompliance_enabled + ): self.pushButton_start.setEnabled(True) self.actionStart_analysis.setEnabled(True) else: @@ -896,8 +1075,8 @@ def open_files_by_drag_and_drop(self, qurls): Function get list of files via Drag and Drop. Possible to select one or more files or directories. """ - info = 'File\\-s opening by Drag & Drop.' - color = 'black' + info = "File\\-s opening by Drag & Drop." + color = "black" self.print_log(info, color=color) paths = [] @@ -906,15 +1085,19 @@ def open_files_by_drag_and_drop(self, qurls): url_data = urllib.parse.urlparse(url) path = urllib.parse.unquote(url_data.path) os_name = platform.system() - if os_name == 'Windows': + if os_name == "Windows": path = os.path.abspath(path[1:]) - extension = ['nessus', 'zip'] + extension = ["nessus", "zip"] if os.path.isfile(path): - if fnmatch.fnmatch(path, f'*.{extension[0]}') or fnmatch.fnmatch(path, f'*.{extension[1]}'): + if fnmatch.fnmatch(path, f"*.{extension[0]}") or fnmatch.fnmatch( + path, f"*.{extension[1]}" + ): paths.append(path) - if self.checkBox_set_source_directory_as_target_directory.isChecked(): + if ( + self.checkBox_set_source_directory_as_target_directory.isChecked() + ): target_directory = os.path.dirname(os.path.abspath(path)) self.set_target_directory(target_directory) self.get_target_directory_from_file() @@ -926,10 +1109,24 @@ def open_files_by_drag_and_drop(self, qurls): self.set_target_directory(target_directory) self.get_target_directory_from_directory() - nessus_files = glob.glob(target_directory + os_separator + '**' + os_separator + '*.' + extension[0], - recursive=True) - zip_files = glob.glob(target_directory + os_separator + '**' + os_separator + '*.' + extension[1], - recursive=True) + nessus_files = glob.glob( + target_directory + + os_separator + + "**" + + os_separator + + "*." + + extension[0], + recursive=True, + ) + zip_files = glob.glob( + target_directory + + os_separator + + "**" + + os_separator + + "*." + + extension[1], + recursive=True, + ) files = nessus_files + zip_files for file in files: paths.append(file) @@ -937,10 +1134,12 @@ def open_files_by_drag_and_drop(self, qurls): self.list_of_files_to_pars(paths) if len(paths) > 0: - if (self.__report_scan_enabled - or self.__report_host_enabled - or self.__report_vulnerabilities_enabled - or self.__report_noncompliance_enabled): + if ( + self.__report_scan_enabled + or self.__report_host_enabled + or self.__report_vulnerabilities_enabled + or self.__report_noncompliance_enabled + ): self.pushButton_start.setEnabled(True) self.actionStart_analysis.setEnabled(True) @@ -957,15 +1156,15 @@ def open_target_directory(self): """ os_name = platform.system() - if os_name == 'Darwin': - subprocess.call(['open', self.__target_directory]) - elif os_name == 'Windows': - subprocess.call(['explorer', self.__target_directory]) - elif os_name == 'Linux': - subprocess.call(['nautilus', self.__target_directory]) + if os_name == "Darwin": + subprocess.call(["open", self.__target_directory]) + elif os_name == "Windows": + subprocess.call(["explorer", self.__target_directory]) + elif os_name == "Linux": + subprocess.call(["nautilus", self.__target_directory]) else: - info = f'Can\'t open directory, your Operating System {os_name} is not supported. Please report it to us.' - color = 'red' + info = f"Can't open directory, your Operating System {os_name} is not supported. Please report it to us." + color = "red" self.print_log(info, color=color) def print_log(self, log_value, color): @@ -974,18 +1173,23 @@ def print_log(self, log_value, color): :param log_value: information to display :param color: color for given information """ - if color == 'black': + if color == "black": color_set = QColor(0, 0, 0) - elif color == 'red': + elif color == "red": color_set = QColor(230, 30, 30) - elif color == 'green': + elif color == "green": color_set = QColor(60, 160, 60) - elif color == 'blue': + elif color == "blue": color_set = QColor(0, 0, 255) else: color_set = QColor(0, 0, 0) - log_output = '[' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f') + '] ' + log_value + log_output = ( + "[" + + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") + + "] " + + log_value + ) self.textEdit_progress.setTextColor(color_set) self.textEdit_progress.append(log_output) @@ -996,9 +1200,9 @@ def exit_application(self): """ Function to exit from application. """ - info = 'Exit application' + info = "Exit application" print(info) - color = 'black' + color = "black" self.print_log(info, color=color) self.close() @@ -1011,55 +1215,64 @@ def list_of_files_to_pars(self, files): """ number_of_files = len(files) if number_of_files == 1: - suffix = '' + suffix = "" else: - suffix = 's' - info = f'Selected {str(number_of_files)} file{suffix}.' - color = 'blue' + suffix = "s" + info = f"Selected {str(number_of_files)} file{suffix}." + color = "blue" self.print_log(info, color=color) self.print_status_bar_info(info) self.__files_to_pars = files for file_to_pars in self.__files_to_pars: file_to_pars = nfr.file.nessus_scan_file_name_with_path(file_to_pars) - color = 'black' - action_name = 'info ' - notification_info = f'[action={action_name}] [source_file={file_to_pars}]' + color = "black" + action_name = "info " + notification_info = f"[action={action_name}] [source_file={file_to_pars}]" self.print_log(notification_info, color=color) nessus_files_only = [] - if fnmatch.fnmatch(file_to_pars, '*.zip'): + if fnmatch.fnmatch(file_to_pars, "*.zip"): zip_file_to_pars = zipfile.ZipFile(file_to_pars) nessus_file_names = zip_file_to_pars.namelist() for nessus_file_name in nessus_file_names: - if fnmatch.fnmatch(nessus_file_name, '*.nessus'): + if fnmatch.fnmatch(nessus_file_name, "*.nessus"): # print('nessus file') nessus_files_only.append(nessus_file_name) # print(len(nessus_files_only), nessus_files_only) if len(nessus_files_only) == 0: - color = 'red' - action_name = 'info ' - notification_info = f'[action={action_name}] [There is no nessus files inside this zip archive.]' + color = "red" + action_name = "info " + notification_info = f"[action={action_name}] [There is no nessus files inside this zip archive.]" self.print_log(notification_info, color=color) else: for nessus_file_only in nessus_files_only: - color = 'black' - action_name = 'info ' - notification_info = f'[action={action_name}] [source_file={nessus_file_only}]' + color = "black" + action_name = "info " + notification_info = ( + f"[action={action_name}] [source_file={nessus_file_only}]" + ) self.print_log(notification_info, color=color) self.__file_analysis_counter = 0 class ParsingThread(QThread): - signal = pyqtSignal('PyQt_PyObject') + signal = pyqtSignal("PyQt_PyObject") progress = pyqtSignal(int, int) file_analysis_started = pyqtSignal(int) - print_status_bar_info = pyqtSignal('QString') - - def __init__(self, files_to_pars, target_directory, target_directory_changed, parsing_settings, parent=None): + print_status_bar_info = pyqtSignal("QString") + + def __init__( + self, + files_to_pars, + target_directory, + target_directory_changed, + parsing_settings, + parent=None, + ): super(ParsingThread, self).__init__(parent) self.files_to_pars = files_to_pars @@ -1071,23 +1284,37 @@ def __init__(self, files_to_pars, target_directory, target_directory_changed, pa self.number_of_selected_reports = 0 # target file name - self.target_file_name_prefix = self.parsing_settings['target_file_name_prefix'] + self.target_file_name_prefix = self.parsing_settings["target_file_name_prefix"] # reports - self.report_scan_enabled = self.parsing_settings['report_scan_enabled'] - self.report_host_enabled = self.parsing_settings['report_host_enabled'] - self.report_vulnerabilities_enabled = self.parsing_settings['report_vulnerabilities_enabled'] - self.report_noncompliance_enabled = self.parsing_settings['report_noncompliance_enabled'] + self.report_scan_enabled = self.parsing_settings["report_scan_enabled"] + self.report_host_enabled = self.parsing_settings["report_host_enabled"] + self.report_vulnerabilities_enabled = self.parsing_settings[ + "report_vulnerabilities_enabled" + ] + self.report_noncompliance_enabled = self.parsing_settings[ + "report_noncompliance_enabled" + ] # reports settings: - self.report_scan_debug_data_enabled = self.parsing_settings['report_scan_debug_data_enabled'] - self.report_host_debug_data_enabled = self.parsing_settings['report_host_debug_data_enabled'] + self.report_scan_debug_data_enabled = self.parsing_settings[ + "report_scan_debug_data_enabled" + ] + self.report_host_debug_data_enabled = self.parsing_settings[ + "report_host_debug_data_enabled" + ] self.report_vulnerabilities_debug_data_enabled = self.parsing_settings[ - 'report_vulnerabilities_debug_data_enabled'] - self.report_vulnerabilities_none_filter_out = self.parsing_settings['report_vulnerabilities_none_filter_out'] - self.report_vulnerabilities_none_skip = self.parsing_settings['report_vulnerabilities_none_skip'] + "report_vulnerabilities_debug_data_enabled" + ] + self.report_vulnerabilities_none_filter_out = self.parsing_settings[ + "report_vulnerabilities_none_filter_out" + ] + self.report_vulnerabilities_none_skip = self.parsing_settings[ + "report_vulnerabilities_none_skip" + ] self.report_noncompliance_debug_data_enabled = self.parsing_settings[ - 'report_noncompliance_debug_data_enabled'] + "report_noncompliance_debug_data_enabled" + ] # print(self.report_vulnerabilities_none_filter_out) # print(self.report_vulnerabilities_none_skip) @@ -1097,114 +1324,144 @@ def run(self): target_file_name_prefix = self.target_file_name_prefix - suffix = self.parsing_settings['suffix'] + suffix = self.parsing_settings["suffix"] - target_file_name = target_file_name_prefix + suffix + '.xlsx' - final_path_to_save = target_directory + '/' + target_file_name - workbook = xlsxwriter.Workbook(final_path_to_save, {'constant_memory': True}) + target_file_name = target_file_name_prefix + suffix + ".xlsx" + final_path_to_save = target_directory + "/" + target_file_name + workbook = xlsxwriter.Workbook(final_path_to_save, {"constant_memory": True}) - if self.report_scan_enabled and not \ - self.report_host_enabled and not \ - self.report_vulnerabilities_enabled and not \ - self.report_noncompliance_enabled: + if ( + self.report_scan_enabled + and not self.report_host_enabled + and not self.report_vulnerabilities_enabled + and not self.report_noncompliance_enabled + ): self.number_of_selected_reports = 1 self.create_worksheet_for_scans(workbook, files_to_pars) - elif not self.report_scan_enabled and \ - self.report_host_enabled and not \ - self.report_vulnerabilities_enabled and not \ - self.report_noncompliance_enabled: + elif ( + not self.report_scan_enabled + and self.report_host_enabled + and not self.report_vulnerabilities_enabled + and not self.report_noncompliance_enabled + ): self.number_of_selected_reports = 1 self.create_worksheet_for_hosts(workbook, files_to_pars) - elif not self.report_scan_enabled and not \ - self.report_host_enabled and \ - self.report_vulnerabilities_enabled and not \ - self.report_noncompliance_enabled: + elif ( + not self.report_scan_enabled + and not self.report_host_enabled + and self.report_vulnerabilities_enabled + and not self.report_noncompliance_enabled + ): self.number_of_selected_reports = 1 self.create_worksheet_for_vulnerabilities(workbook, files_to_pars) - elif not self.report_scan_enabled and not \ - self.report_host_enabled and not \ - self.report_vulnerabilities_enabled and \ - self.report_noncompliance_enabled: + elif ( + not self.report_scan_enabled + and not self.report_host_enabled + and not self.report_vulnerabilities_enabled + and self.report_noncompliance_enabled + ): self.number_of_selected_reports = 1 self.create_worksheet_for_noncompliance(workbook, files_to_pars) - elif self.report_scan_enabled and \ - self.report_host_enabled and not \ - self.report_vulnerabilities_enabled and not \ - self.report_noncompliance_enabled: + elif ( + self.report_scan_enabled + and self.report_host_enabled + and not self.report_vulnerabilities_enabled + and not self.report_noncompliance_enabled + ): self.number_of_selected_reports = 2 self.create_worksheet_for_scans(workbook, files_to_pars) self.create_worksheet_for_hosts(workbook, files_to_pars) - elif self.report_scan_enabled and not \ - self.report_host_enabled and \ - self.report_vulnerabilities_enabled and not \ - self.report_noncompliance_enabled: + elif ( + self.report_scan_enabled + and not self.report_host_enabled + and self.report_vulnerabilities_enabled + and not self.report_noncompliance_enabled + ): self.number_of_selected_reports = 2 self.create_worksheet_for_scans(workbook, files_to_pars) self.create_worksheet_for_vulnerabilities(workbook, files_to_pars) - elif self.report_scan_enabled and not \ - self.report_host_enabled and not \ - self.report_vulnerabilities_enabled and \ - self.report_noncompliance_enabled: + elif ( + self.report_scan_enabled + and not self.report_host_enabled + and not self.report_vulnerabilities_enabled + and self.report_noncompliance_enabled + ): self.number_of_selected_reports = 2 self.create_worksheet_for_scans(workbook, files_to_pars) self.create_worksheet_for_noncompliance(workbook, files_to_pars) - elif not self.report_scan_enabled and \ - self.report_host_enabled and \ - self.report_vulnerabilities_enabled and not \ - self.report_noncompliance_enabled: + elif ( + not self.report_scan_enabled + and self.report_host_enabled + and self.report_vulnerabilities_enabled + and not self.report_noncompliance_enabled + ): self.number_of_selected_reports = 2 self.create_worksheet_for_hosts(workbook, files_to_pars) self.create_worksheet_for_vulnerabilities(workbook, files_to_pars) - elif not self.report_scan_enabled and \ - self.report_host_enabled and not \ - self.report_vulnerabilities_enabled and \ - self.report_noncompliance_enabled: + elif ( + not self.report_scan_enabled + and self.report_host_enabled + and not self.report_vulnerabilities_enabled + and self.report_noncompliance_enabled + ): self.number_of_selected_reports = 2 self.create_worksheet_for_hosts(workbook, files_to_pars) self.create_worksheet_for_noncompliance(workbook, files_to_pars) - elif not self.report_scan_enabled and not \ - self.report_host_enabled and \ - self.report_vulnerabilities_enabled and \ - self.report_noncompliance_enabled: + elif ( + not self.report_scan_enabled + and not self.report_host_enabled + and self.report_vulnerabilities_enabled + and self.report_noncompliance_enabled + ): self.number_of_selected_reports = 2 self.create_worksheet_for_vulnerabilities(workbook, files_to_pars) self.create_worksheet_for_noncompliance(workbook, files_to_pars) - elif self.report_scan_enabled and \ - self.report_host_enabled and \ - self.report_vulnerabilities_enabled and not \ - self.report_noncompliance_enabled: + elif ( + self.report_scan_enabled + and self.report_host_enabled + and self.report_vulnerabilities_enabled + and not self.report_noncompliance_enabled + ): self.number_of_selected_reports = 3 self.create_worksheet_for_scans(workbook, files_to_pars) self.create_worksheet_for_hosts(workbook, files_to_pars) self.create_worksheet_for_vulnerabilities(workbook, files_to_pars) - elif self.report_scan_enabled and \ - self.report_host_enabled and not \ - self.report_vulnerabilities_enabled and \ - self.report_noncompliance_enabled: + elif ( + self.report_scan_enabled + and self.report_host_enabled + and not self.report_vulnerabilities_enabled + and self.report_noncompliance_enabled + ): self.number_of_selected_reports = 3 self.create_worksheet_for_scans(workbook, files_to_pars) self.create_worksheet_for_hosts(workbook, files_to_pars) self.create_worksheet_for_noncompliance(workbook, files_to_pars) - elif self.report_scan_enabled and not \ - self.report_host_enabled and \ - self.report_vulnerabilities_enabled and \ - self.report_noncompliance_enabled: + elif ( + self.report_scan_enabled + and not self.report_host_enabled + and self.report_vulnerabilities_enabled + and self.report_noncompliance_enabled + ): self.number_of_selected_reports = 3 self.create_worksheet_for_scans(workbook, files_to_pars) self.create_worksheet_for_vulnerabilities(workbook, files_to_pars) self.create_worksheet_for_noncompliance(workbook, files_to_pars) - elif not self.report_scan_enabled and \ - self.report_host_enabled and \ - self.report_vulnerabilities_enabled and \ - self.report_noncompliance_enabled: + elif ( + not self.report_scan_enabled + and self.report_host_enabled + and self.report_vulnerabilities_enabled + and self.report_noncompliance_enabled + ): self.number_of_selected_reports = 3 self.create_worksheet_for_hosts(workbook, files_to_pars) self.create_worksheet_for_vulnerabilities(workbook, files_to_pars) self.create_worksheet_for_noncompliance(workbook, files_to_pars) - elif self.report_scan_enabled and \ - self.report_host_enabled and \ - self.report_vulnerabilities_enabled and \ - self.report_noncompliance_enabled: + elif ( + self.report_scan_enabled + and self.report_host_enabled + and self.report_vulnerabilities_enabled + and self.report_noncompliance_enabled + ): self.number_of_selected_reports = 4 self.create_worksheet_for_scans(workbook, files_to_pars) self.create_worksheet_for_hosts(workbook, files_to_pars) @@ -1212,7 +1469,9 @@ def run(self): self.create_worksheet_for_noncompliance(workbook, files_to_pars) else: - info = 'You did not choose any report type to generate. Select at least one.' + info = ( + "You did not choose any report type to generate. Select at least one." + ) print(info) workbook.close() @@ -1223,73 +1482,78 @@ def create_worksheet_for_scans(self, workbook, list_of_source_files): :param workbook: workbook where spreadsheet are created :param list_of_source_files: list of selected files """ - report_name = 'scans' + report_name = "scans" self.report_counter += 1 - info_report = 'Report: ' + str(self.report_counter) + '/' + str(self.number_of_selected_reports) + info_report = ( + "Report: " + + str(self.report_counter) + + "/" + + str(self.number_of_selected_reports) + ) worksheet = workbook.add_worksheet(report_name) - cell_format_bold = workbook.add_format({'bold': True}) + cell_format_bold = workbook.add_format({"bold": True}) worksheet.set_row(0, None, cell_format_bold) # print('>>>>>>>>>>>>>>>> ', self.report_scan_debug_data_enabled) if not self.report_scan_debug_data_enabled: headers = [ - 'Target hosts', - 'Target hosts (without duplicates)', - 'Scanned hosts', - 'Scanned hosts with credentialed checks', - 'Unreachable hosts', - 'Scan started', - 'Scan ended', - 'Elapsed time per scan', - 'Login used', - 'DB SID', - 'DB port', - 'ALL plugins', - 'Critical plugins', - 'High plugins', - 'Medium plugins', - 'Low plugins', - 'None plugins', - 'ALL compliance', - 'Passed compliance', - 'Failed compliance', - 'Warning compliance' + "Target hosts", + "Target hosts (without duplicates)", + "Scanned hosts", + "Scanned hosts with credentialed checks", + "Unreachable hosts", + "Scan started", + "Scan ended", + "Elapsed time per scan", + "Login used", + "DB SID", + "DB port", + "ALL plugins", + "Critical plugins", + "High plugins", + "Medium plugins", + "Low plugins", + "None plugins", + "ALL compliance", + "Passed compliance", + "Failed compliance", + "Warning compliance", ] else: headers = [ - 'Nessus scan name', - 'Nessus file name', - 'nessus file size', - 'Target hosts', - 'Target hosts (without duplicates)', - 'Scanned hosts', - 'Scanned hosts with credentialed checks', - 'Unreachable hosts', - 'Scan started', - 'Scan ended', - 'Elapsed time per scan', - 'Policy name', - 'Login used', - 'DB SID', - 'DB port', - 'Reverse lookup', - 'Max hosts', - 'Max checks', - 'Network timeout', - 'Used plugins', - 'ALL plugins', - 'Critical plugins', - 'High plugins', - 'Medium plugins', - 'Low plugins', - 'None plugins', - 'ALL compliance', - 'Passed compliance', - 'Failed compliance', - 'Warning compliance' + "Nessus scan name", + "Nessus file name", + "nessus file size", + "Target hosts", + "Target hosts (without duplicates)", + "Scanned hosts", + "Scanned hosts with credentialed checks", + "Unreachable hosts", + "Scan started", + "Scan ended", + "Elapsed time per scan", + "Policy name", + "Login used", + "DB SID", + "DB port", + "Reverse lookup", + "Max hosts", + "Max checks", + "Network timeout", + "Used plugins", + "ALL plugins", + "Critical plugins", + "High plugins", + "Medium plugins", + "Low plugins", + "None plugins", + "ALL compliance", + "Passed compliance", + "Failed compliance", + "Warning compliance", ] number_of_columns = len(headers) @@ -1300,10 +1564,13 @@ def create_worksheet_for_scans(self, workbook, list_of_source_files): debug_columns_list = [0, 1, 2, 11, 15, 16, 17, 18, 19] for column_index, header in enumerate(headers): - if self.report_scan_debug_data_enabled and column_index in debug_columns_list: + if ( + self.report_scan_debug_data_enabled + and column_index in debug_columns_list + ): cell_format_bold_blue = workbook.add_format() cell_format_bold_blue.set_bold() - cell_format_bold_blue.set_font_color('blue') + cell_format_bold_blue.set_font_color("blue") worksheet.write(0, column_index, header, cell_format_bold_blue) else: worksheet.write(0, column_index, header) @@ -1316,17 +1583,17 @@ def create_worksheet_for_scans(self, workbook, list_of_source_files): number_of_rows = 0 row_index = 0 for scan_file in list_of_source_files: - source_file_type = '' + source_file_type = "" files_to_pars = [] - if fnmatch.fnmatch(scan_file, '*.nessus'): + if fnmatch.fnmatch(scan_file, "*.nessus"): files_to_pars.append(scan_file) - source_file_type = 'nessus' - elif fnmatch.fnmatch(scan_file, '*.zip'): - source_file_type = 'zip' + source_file_type = "nessus" + elif fnmatch.fnmatch(scan_file, "*.zip"): + source_file_type = "zip" zip_source = zipfile.ZipFile(scan_file) zip_files_list = zip_source.namelist() for zip_file in zip_files_list: - if fnmatch.fnmatch(zip_file, '*.nessus'): + if fnmatch.fnmatch(zip_file, "*.nessus"): files_to_pars.append(zip_file) print(files_to_pars) @@ -1336,40 +1603,73 @@ def create_worksheet_for_scans(self, workbook, list_of_source_files): number_of_rows += 1 nessus_scan_file_number += 1 - info_file = ', File: ' + str(row_index) + '/' + str(len(list_of_source_files)) + info_file = ( + ", File: " + str(row_index) + "/" + str(len(list_of_source_files)) + ) try: - file_to_pars_full_name = '' + file_to_pars_full_name = "" source_file_size = 0 - if source_file_type == 'nessus': + if source_file_type == "nessus": root = nfr.file.nessus_scan_file_root_element(file_to_pars) scan_file_source = nfr.scan.scan_file_source(root) - file_to_pars_full_name = nfr.file.nessus_scan_file_name_with_path(file_to_pars) - source_file_size = nfa.utilities.size_of_file_human(file_to_pars_full_name) - elif source_file_type == 'zip': - source_file_size = nfa.utilities.size_of_file_inside_zip_human(zip_source, file_to_pars) - file_to_pars_full_name = f'{nfr.file.nessus_scan_file_name_with_path(scan_file)} ' \ - f'[{zip_source.getinfo(file_to_pars).filename}]' + file_to_pars_full_name = ( + nfr.file.nessus_scan_file_name_with_path(file_to_pars) + ) + source_file_size = nfa.utilities.size_of_file_human( + file_to_pars_full_name + ) + elif source_file_type == "zip": + source_file_size = nfa.utilities.size_of_file_inside_zip_human( + zip_source, file_to_pars + ) + file_to_pars_full_name = ( + f"{nfr.file.nessus_scan_file_name_with_path(scan_file)} " + f"[{zip_source.getinfo(file_to_pars).filename}]" + ) file_to_pars = zip_source.open(file_to_pars) root = nfr.file.nessus_scan_file_root_element(file_to_pars) scan_file_source = nfr.scan.scan_file_source(root) start_time = time.time() - self.log_emitter('start', file_to_pars_full_name) + self.log_emitter("start", file_to_pars_full_name) self.file_analysis_started.emit(1) - self.log_emitter('info ', file_to_pars_full_name, f'[scan_file_source={scan_file_source}]') - self.log_emitter('info ', file_to_pars_full_name, f'[source_file_size={source_file_size}]') - self.log_emitter('info ', file_to_pars_full_name, f'[report_type={report_name}]') - self.log_emitter('info ', file_to_pars_full_name, - f'[number_of_target_hosts={nfr.scan.number_of_target_hosts(root)}]') - self.log_emitter('info ', file_to_pars_full_name, - f'[number_of_scanned_hosts={nfr.scan.number_of_scanned_hosts(root)}]') - self.log_emitter('info ', file_to_pars_full_name, - f'[number_of_scanned_hosts_with_credentials=' - f'{nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(root)}]') - - date_format = workbook.add_format({'num_format': 'yyyy-mm-dd hh:mm:ss'}) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[scan_file_source={scan_file_source}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[source_file_size={source_file_size}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[report_type={report_name}]" + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[number_of_target_hosts={nfr.scan.number_of_target_hosts(root)}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[number_of_scanned_hosts={nfr.scan.number_of_scanned_hosts(root)}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[number_of_scanned_hosts_with_credentials=" + f"{nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(root)}]", + ) + + date_format = workbook.add_format( + {"num_format": "yyyy-mm-dd hh:mm:ss"} + ) login_used = None number_of_plugins = 0 @@ -1385,12 +1685,19 @@ def create_worksheet_for_scans(self, workbook, list_of_source_files): policy_max_hosts = nfr.scan.policy_max_hosts(root) policy_max_checks = nfr.scan.policy_max_checks(root) - policy_checks_read_timeout = nfr.scan.policy_checks_read_timeout(root) + policy_checks_read_timeout = nfr.scan.policy_checks_read_timeout( + root + ) report_host_counter = 0 number_of_report_hosts = nfr.scan.number_of_scanned_hosts(root) - info_host = ', Host: ' + str(report_host_counter) + '/' + str(number_of_report_hosts) + info_host = ( + ", Host: " + + str(report_host_counter) + + "/" + + str(number_of_report_hosts) + ) info_final = info_report + info_file + info_host self.print_status_bar_info.emit(info_final) @@ -1398,44 +1705,104 @@ def create_worksheet_for_scans(self, workbook, list_of_source_files): report_host_counter += 1 self.progress.emit(report_host_counter, number_of_report_hosts) - info_host = ', Host: ' + str(report_host_counter) + '/' + str(number_of_report_hosts) + info_host = ( + ", Host: " + + str(report_host_counter) + + "/" + + str(number_of_report_hosts) + ) info_final = info_report + info_file + info_host self.print_status_bar_info.emit(info_final) login_used = nfr.host.login_used(report_host) number_of_plugins += nfr.host.number_of_plugins(report_host) - number_of_plugins_critical += nfr.host.number_of_plugins_per_risk_factor(report_host, - 'Critical') - number_of_plugins_high += nfr.host.number_of_plugins_per_risk_factor(report_host, 'High') - number_of_plugins_medium += nfr.host.number_of_plugins_per_risk_factor(report_host, 'Medium') - number_of_plugins_low += nfr.host.number_of_plugins_per_risk_factor(report_host, 'Low') - number_of_plugins_none += nfr.host.number_of_plugins_per_risk_factor(report_host, 'None') - number_of_compliance_plugins += nfr.host.number_of_compliance_plugins(report_host) - number_of_compliance_plugins_passed += \ - nfr.host.number_of_compliance_plugins_per_result(report_host, 'PASSED') - number_of_compliance_plugins_failed += \ - nfr.host.number_of_compliance_plugins_per_result(report_host, 'FAILED') - number_of_compliance_plugins_warning += nfr.host.number_of_compliance_plugins_per_result( - report_host, 'WARNING') + number_of_plugins_critical += ( + nfr.host.number_of_plugins_per_risk_factor( + report_host, "Critical" + ) + ) + number_of_plugins_high += ( + nfr.host.number_of_plugins_per_risk_factor( + report_host, "High" + ) + ) + number_of_plugins_medium += ( + nfr.host.number_of_plugins_per_risk_factor( + report_host, "Medium" + ) + ) + number_of_plugins_low += ( + nfr.host.number_of_plugins_per_risk_factor( + report_host, "Low" + ) + ) + number_of_plugins_none += ( + nfr.host.number_of_plugins_per_risk_factor( + report_host, "None" + ) + ) + number_of_compliance_plugins += ( + nfr.host.number_of_compliance_plugins(report_host) + ) + number_of_compliance_plugins_passed += ( + nfr.host.number_of_compliance_plugins_per_result( + report_host, "PASSED" + ) + ) + number_of_compliance_plugins_failed += ( + nfr.host.number_of_compliance_plugins_per_result( + report_host, "FAILED" + ) + ) + number_of_compliance_plugins_warning += ( + nfr.host.number_of_compliance_plugins_per_result( + report_host, "WARNING" + ) + ) if not self.report_scan_debug_data_enabled: - worksheet.write(row_index, 0, nfr.scan.number_of_target_hosts(root)) - worksheet.write(row_index, 1, nfr.scan.number_of_target_hosts_without_duplicates(root)) - worksheet.write(row_index, 2, nfr.scan.number_of_scanned_hosts(root)) - worksheet.write(row_index, 3, nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes( - root)) - worksheet.write(row_index, 4, nfr.scan.number_of_not_scanned_hosts(root)) + worksheet.write( + row_index, + 0, + nfr.scan.number_of_target_hosts(root) + ) + worksheet.write( + row_index, + 1, + nfr.scan.number_of_target_hosts_without_duplicates(root), + ) + worksheet.write( + row_index, + 2, + nfr.scan.number_of_scanned_hosts(root) + ) + worksheet.write( + row_index, + 3, + nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes( + root + ), + ) + worksheet.write( + row_index, + 4, + nfr.scan.number_of_not_scanned_hosts(root) + ) scan_start_time = nfr.scan.scan_time_start(root) if scan_start_time is not None: - worksheet.write_datetime(row_index, 5, scan_start_time, date_format) + worksheet.write_datetime( + row_index, 5, scan_start_time, date_format + ) else: worksheet.write_blank(row_index, 5, None) scan_end_time = nfr.scan.scan_time_end(root) if scan_end_time is not None: - worksheet.write_datetime(row_index, 6, scan_end_time, date_format) + worksheet.write_datetime( + row_index, 6, scan_end_time, date_format + ) else: worksheet.write_blank(row_index, 6, None) @@ -1450,29 +1817,54 @@ def create_worksheet_for_scans(self, workbook, list_of_source_files): worksheet.write(row_index, 15, number_of_plugins_low) worksheet.write(row_index, 16, number_of_plugins_none) worksheet.write(row_index, 17, number_of_compliance_plugins) - worksheet.write(row_index, 18, number_of_compliance_plugins_passed) - worksheet.write(row_index, 19, number_of_compliance_plugins_failed) - worksheet.write(row_index, 20, number_of_compliance_plugins_warning) + worksheet.write( + row_index, 18, number_of_compliance_plugins_passed + ) + worksheet.write( + row_index, 19, number_of_compliance_plugins_failed + ) + worksheet.write( + row_index, 20, number_of_compliance_plugins_warning + ) else: worksheet.write(row_index, 0, nfr.scan.report_name(root)) worksheet.write(row_index, 1, file_to_pars_full_name) worksheet.write(row_index, 2, source_file_size) - worksheet.write(row_index, 3, nfr.scan.number_of_target_hosts(root)) - worksheet.write(row_index, 4, nfr.scan.number_of_target_hosts_without_duplicates(root)) - worksheet.write(row_index, 5, nfr.scan.number_of_scanned_hosts(root)) - worksheet.write(row_index, 6, nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes( - root)) - worksheet.write(row_index, 7, nfr.scan.number_of_not_scanned_hosts(root)) + worksheet.write( + row_index, 3, nfr.scan.number_of_target_hosts(root) + ) + worksheet.write( + row_index, + 4, + nfr.scan.number_of_target_hosts_without_duplicates(root), + ) + worksheet.write( + row_index, 5, nfr.scan.number_of_scanned_hosts(root) + ) + worksheet.write( + row_index, + 6, + nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes( + root + ), + ) + worksheet.write( + row_index, 7, nfr.scan.number_of_not_scanned_hosts(root) + ) scan_start_time = nfr.scan.scan_time_start(root) if scan_start_time is not None: - worksheet.write_datetime(row_index, 8, scan_start_time, date_format) + worksheet.write_datetime( + row_index, 8, scan_start_time, date_format + ) else: worksheet.write_blank(row_index, 8, None) scan_end_time = nfr.scan.scan_time_end(root) if scan_end_time is not None: - worksheet.write_datetime(row_index, 9, scan_end_time, date_format) + worksheet.write_datetime( + row_index, 9, scan_end_time, date_format + ) else: worksheet.write_blank(row_index, 9, None) @@ -1493,24 +1885,41 @@ def create_worksheet_for_scans(self, workbook, list_of_source_files): worksheet.write(row_index, 24, number_of_plugins_low) worksheet.write(row_index, 25, number_of_plugins_none) worksheet.write(row_index, 26, number_of_compliance_plugins) - worksheet.write(row_index, 27, number_of_compliance_plugins_passed) - worksheet.write(row_index, 28, number_of_compliance_plugins_failed) - worksheet.write(row_index, 29, number_of_compliance_plugins_warning) + worksheet.write( + row_index, 27, number_of_compliance_plugins_passed + ) + worksheet.write( + row_index, 28, number_of_compliance_plugins_failed + ) + worksheet.write( + row_index, 29, number_of_compliance_plugins_warning + ) end_time = time.time() elapsed_time = end_time - start_time - elapsed_time_parsed = time.strftime('%H:%M:%S', time.gmtime(elapsed_time)) + elapsed_time_parsed = time.strftime( + "%H:%M:%S", time.gmtime(elapsed_time) + ) - self.log_emitter('info ', file_to_pars_full_name, f'[elapsed_time={elapsed_time_parsed}]') - self.log_emitter('end ', file_to_pars_full_name) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[elapsed_time={elapsed_time_parsed}]", + ) + self.log_emitter("end ", file_to_pars_full_name) except Exception as e: number_of_files_with_errors += 1 traceback.print_exc() - self.log_emitter('info', - "ERROR Parsing [" + str(nessus_scan_file_number) + "/" + - str(len(list_of_source_files)) + "] nessus files") - self.log_emitter('info', e) + self.log_emitter( + "info", + "ERROR Parsing [" + + str(nessus_scan_file_number) + + "/" + + str(len(list_of_source_files)) + + "] nessus files", + ) + self.log_emitter("info", e) if number_of_rows > 0: worksheet.autofilter(0, 0, number_of_rows, number_of_columns - 1) @@ -1521,145 +1930,184 @@ def create_worksheet_for_hosts(self, workbook, list_of_source_files): :param workbook: workbook where spreadsheet are created :param list_of_source_files: list of selected files """ - report_name = 'hosts' + report_name = "hosts" self.report_counter += 1 - info_report = 'Report: ' + str(self.report_counter) + '/' + str(self.number_of_selected_reports) + info_report = ( + "Report: " + + str(self.report_counter) + + "/" + + str(self.number_of_selected_reports) + ) worksheet = workbook.add_worksheet(report_name) - cell_format_bold = workbook.add_format({'bold': True}) + cell_format_bold = workbook.add_format({"bold": True}) worksheet.set_row(0, None, cell_format_bold) # print('>>>>>>>>>>>>>>>> ', self.report_host_debug_data_enabled) if not self.report_host_debug_data_enabled: headers = [ - # 'Nessus scanner IP', - # 'Nessus scan name', - # 'Nessus file name', - 'Target', - 'Hostname', - 'FQDN', - # 'NetBIOS Computer name', - # 'NetBIOS Domain name', - 'IP', - 'Scanned', - 'Credentialed checks', - 'Scan started', - 'Scan ended', - 'Elapsed time per host', - 'Elapsed time per scan', - # 'Policy name', - 'Login used', - 'DB SID', - 'DB port', - # 'Max hosts', - # 'Max checks', - # 'Network timeout', - 'Operating System', - 'ALL plugins', - 'Critical plugins', - 'High plugins', - 'Medium plugins', - 'Low plugins', - 'None plugins', - 'ALL compliance', - 'Passed compliance', - 'Failed compliance', - 'Warning compliance' - # '10180: Ping to remote host', - # '10287: Traceroute Information', - # '11936: OS Identification', - # '45590: Common Platform Enumeration (CPE)', - # '54615: Device Type', - # '21745: Authentication Failure - Local Checks Not Run', - # '12634: Authenticated Check : OS Name and Installed Package Enumeration', - # '110385: Authentication Success Insufficient Access', - # '102094: SSH Commands Require Privilege Escalation', - # '10394: Microsoft Windows SMB Log In Possible', - # '24786: Nessus Windows Scan Not Performed with Admin Privileges', - # '24269: Windows Management Instrumentation (WMI) Available', - # '11011: Microsoft Windows SMB Service Detection', - # '10400: Microsoft Windows SMB Registry Remotely Accessible', - # '26917: Microsoft Windows SMB Registry : Nessus Cannot Access the Windows Registry', - # '42897: SMB Registry : Start the Registry Service during the scan (WMI)', - # '20811: Microsoft Windows Installed Software Enumeration (credentialed check)', - # '91825: Oracle DB Login Possible', - # '91827: Microsoft SQL Server Login Possible', - # '47864: Cisco IOS Version', - # '67217: Cisco IOS XE Version' + # "Nessus scanner IP", + # "Nessus scan name", + # "Nessus file name", + "Target", + "Hostname", + "FQDN", + # "NetBIOS Computer name", + # "NetBIOS Domain name", + "IP", + "Scanned", + "Credentialed checks", + "Scan started", + "Scan ended", + "Elapsed time per host", + "Elapsed time per scan", + # "Policy name", + "Login used", + "DB SID", + "DB port", + # "Max hosts", + # "Max checks", + # "Network timeout", + "Operating System", + "ALL plugins", + "Critical plugins", + "High plugins", + "Medium plugins", + "Low plugins", + "None plugins", + "ALL compliance", + "Passed compliance", + "Failed compliance", + "Warning compliance", + # "10180: Ping to remote host", + # "10287: Traceroute Information", + # "11936: OS Identification", + # "45590: Common Platform Enumeration (CPE)", + # "54615: Device Type", + # "21745: Authentication Failure - Local Checks Not Run", + # "12634: Authenticated Check : OS Name and Installed Package Enumeration", + # "110385: Authentication Success Insufficient Access", + # "102094: SSH Commands Require Privilege Escalation", + # "10394: Microsoft Windows SMB Log In Possible", + # "24786: Nessus Windows Scan Not Performed with Admin Privileges", + # "24269: Windows Management Instrumentation (WMI) Available", + # "11011: Microsoft Windows SMB Service Detection", + # "10400: Microsoft Windows SMB Registry Remotely Accessible", + # "26917: Microsoft Windows SMB Registry : Nessus Cannot Access the Windows Registry", + # "42897: SMB Registry : Start the Registry Service during the scan (WMI)", + # "20811: Microsoft Windows Installed Software Enumeration (credentialed check)", + # "91825: Oracle DB Login Possible", + # "91827: Microsoft SQL Server Login Possible", + # "47864: Cisco IOS Version", + # "67217: Cisco IOS XE Version" ] else: headers = [ - 'Nessus scanner IP', - 'Nessus scan name', - 'Nessus file name', - 'Target', - 'Hostname', - 'FQDN', - 'NetBIOS Computer name', - 'NetBIOS Domain name', - 'IP', - 'Scanned', - 'Credentialed checks', - 'Scan started', - 'Scan ended', - 'Elapsed time per host', - 'Elapsed time per scan', - 'Policy name', - 'Login used', - 'DB SID', - 'DB port', - 'Reverse lookup', - 'Max hosts', - 'Max checks', - 'Network timeout', - 'Operating System', - 'ALL plugins', - 'Critical plugins', - 'High plugins', - 'Medium plugins', - 'Low plugins', - 'None plugins', - 'ALL compliance', - 'Passed compliance', - 'Failed compliance', - 'Warning compliance', - '10180: Ping to remote host', - '10287: Traceroute Information', - '11936: OS Identification', - '45590: Common Platform Enumeration (CPE)', - '54615: Device Type', - '21745: Authentication Failure - Local Checks Not Run', - '12634: Authenticated Check : OS Name and Installed Package Enumeration', - '110385: Authentication Success Insufficient Access', - '102094: SSH Commands Require Privilege Escalation', - '10394: Microsoft Windows SMB Log In Possible', - '24786: Nessus Windows Scan Not Performed with Admin Privileges', - '24269: Windows Management Instrumentation (WMI) Available', - '11011: Microsoft Windows SMB Service Detection', - '10400: Microsoft Windows SMB Registry Remotely Accessible', - '26917: Microsoft Windows SMB Registry : Nessus Cannot Access the Windows Registry', - '42897: SMB Registry : Start the Registry Service during the scan (WMI)', - '20811: Microsoft Windows Installed Software Enumeration (credentialed check)', - '91825: Oracle DB Login Possible', - '91827: Microsoft SQL Server Login Possible', - '47864: Cisco IOS Version', - '67217: Cisco IOS XE Version' + "Nessus scanner IP", + "Nessus scan name", + "Nessus file name", + "Target", + "Hostname", + "FQDN", + "NetBIOS Computer name", + "NetBIOS Domain name", + "IP", + "Scanned", + "Credentialed checks", + "Scan started", + "Scan ended", + "Elapsed time per host", + "Elapsed time per scan", + "Policy name", + "Login used", + "DB SID", + "DB port", + "Reverse lookup", + "Max hosts", + "Max checks", + "Network timeout", + "Operating System", + "ALL plugins", + "Critical plugins", + "High plugins", + "Medium plugins", + "Low plugins", + "None plugins", + "ALL compliance", + "Passed compliance", + "Failed compliance", + "Warning compliance", + "10180: Ping to remote host", + "10287: Traceroute Information", + "11936: OS Identification", + "45590: Common Platform Enumeration (CPE)", + "54615: Device Type", + "21745: Authentication Failure - Local Checks Not Run", + "12634: Authenticated Check : OS Name and Installed Package Enumeration", + "110385: Authentication Success Insufficient Access", + "102094: SSH Commands Require Privilege Escalation", + "10394: Microsoft Windows SMB Log In Possible", + "24786: Nessus Windows Scan Not Performed with Admin Privileges", + "24269: Windows Management Instrumentation (WMI) Available", + "11011: Microsoft Windows SMB Service Detection", + "10400: Microsoft Windows SMB Registry Remotely Accessible", + "26917: Microsoft Windows SMB Registry : Nessus Cannot Access the Windows Registry", + "42897: SMB Registry : Start the Registry Service during the scan (WMI)", + "20811: Microsoft Windows Installed Software Enumeration (credentialed check)", + "91825: Oracle DB Login Possible", + "91827: Microsoft SQL Server Login Possible", + "47864: Cisco IOS Version", + "67217: Cisco IOS XE Version", ] number_of_columns = len(headers) # print('Number of columns: ' + str(number_of_columns)) worksheet.set_column(0, number_of_columns - 1, 18) - debug_columns_list = [0, 1, 2, 6, 7, 13, 17, 18, 19, 20, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52] + debug_columns_list = [ + 0, + 1, + 2, + 6, + 7, + 13, + 17, + 18, + 19, + 20, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + ] for column_index, header in enumerate(headers): - if self.report_host_debug_data_enabled and column_index in debug_columns_list: + if ( + self.report_host_debug_data_enabled + and column_index in debug_columns_list + ): cell_format_bold_blue = workbook.add_format() cell_format_bold_blue.set_bold() - cell_format_bold_blue.set_font_color('blue') + cell_format_bold_blue.set_font_color("blue") worksheet.write(0, column_index, header, cell_format_bold_blue) else: worksheet.write(0, column_index, header) @@ -1673,61 +2121,101 @@ def create_worksheet_for_hosts(self, workbook, list_of_source_files): row_index = 0 row_index_per_file = 0 for scan_file in list_of_source_files: - source_file_type = '' + source_file_type = "" files_to_pars = [] - if fnmatch.fnmatch(scan_file, '*.nessus'): + if fnmatch.fnmatch(scan_file, "*.nessus"): files_to_pars.append(scan_file) - source_file_type = 'nessus' - elif fnmatch.fnmatch(scan_file, '*.zip'): - source_file_type = 'zip' + source_file_type = "nessus" + elif fnmatch.fnmatch(scan_file, "*.zip"): + source_file_type = "zip" zip_source = zipfile.ZipFile(scan_file) zip_files_list = zip_source.namelist() for zip_file in zip_files_list: - if fnmatch.fnmatch(zip_file, '*.nessus'): + if fnmatch.fnmatch(zip_file, "*.nessus"): files_to_pars.append(zip_file) for file_to_pars in files_to_pars: row_index_per_file += 1 nessus_scan_file_number += 1 - info_file = ', File: ' + str(row_index_per_file) + '/' + str(len(list_of_source_files)) + info_file = ( + ", File: " + + str(row_index_per_file) + + "/" + + str(len(list_of_source_files)) + ) try: - file_to_pars_full_name = '' + file_to_pars_full_name = "" source_file_size = 0 - if source_file_type == 'nessus': + if source_file_type == "nessus": root = nfr.file.nessus_scan_file_root_element(file_to_pars) scan_file_source = nfr.scan.scan_file_source(root) - file_to_pars_full_name = nfr.file.nessus_scan_file_name_with_path(file_to_pars) - source_file_size = nfa.utilities.size_of_file_human(file_to_pars_full_name) - elif source_file_type == 'zip': - source_file_size = nfa.utilities.size_of_file_inside_zip_human(zip_source, file_to_pars) - file_to_pars_full_name = f'{nfr.file.nessus_scan_file_name_with_path(scan_file)} ' \ - f'[{zip_source.getinfo(file_to_pars).filename}]' + file_to_pars_full_name = ( + nfr.file.nessus_scan_file_name_with_path(file_to_pars) + ) + source_file_size = nfa.utilities.size_of_file_human( + file_to_pars_full_name + ) + elif source_file_type == "zip": + source_file_size = nfa.utilities.size_of_file_inside_zip_human( + zip_source, file_to_pars + ) + file_to_pars_full_name = ( + f"{nfr.file.nessus_scan_file_name_with_path(scan_file)} " + f"[{zip_source.getinfo(file_to_pars).filename}]" + ) file_to_pars = zip_source.open(file_to_pars) root = nfr.file.nessus_scan_file_root_element(file_to_pars) scan_file_source = nfr.scan.scan_file_source(root) start_time = time.time() - self.log_emitter('start', file_to_pars_full_name) + self.log_emitter("start", file_to_pars_full_name) self.file_analysis_started.emit(1) - self.log_emitter('info ', file_to_pars_full_name, f'[scan_file_source={scan_file_source}]') - self.log_emitter('info ', file_to_pars_full_name, f'[source_file_size={source_file_size}]') - self.log_emitter('info ', file_to_pars_full_name, f'[report_type={report_name}]') - self.log_emitter('info ', file_to_pars_full_name, - f'[number_of_target_hosts={nfr.scan.number_of_target_hosts(root)}]') - self.log_emitter('info ', file_to_pars_full_name, - f'[number_of_scanned_hosts={nfr.scan.number_of_scanned_hosts(root)}]') - self.log_emitter('info ', file_to_pars_full_name, - f'[number_of_scanned_hosts_with_credentials=' - f'{nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(root)}]') - - date_format = workbook.add_format({'num_format': 'yyyy-mm-dd hh:mm:ss'}) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[scan_file_source={scan_file_source}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[source_file_size={source_file_size}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[report_type={report_name}]" + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[number_of_target_hosts={nfr.scan.number_of_target_hosts(root)}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[number_of_scanned_hosts={nfr.scan.number_of_scanned_hosts(root)}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[number_of_scanned_hosts_with_credentials=" + f"{nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(root)}]", + ) + + date_format = workbook.add_format( + {"num_format": "yyyy-mm-dd hh:mm:ss"} + ) policy_max_hosts = nfr.scan.policy_max_hosts(root) policy_max_checks = nfr.scan.policy_max_checks(root) - policy_checks_read_timeout = nfr.scan.policy_checks_read_timeout(root) + policy_checks_read_timeout = nfr.scan.policy_checks_read_timeout( + root + ) scan_report_name = nfr.scan.report_name(root) - nessus_scan_file_name_with_path = nfr.file.nessus_scan_file_name_with_path(file_to_pars_full_name) + nessus_scan_file_name_with_path = ( + nfr.file.nessus_scan_file_name_with_path(file_to_pars_full_name) + ) scan_time_elapsed = nfr.scan.scan_time_elapsed(root) scan_policy_name = nfr.scan.policy_name(root) scan_policy_db_sid = nfr.scan.policy_db_sid(root) @@ -1738,7 +2226,12 @@ def create_worksheet_for_hosts(self, workbook, list_of_source_files): report_host_counter = 0 number_of_report_hosts = nfr.scan.number_of_scanned_hosts(root) - info_host = ', Host: ' + str(report_host_counter) + '/' + str(number_of_report_hosts) + info_host = ( + ", Host: " + + str(report_host_counter) + + "/" + + str(number_of_report_hosts) + ) info_final = info_report + info_file + info_host self.print_status_bar_info.emit(info_final) @@ -1750,7 +2243,12 @@ def create_worksheet_for_hosts(self, workbook, list_of_source_files): report_host_counter += 1 self.progress.emit(report_host_counter, number_of_report_hosts) - info_host = ', Host: ' + str(report_host_counter) + '/' + str(number_of_report_hosts) + info_host = ( + ", Host: " + + str(report_host_counter) + + "/" + + str(number_of_report_hosts) + ) info_final = info_report + info_file + info_host self.print_status_bar_info.emit(info_final) @@ -1759,44 +2257,107 @@ def create_worksheet_for_hosts(self, workbook, list_of_source_files): login_used = scan_policy_login_specified number_of_plugins = nfr.host.number_of_plugins(report_host) - number_of_plugins_critical = nfr.host.number_of_plugins_per_risk_factor(report_host, 'Critical') - number_of_plugins_high = nfr.host.number_of_plugins_per_risk_factor(report_host, 'High') - number_of_plugins_medium = nfr.host.number_of_plugins_per_risk_factor(report_host, 'Medium') - number_of_plugins_low = nfr.host.number_of_plugins_per_risk_factor(report_host, 'Low') - number_of_plugins_none = nfr.host.number_of_plugins_per_risk_factor(report_host, 'None') - number_of_compliance_plugins = nfr.host.number_of_compliance_plugins(report_host) - number_of_compliance_plugins_passed = \ - nfr.host.number_of_compliance_plugins_per_result(report_host, 'PASSED') - number_of_compliance_plugins_failed = \ - nfr.host.number_of_compliance_plugins_per_result(report_host, 'FAILED') - number_of_compliance_plugins_warning = \ - nfr.host.number_of_compliance_plugins_per_result(report_host, 'WARNING') + number_of_plugins_critical = ( + nfr.host.number_of_plugins_per_risk_factor( + report_host, "Critical" + ) + ) + number_of_plugins_high = ( + nfr.host.number_of_plugins_per_risk_factor( + report_host, "High" + ) + ) + number_of_plugins_medium = ( + nfr.host.number_of_plugins_per_risk_factor( + report_host, "Medium" + ) + ) + number_of_plugins_low = ( + nfr.host.number_of_plugins_per_risk_factor( + report_host, "Low" + ) + ) + number_of_plugins_none = ( + nfr.host.number_of_plugins_per_risk_factor( + report_host, "None" + ) + ) + number_of_compliance_plugins = ( + nfr.host.number_of_compliance_plugins(report_host) + ) + number_of_compliance_plugins_passed = ( + nfr.host.number_of_compliance_plugins_per_result( + report_host, "PASSED" + ) + ) + number_of_compliance_plugins_failed = ( + nfr.host.number_of_compliance_plugins_per_result( + report_host, "FAILED" + ) + ) + number_of_compliance_plugins_warning = ( + nfr.host.number_of_compliance_plugins_per_result( + report_host, "WARNING" + ) + ) if not self.report_host_debug_data_enabled: - worksheet.write(row_index, 0, nfr.host.report_host_name(report_host)) - worksheet.write(row_index, 1, nfr.host.resolved_hostname(report_host)) - worksheet.write(row_index, 2, nfr.host.resolved_fqdn(report_host)) - worksheet.write(row_index, 3, nfr.host.resolved_ip(report_host)) - worksheet.write(row_index, 4, 'yes') - worksheet.write(row_index, 5, nfr.host.credentialed_checks(root, report_host)) + worksheet.write( + row_index, + 0, + nfr.host.report_host_name(report_host) + ) + worksheet.write( + row_index, + 1, + nfr.host.resolved_hostname(report_host) + ) + worksheet.write( + row_index, + 2, + nfr.host.resolved_fqdn(report_host) + ) + worksheet.write( + row_index, + 3, + nfr.host.resolved_ip(report_host) + ) + worksheet.write( + row_index, + 4, + "yes" + ) + worksheet.write( + row_index, + 5, + nfr.host.credentialed_checks(root, report_host), + ) host_start_time = nfr.host.host_time_start(report_host) if host_start_time is not None: - worksheet.write_datetime(row_index, 6, host_start_time, date_format) + worksheet.write_datetime( + row_index, 6, host_start_time, date_format + ) else: worksheet.write_blank(row_index, 6, None) host_end_time = nfr.host.host_time_end(report_host) if host_end_time is not None: - worksheet.write_datetime(row_index, 7, host_end_time, date_format) + worksheet.write_datetime( + row_index, 7, host_end_time, date_format + ) else: worksheet.write_blank(row_index, 7, None) - worksheet.write(row_index, 8, nfr.host.host_time_elapsed(report_host)) + worksheet.write( + row_index, 8, nfr.host.host_time_elapsed(report_host) + ) worksheet.write(row_index, 9, scan_time_elapsed) worksheet.write(row_index, 10, login_used) worksheet.write(row_index, 11, scan_policy_db_sid) worksheet.write(row_index, 12, scan_policy_db_port) - worksheet.write(row_index, 13, nfr.host.detected_os(report_host)) + worksheet.write( + row_index, 13, nfr.host.detected_os(report_host) + ) worksheet.write(row_index, 14, number_of_plugins) worksheet.write(row_index, 15, number_of_plugins_critical) worksheet.write(row_index, 16, number_of_plugins_high) @@ -1804,38 +2365,76 @@ def create_worksheet_for_hosts(self, workbook, list_of_source_files): worksheet.write(row_index, 18, number_of_plugins_low) worksheet.write(row_index, 19, number_of_plugins_none) worksheet.write(row_index, 20, number_of_compliance_plugins) - worksheet.write(row_index, 21, number_of_compliance_plugins_passed) - worksheet.write(row_index, 22, number_of_compliance_plugins_failed) - worksheet.write(row_index, 23, number_of_compliance_plugins_warning) + worksheet.write( + row_index, 21, number_of_compliance_plugins_passed + ) + worksheet.write( + row_index, 22, number_of_compliance_plugins_failed + ) + worksheet.write( + row_index, 23, number_of_compliance_plugins_warning + ) else: - worksheet.write(row_index, 0, nfr.host.scanner_ip(root, report_host)) + worksheet.write( + row_index, 0, nfr.host.scanner_ip(root, report_host) + ) worksheet.write(row_index, 1, scan_report_name) - worksheet.write(row_index, 2, nessus_scan_file_name_with_path) - worksheet.write(row_index, 3, nfr.host.report_host_name(report_host)) - worksheet.write(row_index, 4, nfr.host.resolved_hostname(report_host)) - worksheet.write(row_index, 5, nfr.host.resolved_fqdn(report_host)) - worksheet.write(row_index, 6, - nfr.host.netbios_network_name(root, report_host)['netbios_computer_name']) - worksheet.write(row_index, 7, - nfr.host.netbios_network_name(root, report_host)['netbios_domain_name']) - worksheet.write(row_index, 8, nfr.host.resolved_ip(report_host)) - worksheet.write(row_index, 9, 'yes') - worksheet.write(row_index, 10, nfr.host.credentialed_checks(root, report_host)) + worksheet.write( + row_index, 2, nessus_scan_file_name_with_path + ) + worksheet.write( + row_index, 3, nfr.host.report_host_name(report_host) + ) + worksheet.write( + row_index, 4, nfr.host.resolved_hostname(report_host) + ) + worksheet.write( + row_index, 5, nfr.host.resolved_fqdn(report_host) + ) + worksheet.write( + row_index, + 6, + nfr.host.netbios_network_name(root, report_host)[ + "netbios_computer_name" + ], + ) + worksheet.write( + row_index, + 7, + nfr.host.netbios_network_name(root, report_host)[ + "netbios_domain_name" + ], + ) + worksheet.write( + row_index, 8, nfr.host.resolved_ip(report_host) + ) + worksheet.write(row_index, 9, "yes") + worksheet.write( + row_index, + 10, + nfr.host.credentialed_checks(root, report_host), + ) host_start_time = nfr.host.host_time_start(report_host) if host_start_time is not None: - worksheet.write_datetime(row_index, 11, host_start_time, date_format) + worksheet.write_datetime( + row_index, 11, host_start_time, date_format + ) else: worksheet.write_blank(row_index, 11, None) host_end_time = nfr.host.host_time_end(report_host) if host_end_time is not None: - worksheet.write_datetime(row_index, 12, host_end_time, date_format) + worksheet.write_datetime( + row_index, 12, host_end_time, date_format + ) else: worksheet.write_blank(row_index, 12, None) - worksheet.write(row_index, 13, nfr.host.host_time_elapsed(report_host)) + worksheet.write( + row_index, 13, nfr.host.host_time_elapsed(report_host) + ) worksheet.write(row_index, 14, scan_time_elapsed) worksheet.write(row_index, 15, scan_policy_name) worksheet.write(row_index, 16, login_used) @@ -1845,7 +2444,9 @@ def create_worksheet_for_hosts(self, workbook, list_of_source_files): worksheet.write(row_index, 20, policy_max_hosts) worksheet.write(row_index, 21, policy_max_checks) worksheet.write(row_index, 22, policy_checks_read_timeout) - worksheet.write(row_index, 23, nfr.host.detected_os(report_host)) + worksheet.write( + row_index, 23, nfr.host.detected_os(report_host) + ) worksheet.write(row_index, 24, number_of_plugins) worksheet.write(row_index, 25, number_of_plugins_critical) worksheet.write(row_index, 26, number_of_plugins_high) @@ -1853,90 +2454,193 @@ def create_worksheet_for_hosts(self, workbook, list_of_source_files): worksheet.write(row_index, 28, number_of_plugins_low) worksheet.write(row_index, 29, number_of_plugins_none) worksheet.write(row_index, 30, number_of_compliance_plugins) - worksheet.write(row_index, 31, number_of_compliance_plugins_passed) - worksheet.write(row_index, 32, number_of_compliance_plugins_failed) - worksheet.write(row_index, 33, number_of_compliance_plugins_warning) + worksheet.write( + row_index, 31, number_of_compliance_plugins_passed + ) + worksheet.write( + row_index, 32, number_of_compliance_plugins_failed + ) + worksheet.write( + row_index, 33, number_of_compliance_plugins_warning + ) # '10180: Ping to remote host' - worksheet.write(row_index, 34, nfr.plugin.plugin_output(root, report_host, '10180')) + worksheet.write( + row_index, + 34, + nfr.plugin.plugin_output(root, report_host, "10180"), + ) # '10287: Traceroute Information' - worksheet.write(row_index, 35, nfr.plugin.plugin_output(root, report_host, '10287')) + worksheet.write( + row_index, + 35, + nfr.plugin.plugin_output(root, report_host, "10287"), + ) # '11936: OS Identification' - worksheet.write(row_index, 36, nfr.plugin.plugin_output(root, report_host, '11936')) + worksheet.write( + row_index, + 36, + nfr.plugin.plugin_output(root, report_host, "11936"), + ) # '45590: Common Platform Enumeration (CPE)' - worksheet.write(row_index, 37, nfr.plugin.plugin_output(root, report_host, '45590')) + worksheet.write( + row_index, + 37, + nfr.plugin.plugin_output(root, report_host, "45590"), + ) # '54615: Device Type' - worksheet.write(row_index, 38, nfr.plugin.plugin_output(root, report_host, '54615')) + worksheet.write( + row_index, + 38, + nfr.plugin.plugin_output(root, report_host, "54615"), + ) # '21745: Authentication Failure - Local Checks Not Run' - worksheet.write(row_index, 39, nfr.plugin.plugin_output(root, report_host, '21745')) + worksheet.write( + row_index, + 39, + nfr.plugin.plugin_output(root, report_host, "21745"), + ) # '12634: Authenticated Check : OS Name and Installed Package Enumeration' - worksheet.write(row_index, 40, nfr.plugin.plugin_output(root, report_host, '12634')) + worksheet.write( + row_index, + 40, + nfr.plugin.plugin_output(root, report_host, "12634"), + ) # '110385: Authentication Success Insufficient Access' - worksheet.write(row_index, 41, nfr.plugin.plugin_output(root, report_host, '110385')) + worksheet.write( + row_index, + 41, + nfr.plugin.plugin_output(root, report_host, "110385"), + ) # '102094: SSH Commands Require Privilege Escalation' - worksheet.write(row_index, 42, nfr.plugin.plugin_output(root, report_host, '102094')) + worksheet.write( + row_index, + 42, + nfr.plugin.plugin_output(root, report_host, "102094"), + ) # '10394: Microsoft Windows SMB Log In Possible' - worksheet.write(row_index, 43, nfr.plugin.plugin_output(root, report_host, '10394')) + worksheet.write( + row_index, + 43, + nfr.plugin.plugin_output(root, report_host, "10394"), + ) # '24786: Nessus Windows Scan Not Performed with Admin Privileges' - worksheet.write(row_index, 44, nfr.plugin.plugin_output(root, report_host, '24786')) + worksheet.write( + row_index, + 44, + nfr.plugin.plugin_output(root, report_host, "24786"), + ) # '24269: Windows Management Instrumentation (WMI) Available' - worksheet.write(row_index, 45, nfr.plugin.plugin_output(root, report_host, '24269')) + worksheet.write( + row_index, + 45, + nfr.plugin.plugin_output(root, report_host, "24269"), + ) # '11011: Microsoft Windows SMB Service Detection' - worksheet.write(row_index, 46, nfr.plugin.plugin_outputs(root, report_host, '11011')) + worksheet.write( + row_index, + 46, + nfr.plugin.plugin_outputs(root, report_host, "11011"), + ) # '10400: Microsoft Windows SMB Registry Remotely Accessible' - worksheet.write(row_index, 47, nfr.plugin.plugin_output(root, report_host, '10400')) + worksheet.write( + row_index, + 47, + nfr.plugin.plugin_output(root, report_host, "10400"), + ) # '26917: Microsoft Windows SMB Registry : Nessus Cannot Access the Windows Registry' - worksheet.write(row_index, 48, nfr.plugin.plugin_output(root, report_host, '26917')) + worksheet.write( + row_index, + 48, + nfr.plugin.plugin_output(root, report_host, "26917"), + ) # '42897: SMB Registry : Start the Registry Service during the scan (WMI)' - worksheet.write(row_index, 49, nfr.plugin.plugin_output(root, report_host, '42897')) + worksheet.write( + row_index, + 49, + nfr.plugin.plugin_output(root, report_host, "42897"), + ) # '20811: Microsoft Windows Installed Software Enumeration (credentialed check)' - worksheet.write(row_index, 50, nfr.plugin.plugin_output(root, report_host, '20811')) + worksheet.write( + row_index, + 50, + nfr.plugin.plugin_output(root, report_host, "20811"), + ) # '91825: Oracle DB Login Possible' - worksheet.write(row_index, 51, nfr.plugin.plugin_output(root, report_host, '91825')) + worksheet.write( + row_index, + 51, + nfr.plugin.plugin_output(root, report_host, "91825"), + ) # '91827: Microsoft SQL Server Login Possible' - worksheet.write(row_index, 52, nfr.plugin.plugin_output(root, report_host, '91827')) + worksheet.write( + row_index, + 52, + nfr.plugin.plugin_output(root, report_host, "91827"), + ) # '47864: Cisco IOS Version' - worksheet.write(row_index, 53, nfr.plugin.plugin_output(root, report_host, '47864')) + worksheet.write( + row_index, + 53, + nfr.plugin.plugin_output(root, report_host, "47864"), + ) # '67217: Cisco IOS XE Version' - worksheet.write(row_index, 54, nfr.plugin.plugin_output(root, report_host, '67217')) - - number_of_not_scanned_hosts = nfr.scan.number_of_not_scanned_hosts(root) + worksheet.write( + row_index, + 54, + nfr.plugin.plugin_output(root, report_host, "67217"), + ) + + number_of_not_scanned_hosts = nfr.scan.number_of_not_scanned_hosts( + root + ) if number_of_not_scanned_hosts is not None: if number_of_not_scanned_hosts > 0: not_scanned_hosts = nfr.scan.list_of_not_scanned_hosts(root) not_scanned_host_counter = 0 file_source = nfr.scan.scan_file_source(root) - if file_source == 'Tenable.sc': - targets_sc = nfr.scan.list_of_target_hosts_sc_fqdn_ip(root) + if file_source == "Tenable.sc": + targets_sc = nfr.scan.list_of_target_hosts_sc_fqdn_ip( + root + ) for target in not_scanned_hosts: row_index += 1 not_scanned_host_counter += 1 - self.progress.emit(not_scanned_host_counter, number_of_not_scanned_hosts) - - target_fqdn = '' - target_hostname = '' - target_ip = '' + self.progress.emit( + not_scanned_host_counter, + number_of_not_scanned_hosts, + ) + + target_fqdn = "" + target_hostname = "" + target_ip = "" for target_sc in targets_sc: - if target == target_sc['target_fqdn']: - target_fqdn = target_sc['target_fqdn'] - target_hostname = target_fqdn.split('.')[0] - target_ip = target_sc['target_ip'] + if target == target_sc["target_fqdn"]: + target_fqdn = target_sc["target_fqdn"] + target_hostname = target_fqdn.split(".")[0] + target_ip = target_sc["target_ip"] if not self.report_host_debug_data_enabled: worksheet.write(row_index, 0, target) worksheet.write(row_index, 1, target_hostname) worksheet.write(row_index, 2, target_fqdn) worksheet.write(row_index, 3, target_ip) - worksheet.write(row_index, 4, 'no') - worksheet.write(row_index, 5, 'no') + worksheet.write(row_index, 4, "no") + worksheet.write(row_index, 5, "no") worksheet.write_blank(row_index, 6, None) worksheet.write_blank(row_index, 7, None) - worksheet.write(row_index, 8, '0:00:00') + worksheet.write(row_index, 8, "0:00:00") worksheet.write(row_index, 9, scan_time_elapsed) - worksheet.write(row_index, 10, scan_policy_login_specified) - worksheet.write(row_index, 11, scan_policy_db_sid) - worksheet.write(row_index, 12, scan_policy_db_port) + worksheet.write( + row_index, 10, scan_policy_login_specified + ) + worksheet.write( + row_index, 11, scan_policy_db_sid + ) + worksheet.write( + row_index, 12, scan_policy_db_port + ) worksheet.write_blank(row_index, 13, None) worksheet.write_blank(row_index, 14, None) worksheet.write_blank(row_index, 15, None) @@ -1950,26 +2654,42 @@ def create_worksheet_for_hosts(self, workbook, list_of_source_files): else: # worksheet.write_blank(row_index, 0, None) worksheet.write(row_index, 1, scan_report_name) - worksheet.write(row_index, 2, nessus_scan_file_name_with_path) + worksheet.write( + row_index, + 2, + nessus_scan_file_name_with_path, + ) worksheet.write(row_index, 3, target) worksheet.write(row_index, 4, target_hostname) worksheet.write(row_index, 5, target_fqdn) # worksheet.write_blank(row_index, 6, None) # worksheet.write_blank(row_index, 7, None) worksheet.write(row_index, 8, target_ip) - worksheet.write(row_index, 9, 'no') - worksheet.write(row_index, 10, 'no') + worksheet.write(row_index, 9, "no") + worksheet.write(row_index, 10, "no") # worksheet.write_blank(row_index, 11, None) # worksheet.write_blank(row_index, 12, None) - worksheet.write(row_index, 13, '0:00:00') - worksheet.write(row_index, 14, scan_time_elapsed) + worksheet.write(row_index, 13, "0:00:00") + worksheet.write( + row_index, 14, scan_time_elapsed + ) worksheet.write(row_index, 15, scan_policy_name) - worksheet.write(row_index, 16, scan_policy_login_specified) - worksheet.write(row_index, 17, scan_policy_db_sid) - worksheet.write(row_index, 18, scan_policy_db_port) - worksheet.write(row_index, 19, scan_reverse_lookup) + worksheet.write( + row_index, 16, scan_policy_login_specified + ) + worksheet.write( + row_index, 17, scan_policy_db_sid + ) + worksheet.write( + row_index, 18, scan_policy_db_port + ) + worksheet.write( + row_index, 19, scan_reverse_lookup + ) worksheet.write(row_index, 20, policy_max_hosts) - worksheet.write(row_index, 21, policy_max_checks) + worksheet.write( + row_index, 21, policy_max_checks + ) # worksheet.write_blank(row_index, 22, None) # worksheet.write_blank(row_index, 23, None) # worksheet.write_blank(row_index, 24, None) @@ -2029,22 +2749,31 @@ def create_worksheet_for_hosts(self, workbook, list_of_source_files): for target in not_scanned_hosts: row_index += 1 not_scanned_host_counter += 1 - self.progress.emit(not_scanned_host_counter, number_of_not_scanned_hosts) + self.progress.emit( + not_scanned_host_counter, + number_of_not_scanned_hosts, + ) if not self.report_host_debug_data_enabled: worksheet.write(row_index, 0, target) worksheet.write_blank(row_index, 1, None) worksheet.write_blank(row_index, 2, None) worksheet.write_blank(row_index, 3, None) - worksheet.write(row_index, 4, 'no') - worksheet.write(row_index, 5, 'no') + worksheet.write(row_index, 4, "no") + worksheet.write(row_index, 5, "no") worksheet.write_blank(row_index, 6, None) worksheet.write_blank(row_index, 7, None) - worksheet.write(row_index, 8, '0:00:00') + worksheet.write(row_index, 8, "0:00:00") worksheet.write(row_index, 9, scan_time_elapsed) - worksheet.write(row_index, 10, scan_policy_login_specified) - worksheet.write(row_index, 11, scan_policy_db_sid) - worksheet.write(row_index, 12, scan_policy_db_port) + worksheet.write( + row_index, 10, scan_policy_login_specified + ) + worksheet.write( + row_index, 11, scan_policy_db_sid + ) + worksheet.write( + row_index, 12, scan_policy_db_port + ) worksheet.write_blank(row_index, 13, None) worksheet.write_blank(row_index, 14, None) worksheet.write_blank(row_index, 15, None) @@ -2058,26 +2787,42 @@ def create_worksheet_for_hosts(self, workbook, list_of_source_files): else: # worksheet.write_blank(row_index, 0, None) worksheet.write(row_index, 1, scan_report_name) - worksheet.write(row_index, 2, nessus_scan_file_name_with_path) + worksheet.write( + row_index, + 2, + nessus_scan_file_name_with_path, + ) worksheet.write(row_index, 3, target) # worksheet.write_blank(row_index, 4, None) # worksheet.write_blank(row_index, 5, None) # worksheet.write_blank(row_index, 6, None) # worksheet.write_blank(row_index, 7, None) # worksheet.write_blank(row_index, 8, None) - worksheet.write(row_index, 9, 'no') - worksheet.write(row_index, 10, 'no') + worksheet.write(row_index, 9, "no") + worksheet.write(row_index, 10, "no") # worksheet.write_blank(row_index, 11, None) # worksheet.write_blank(row_index, 12, None) - worksheet.write(row_index, 13, '0:00:00') - worksheet.write(row_index, 14, scan_time_elapsed) + worksheet.write(row_index, 13, "0:00:00") + worksheet.write( + row_index, 14, scan_time_elapsed + ) worksheet.write(row_index, 15, scan_policy_name) - worksheet.write(row_index, 16, scan_policy_login_specified) - worksheet.write(row_index, 17, scan_policy_db_sid) - worksheet.write(row_index, 18, scan_policy_db_port) - worksheet.write(row_index, 19, scan_reverse_lookup) + worksheet.write( + row_index, 16, scan_policy_login_specified + ) + worksheet.write( + row_index, 17, scan_policy_db_sid + ) + worksheet.write( + row_index, 18, scan_policy_db_port + ) + worksheet.write( + row_index, 19, scan_reverse_lookup + ) worksheet.write(row_index, 20, policy_max_hosts) - worksheet.write(row_index, 21, policy_max_checks) + worksheet.write( + row_index, 21, policy_max_checks + ) # worksheet.write_blank(row_index, 22, None) # worksheet.write_blank(row_index, 23, None) # worksheet.write_blank(row_index, 24, None) @@ -2135,18 +2880,29 @@ def create_worksheet_for_hosts(self, workbook, list_of_source_files): end_time = time.time() elapsed_time = end_time - start_time - elapsed_time_parsed = time.strftime('%H:%M:%S', time.gmtime(elapsed_time)) + elapsed_time_parsed = time.strftime( + "%H:%M:%S", time.gmtime(elapsed_time) + ) - self.log_emitter('info ', file_to_pars_full_name, f'[elapsed_time={elapsed_time_parsed}]') - self.log_emitter('end ', file_to_pars_full_name) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[elapsed_time={elapsed_time_parsed}]", + ) + self.log_emitter("end ", file_to_pars_full_name) except Exception as e: number_of_files_with_errors += 1 traceback.print_exc() - self.log_emitter('info', - "ERROR Parsing [" + str(nessus_scan_file_number) + "/" + - str(len(list_of_source_files)) + "] nessus files") - self.log_emitter('info', e) + self.log_emitter( + "info", + "ERROR Parsing [" + + str(nessus_scan_file_number) + + "/" + + str(len(list_of_source_files)) + + "] nessus files", + ) + self.log_emitter("info", e) if number_of_rows > 0: worksheet.autofilter(0, 0, number_of_rows, number_of_columns - 1) @@ -2157,84 +2913,89 @@ def create_worksheet_for_vulnerabilities(self, workbook, list_of_source_files): :param workbook: workbook where spreadsheet are created :param list_of_source_files: list of selected files """ - report_name = 'vulnerabilities' + report_name = "vulnerabilities" self.report_counter += 1 - info_report = 'Report: ' + str(self.report_counter) + '/' + str(self.number_of_selected_reports) + info_report = ( + "Report: " + + str(self.report_counter) + + "/" + + str(self.number_of_selected_reports) + ) worksheet = workbook.add_worksheet(report_name) - cell_format_bold = workbook.add_format({'bold': True}) + cell_format_bold = workbook.add_format({"bold": True}) worksheet.set_row(0, None, cell_format_bold) # print('>>>>>>>>>>>>>>>> ', self.report_vulnerabilities_debug_data_enabled) if not self.report_vulnerabilities_debug_data_enabled: headers = [ - # 'Nessus scanner IP', - # 'Nessus scan name', - # 'Nessus file name', - 'Target', - 'Hostname', - 'FQDN', - 'IP', - 'Scanned', - 'Credentialed checks', - # 'Policy name', - 'Protocol', - 'Service Name', - 'Port', - 'Plugin ID', - 'Plugin name', - 'Plugin type', - 'Risk Factor', - 'Plugin family', - # 'Plugin file name', - 'Plugin version', - 'Plugin publication date', - 'Plugin modification date', - 'Plugin description', - 'Solution', - 'Plugin output', - 'CVE counter', - 'CVE number', - 'Exploit available', - 'Exploit code maturity', - 'Exploit framework metasploit', - 'Exploitability ease', + # "Nessus scanner IP", + # "Nessus scan name", + # "Nessus file name", + "Target", + "Hostname", + "FQDN", + "IP", + "Scanned", + "Credentialed checks", + # "Policy name", + "Protocol", + "Service Name", + "Port", + "Plugin ID", + "Plugin name", + "Plugin type", + "Risk Factor", + "Plugin family", + # "Plugin file name", + "Plugin version", + "Plugin publication date", + "Plugin modification date", + "Plugin description", + "Solution", + "Plugin output", + "CVE counter", + "CVE number", + "Exploit available", + "Exploit code maturity", + "Exploit framework metasploit", + "Exploitability ease", ] else: headers = [ - 'Nessus scanner IP', - 'Nessus scan name', - 'Nessus file name', - 'Target', - 'Hostname', - 'FQDN', - 'IP', - 'Scanned', - 'Credentialed checks', - 'Policy name', - 'Protocol', - 'Service Name', - 'Port', - 'Plugin ID', - 'Plugin name', - 'Plugin type', - 'Risk Factor', - 'Plugin family', - 'Plugin file name', - 'Plugin version', - 'Plugin publication date', - 'Plugin modification date', - 'Plugin description', - 'Solution', - 'Plugin output', - 'CVE counter', - 'CVE number', - 'Exploit available', - 'Exploit code maturity', - 'Exploit framework metasploit', - 'Exploitability ease', + "Nessus scanner IP", + "Nessus scan name", + "Nessus file name", + "Target", + "Hostname", + "FQDN", + "IP", + "Scanned", + "Credentialed checks", + "Policy name", + "Protocol", + "Service Name", + "Port", + "Plugin ID", + "Plugin name", + "Plugin type", + "Risk Factor", + "Plugin family", + "Plugin file name", + "Plugin version", + "Plugin publication date", + "Plugin modification date", + "Plugin description", + "Solution", + "Plugin output", + "CVE counter", + "CVE number", + "Exploit available", + "Exploit code maturity", + "Exploit framework metasploit", + "Exploitability ease", ] number_of_columns = len(headers) @@ -2245,10 +3006,13 @@ def create_worksheet_for_vulnerabilities(self, workbook, list_of_source_files): debug_columns_list = [0, 1, 2, 9, 18] for column_index, header in enumerate(headers): - if self.report_vulnerabilities_debug_data_enabled and column_index in debug_columns_list: + if ( + self.report_vulnerabilities_debug_data_enabled + and column_index in debug_columns_list + ): cell_format_bold_blue = workbook.add_format() cell_format_bold_blue.set_bold() - cell_format_bold_blue.set_font_color('blue') + cell_format_bold_blue.set_font_color("blue") worksheet.write(0, column_index, header, cell_format_bold_blue) else: worksheet.write(0, column_index, header) @@ -2262,65 +3026,106 @@ def create_worksheet_for_vulnerabilities(self, workbook, list_of_source_files): row_index = 0 row_index_per_file = 0 for scan_file in list_of_source_files: - source_file_type = '' + source_file_type = "" files_to_pars = [] - if fnmatch.fnmatch(scan_file, '*.nessus'): + if fnmatch.fnmatch(scan_file, "*.nessus"): files_to_pars.append(scan_file) - source_file_type = 'nessus' - elif fnmatch.fnmatch(scan_file, '*.zip'): - source_file_type = 'zip' + source_file_type = "nessus" + elif fnmatch.fnmatch(scan_file, "*.zip"): + source_file_type = "zip" zip_source = zipfile.ZipFile(scan_file) zip_files_list = zip_source.namelist() for zip_file in zip_files_list: - if fnmatch.fnmatch(zip_file, '*.nessus'): + if fnmatch.fnmatch(zip_file, "*.nessus"): files_to_pars.append(zip_file) for file_to_pars in files_to_pars: row_index_per_file += 1 nessus_scan_file_number = nessus_scan_file_number + 1 - info_file = ', File: ' + str(row_index_per_file) + '/' + str(len(list_of_source_files)) + info_file = ( + ", File: " + + str(row_index_per_file) + + "/" + + str(len(list_of_source_files)) + ) try: - file_to_pars_full_name = '' + file_to_pars_full_name = "" source_file_size = 0 - if source_file_type == 'nessus': + if source_file_type == "nessus": root = nfr.file.nessus_scan_file_root_element(file_to_pars) scan_file_source = nfr.scan.scan_file_source(root) - file_to_pars_full_name = nfr.file.nessus_scan_file_name_with_path(file_to_pars) - source_file_size = nfa.utilities.size_of_file_human(file_to_pars_full_name) - elif source_file_type == 'zip': - source_file_size = nfa.utilities.size_of_file_inside_zip_human(zip_source, file_to_pars) - file_to_pars_full_name = f'{nfr.file.nessus_scan_file_name_with_path(scan_file)} [' \ - f'{zip_source.getinfo(file_to_pars).filename}]' + file_to_pars_full_name = ( + nfr.file.nessus_scan_file_name_with_path(file_to_pars) + ) + source_file_size = nfa.utilities.size_of_file_human( + file_to_pars_full_name + ) + elif source_file_type == "zip": + source_file_size = nfa.utilities.size_of_file_inside_zip_human( + zip_source, file_to_pars + ) + file_to_pars_full_name = ( + f"{nfr.file.nessus_scan_file_name_with_path(scan_file)} [" + f"{zip_source.getinfo(file_to_pars).filename}]" + ) file_to_pars = zip_source.open(file_to_pars) root = nfr.file.nessus_scan_file_root_element(file_to_pars) scan_file_source = nfr.scan.scan_file_source(root) start_time = time.time() - self.log_emitter('start', file_to_pars_full_name) + self.log_emitter("start", file_to_pars_full_name) self.file_analysis_started.emit(1) - self.log_emitter('info ', file_to_pars_full_name, f'[scan_file_source={scan_file_source}]') - self.log_emitter('info ', file_to_pars_full_name, f'[source_file_size={source_file_size}]') - self.log_emitter('info ', file_to_pars_full_name, f'[report_type={report_name}]') - self.log_emitter('info ', file_to_pars_full_name, - f'[number_of_target_hosts={nfr.scan.number_of_target_hosts(root)}]') - self.log_emitter('info ', file_to_pars_full_name, - f'[number_of_scanned_hosts={nfr.scan.number_of_scanned_hosts(root)}]') - self.log_emitter('info ', file_to_pars_full_name, - f'[number_of_scanned_hosts_with_credentials=' - f'{nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(root)}]') - - date_format = workbook.add_format({'num_format': 'yyyy-mm-dd'}) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[scan_file_source={scan_file_source}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[source_file_size={source_file_size}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[report_type={report_name}]" + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[number_of_target_hosts={nfr.scan.number_of_target_hosts(root)}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[number_of_scanned_hosts={nfr.scan.number_of_scanned_hosts(root)}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[number_of_scanned_hosts_with_credentials=" + f"{nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(root)}]", + ) + + date_format = workbook.add_format({"num_format": "yyyy-mm-dd"}) scan_report_name = nfr.scan.report_name(root) - nessus_scan_file_name_with_path = nfr.file.nessus_scan_file_name_with_path(file_to_pars_full_name) + nessus_scan_file_name_with_path = ( + nfr.file.nessus_scan_file_name_with_path(file_to_pars_full_name) + ) scan_policy_name = nfr.scan.policy_name(root) report_host_counter = 0 number_of_report_hosts = nfr.scan.number_of_scanned_hosts(root) - info_host = ', Host: ' + str(report_host_counter) + '/' + str(number_of_report_hosts) + info_host = ( + ", Host: " + + str(report_host_counter) + + "/" + + str(number_of_report_hosts) + ) info_final = info_report + info_file + info_host self.print_status_bar_info.emit(info_final) @@ -2329,7 +3134,12 @@ def create_worksheet_for_vulnerabilities(self, workbook, list_of_source_files): report_host_counter += 1 self.progress.emit(report_host_counter, number_of_report_hosts) - info_host = ', Host: ' + str(report_host_counter) + '/' + str(number_of_report_hosts) + info_host = ( + ", Host: " + + str(report_host_counter) + + "/" + + str(number_of_report_hosts) + ) info_final = info_report + info_file + info_host self.print_status_bar_info.emit(info_final) @@ -2340,41 +3150,83 @@ def create_worksheet_for_vulnerabilities(self, workbook, list_of_source_files): host_resolved_hostname = nfr.host.resolved_hostname(report_host) host_resolved_fqdn = nfr.host.resolved_fqdn(report_host) host_resolved_ip = nfr.host.resolved_ip(report_host) - host_credentialed_checks = nfr.host.credentialed_checks(root, report_host) + host_credentialed_checks = nfr.host.credentialed_checks( + root, report_host + ) for report_item in report_items_per_host: number_of_rows += 1 row_index += 1 - protocol = nfr.plugin.report_item_value(report_item, 'protocol') - service_name = nfr.plugin.report_item_value(report_item, 'svc_name') - port = int(nfr.plugin.report_item_value(report_item, 'port')) - plugin_id = int(nfr.plugin.report_item_value(report_item, 'pluginID')) - plugin_name = nfr.plugin.report_item_value(report_item, 'pluginName') - plugin_type = nfr.plugin.report_item_value(report_item, 'plugin_type') - risk_factor = nfr.plugin.report_item_value(report_item, 'risk_factor') - plugin_family = nfr.plugin.report_item_value(report_item, 'pluginFamily') - plugin_file_name = nfr.plugin.report_item_value(report_item, 'fname') - plugin_version = nfr.plugin.report_item_value(report_item, 'script_version') - plugin_publication_date = nfr.plugin.report_item_value(report_item, - 'plugin_publication_date') - plugin_modification_date = nfr.plugin.report_item_value(report_item, - 'plugin_modification_date') - plugin_description = nfr.plugin.report_item_value(report_item, 'description') - solution = nfr.plugin.report_item_value(report_item, 'solution') - plugin_output = nfr.plugin.report_item_value(report_item, 'plugin_output') - plugin_cves = nfr.plugin.report_item_values(report_item, 'cve') - exploit_available = nfr.plugin.report_item_value(report_item, 'exploit_available') - exploit_code_maturity = nfr.plugin.report_item_value(report_item, 'exploit_code_maturity') - exploit_framework_metasploit = nfr.plugin.report_item_value(report_item, 'exploit_framework_metasploit') - exploitability_ease = nfr.plugin.report_item_value(report_item, 'exploitability_ease') - - if not self.report_vulnerabilities_debug_data_enabled and not \ - self.report_vulnerabilities_none_skip: + protocol = nfr.plugin.report_item_value( + report_item, "protocol" + ) + service_name = nfr.plugin.report_item_value( + report_item, "svc_name" + ) + port = int( + nfr.plugin.report_item_value(report_item, "port") + ) + plugin_id = int( + nfr.plugin.report_item_value(report_item, "pluginID") + ) + plugin_name = nfr.plugin.report_item_value( + report_item, "pluginName" + ) + plugin_type = nfr.plugin.report_item_value( + report_item, "plugin_type" + ) + risk_factor = nfr.plugin.report_item_value( + report_item, "risk_factor" + ) + plugin_family = nfr.plugin.report_item_value( + report_item, "pluginFamily" + ) + plugin_file_name = nfr.plugin.report_item_value( + report_item, "fname" + ) + plugin_version = nfr.plugin.report_item_value( + report_item, "script_version" + ) + plugin_publication_date = nfr.plugin.report_item_value( + report_item, "plugin_publication_date" + ) + plugin_modification_date = nfr.plugin.report_item_value( + report_item, "plugin_modification_date" + ) + plugin_description = nfr.plugin.report_item_value( + report_item, "description" + ) + solution = nfr.plugin.report_item_value( + report_item, "solution" + ) + plugin_output = nfr.plugin.report_item_value( + report_item, "plugin_output" + ) + plugin_cves = nfr.plugin.report_item_values( + report_item, "cve" + ) + exploit_available = nfr.plugin.report_item_value( + report_item, "exploit_available" + ) + exploit_code_maturity = nfr.plugin.report_item_value( + report_item, "exploit_code_maturity" + ) + exploit_framework_metasploit = nfr.plugin.report_item_value( + report_item, "exploit_framework_metasploit" + ) + exploitability_ease = nfr.plugin.report_item_value( + report_item, "exploitability_ease" + ) + + if ( + not self.report_vulnerabilities_debug_data_enabled + and not self.report_vulnerabilities_none_skip + ): worksheet.write(row_index, 0, host_report_host_name) worksheet.write(row_index, 1, host_resolved_hostname) worksheet.write(row_index, 2, host_resolved_fqdn) worksheet.write(row_index, 3, host_resolved_ip) - worksheet.write(row_index, 4, 'yes') + worksheet.write(row_index, 4, "yes") worksheet.write(row_index, 5, host_credentialed_checks) worksheet.write(row_index, 6, protocol) worksheet.write(row_index, 7, service_name) @@ -2383,21 +3235,31 @@ def create_worksheet_for_vulnerabilities(self, workbook, list_of_source_files): worksheet.write(row_index, 10, plugin_name) worksheet.write(row_index, 11, plugin_type) if self.report_vulnerabilities_none_filter_out: - if risk_factor == 'None': - worksheet.set_row(row_index, options={'hidden': True}) + if risk_factor == "None": + worksheet.set_row( + row_index, options={"hidden": True} + ) worksheet.write(row_index, 12, risk_factor) worksheet.write(row_index, 13, plugin_family) worksheet.write(row_index, 14, plugin_version) if plugin_publication_date is not None: - worksheet.write_datetime(row_index, 15, - nfr.plugin.plugin_date(plugin_publication_date), - date_format) + worksheet.write_datetime( + row_index, + 15, + nfr.plugin.plugin_date(plugin_publication_date), + date_format, + ) else: worksheet.write_blank(row_index, 15, None) if plugin_modification_date is not None: - worksheet.write_datetime(row_index, 16, - nfr.plugin.plugin_date(plugin_modification_date), - date_format) + worksheet.write_datetime( + row_index, + 16, + nfr.plugin.plugin_date( + plugin_modification_date + ), + date_format, + ) else: worksheet.write_blank(row_index, 16, None) worksheet.write(row_index, 17, plugin_description) @@ -2407,39 +3269,57 @@ def create_worksheet_for_vulnerabilities(self, workbook, list_of_source_files): else: worksheet.write_blank(row_index, 19, None) if plugin_cves: - worksheet.write_number(row_index, 20, len(plugin_cves)) - worksheet.write_string(row_index, 21, ','.join(plugin_cves)) + worksheet.write_number( + row_index, 20, len(plugin_cves) + ) + worksheet.write_string( + row_index, 21, ",".join(plugin_cves) + ) else: worksheet.write_number(row_index, 20, 0) worksheet.write_blank(row_index, 21, None) if exploit_available is not None: - worksheet.write_string(row_index, 22, exploit_available) + worksheet.write_string( + row_index, 22, exploit_available + ) else: worksheet.write_blank(row_index, 22, None) if exploit_code_maturity is not None: - worksheet.write_string(row_index, 23, exploit_code_maturity) + worksheet.write_string( + row_index, 23, exploit_code_maturity + ) else: worksheet.write_blank(row_index, 23, None) if exploit_framework_metasploit is not None: - worksheet.write_string(row_index, 24, exploit_framework_metasploit) + worksheet.write_string( + row_index, 24, exploit_framework_metasploit + ) else: worksheet.write_blank(row_index, 24, None) if exploitability_ease is not None: - worksheet.write_string(row_index, 25, exploitability_ease) + worksheet.write_string( + row_index, 25, exploitability_ease + ) else: worksheet.write_blank(row_index, 25, None) - elif not self.report_vulnerabilities_debug_data_enabled and \ - self.report_vulnerabilities_none_skip: - if risk_factor == 'None': + elif ( + not self.report_vulnerabilities_debug_data_enabled + and self.report_vulnerabilities_none_skip + ): + if risk_factor == "None": number_of_rows -= 1 row_index -= 1 - elif risk_factor != 'None': + elif risk_factor != "None": worksheet.write(row_index, 0, host_report_host_name) - worksheet.write(row_index, 1, host_resolved_hostname) + worksheet.write( + row_index, 1, host_resolved_hostname + ) worksheet.write(row_index, 2, host_resolved_fqdn) worksheet.write(row_index, 3, host_resolved_ip) - worksheet.write(row_index, 4, 'yes') - worksheet.write(row_index, 5, host_credentialed_checks) + worksheet.write(row_index, 4, "yes") + worksheet.write( + row_index, 5, host_credentialed_checks + ) worksheet.write(row_index, 6, protocol) worksheet.write(row_index, 7, service_name) worksheet.write(row_index, 8, port) @@ -2450,55 +3330,83 @@ def create_worksheet_for_vulnerabilities(self, workbook, list_of_source_files): worksheet.write(row_index, 13, plugin_family) worksheet.write(row_index, 14, plugin_version) if plugin_publication_date is not None: - worksheet.write_datetime(row_index, 15, - nfr.plugin.plugin_date(plugin_publication_date), - date_format) + worksheet.write_datetime( + row_index, + 15, + nfr.plugin.plugin_date( + plugin_publication_date + ), + date_format, + ) else: worksheet.write_blank(row_index, 15, None) if plugin_modification_date is not None: - worksheet.write_datetime(row_index, 16, - nfr.plugin.plugin_date(plugin_modification_date), - date_format) + worksheet.write_datetime( + row_index, + 16, + nfr.plugin.plugin_date( + plugin_modification_date + ), + date_format, + ) else: worksheet.write_blank(row_index, 16, None) worksheet.write(row_index, 17, plugin_description) worksheet.write(row_index, 18, solution) if plugin_output is not None: - worksheet.write_string(row_index, 19, plugin_output) + worksheet.write_string( + row_index, 19, plugin_output + ) else: worksheet.write_blank(row_index, 19, None) if plugin_cves: - worksheet.write_number(row_index, 20, len(plugin_cves)) - worksheet.write_string(row_index, 21, ','.join(plugin_cves)) + worksheet.write_number( + row_index, 20, len(plugin_cves) + ) + worksheet.write_string( + row_index, 21, ",".join(plugin_cves) + ) else: worksheet.write_number(row_index, 20, 0) worksheet.write_blank(row_index, 21, None) if exploit_available is not None: - worksheet.write_string(row_index, 22, exploit_available) + worksheet.write_string( + row_index, 22, exploit_available + ) else: worksheet.write_blank(row_index, 22, None) if exploit_code_maturity is not None: - worksheet.write_string(row_index, 23, exploit_code_maturity) + worksheet.write_string( + row_index, 23, exploit_code_maturity + ) else: worksheet.write_blank(row_index, 23, None) if exploit_framework_metasploit is not None: - worksheet.write_string(row_index, 24, exploit_framework_metasploit) + worksheet.write_string( + row_index, 24, exploit_framework_metasploit + ) else: worksheet.write_blank(row_index, 24, None) if exploitability_ease is not None: - worksheet.write_string(row_index, 25, exploitability_ease) + worksheet.write_string( + row_index, 25, exploitability_ease + ) else: worksheet.write_blank(row_index, 25, None) - elif self.report_vulnerabilities_debug_data_enabled and not \ - self.report_vulnerabilities_none_skip: + elif ( + self.report_vulnerabilities_debug_data_enabled + and not self.report_vulnerabilities_none_skip + ): worksheet.write(row_index, 0, host_scanner_ip) worksheet.write(row_index, 1, scan_report_name) - worksheet.write(row_index, 2, nessus_scan_file_name_with_path) + worksheet.write( + row_index, 2, nessus_scan_file_name_with_path + ) worksheet.write(row_index, 3, host_report_host_name) worksheet.write(row_index, 4, host_resolved_hostname) worksheet.write(row_index, 5, host_resolved_fqdn) worksheet.write(row_index, 6, host_resolved_ip) - worksheet.write(row_index, 7, 'yes') + worksheet.write(row_index, 7, "yes") worksheet.write(row_index, 8, host_credentialed_checks) worksheet.write(row_index, 9, scan_policy_name) worksheet.write(row_index, 10, protocol) @@ -2508,22 +3416,32 @@ def create_worksheet_for_vulnerabilities(self, workbook, list_of_source_files): worksheet.write(row_index, 14, plugin_name) worksheet.write(row_index, 15, plugin_type) if self.report_vulnerabilities_none_filter_out: - if risk_factor == 'None': - worksheet.set_row(row_index, options={'hidden': True}) + if risk_factor == "None": + worksheet.set_row( + row_index, options={"hidden": True} + ) worksheet.write(row_index, 16, risk_factor) worksheet.write(row_index, 17, plugin_family) worksheet.write(row_index, 18, plugin_file_name) worksheet.write(row_index, 19, plugin_version) if plugin_publication_date is not None: - worksheet.write_datetime(row_index, 20, - nfr.plugin.plugin_date(plugin_publication_date), - date_format) + worksheet.write_datetime( + row_index, + 20, + nfr.plugin.plugin_date(plugin_publication_date), + date_format, + ) else: worksheet.write_blank(row_index, 20, None) if plugin_modification_date is not None: - worksheet.write_datetime(row_index, 21, - nfr.plugin.plugin_date(plugin_modification_date), - date_format) + worksheet.write_datetime( + row_index, + 21, + nfr.plugin.plugin_date( + plugin_modification_date + ), + date_format, + ) else: worksheet.write_blank(row_index, 21, None) worksheet.write(row_index, 22, plugin_description) @@ -2533,42 +3451,62 @@ def create_worksheet_for_vulnerabilities(self, workbook, list_of_source_files): else: worksheet.write_blank(row_index, 24, None) if plugin_cves: - worksheet.write_number(row_index, 25, len(plugin_cves)) - worksheet.write_string(row_index, 26, ','.join(plugin_cves)) + worksheet.write_number( + row_index, 25, len(plugin_cves) + ) + worksheet.write_string( + row_index, 26, ",".join(plugin_cves) + ) else: worksheet.write_number(row_index, 25, 0) worksheet.write_blank(row_index, 26, None) if exploit_available is not None: - worksheet.write_string(row_index, 27, exploit_available) + worksheet.write_string( + row_index, 27, exploit_available + ) else: worksheet.write_blank(row_index, 27, None) if exploit_code_maturity is not None: - worksheet.write_string(row_index, 28, exploit_code_maturity) + worksheet.write_string( + row_index, 28, exploit_code_maturity + ) else: worksheet.write_blank(row_index, 28, None) if exploit_framework_metasploit is not None: - worksheet.write_string(row_index, 29, exploit_framework_metasploit) + worksheet.write_string( + row_index, 29, exploit_framework_metasploit + ) else: worksheet.write_blank(row_index, 29, None) if exploitability_ease is not None: - worksheet.write_string(row_index, 30, exploitability_ease) + worksheet.write_string( + row_index, 30, exploitability_ease + ) else: worksheet.write_blank(row_index, 30, None) - elif self.report_vulnerabilities_debug_data_enabled and \ - self.report_vulnerabilities_none_skip: - if risk_factor == 'None': + elif ( + self.report_vulnerabilities_debug_data_enabled + and self.report_vulnerabilities_none_skip + ): + if risk_factor == "None": number_of_rows -= 1 row_index -= 1 - elif risk_factor != 'None': + elif risk_factor != "None": worksheet.write(row_index, 0, host_scanner_ip) worksheet.write(row_index, 1, scan_report_name) - worksheet.write(row_index, 2, nessus_scan_file_name_with_path) + worksheet.write( + row_index, 2, nessus_scan_file_name_with_path + ) worksheet.write(row_index, 3, host_report_host_name) - worksheet.write(row_index, 4, host_resolved_hostname) + worksheet.write( + row_index, 4, host_resolved_hostname + ) worksheet.write(row_index, 5, host_resolved_fqdn) worksheet.write(row_index, 6, host_resolved_ip) - worksheet.write(row_index, 7, 'yes') - worksheet.write(row_index, 8, host_credentialed_checks) + worksheet.write(row_index, 7, "yes") + worksheet.write( + row_index, 8, host_credentialed_checks + ) worksheet.write(row_index, 9, scan_policy_name) worksheet.write(row_index, 10, protocol) worksheet.write(row_index, 11, service_name) @@ -2581,66 +3519,101 @@ def create_worksheet_for_vulnerabilities(self, workbook, list_of_source_files): worksheet.write(row_index, 18, plugin_file_name) worksheet.write(row_index, 19, plugin_version) if plugin_publication_date is not None: - worksheet.write_datetime(row_index, 20, - nfr.plugin.plugin_date(plugin_publication_date), - date_format) + worksheet.write_datetime( + row_index, + 20, + nfr.plugin.plugin_date( + plugin_publication_date + ), + date_format, + ) else: worksheet.write_blank(row_index, 20, None) if plugin_modification_date is not None: - worksheet.write_datetime(row_index, 21, - nfr.plugin.plugin_date(plugin_modification_date), - date_format) + worksheet.write_datetime( + row_index, + 21, + nfr.plugin.plugin_date( + plugin_modification_date + ), + date_format, + ) else: worksheet.write_blank(row_index, 21, None) worksheet.write(row_index, 22, plugin_description) worksheet.write(row_index, 23, solution) if plugin_output is not None: - worksheet.write_string(row_index, 24, plugin_output) + worksheet.write_string( + row_index, 24, plugin_output + ) else: worksheet.write_blank(row_index, 24, None) if plugin_cves: - worksheet.write_number(row_index, 25, len(plugin_cves)) - worksheet.write_string(row_index, 26, ','.join(plugin_cves)) + worksheet.write_number( + row_index, 25, len(plugin_cves) + ) + worksheet.write_string( + row_index, 26, ",".join(plugin_cves) + ) else: worksheet.write_number(row_index, 25, 0) worksheet.write_blank(row_index, 26, None) if exploit_available is not None: - worksheet.write_string(row_index, 27, exploit_available) + worksheet.write_string( + row_index, 27, exploit_available + ) else: worksheet.write_blank(row_index, 27, None) if exploit_code_maturity is not None: - worksheet.write_string(row_index, 28, exploit_code_maturity) + worksheet.write_string( + row_index, 28, exploit_code_maturity + ) else: worksheet.write_blank(row_index, 28, None) if exploit_framework_metasploit is not None: - worksheet.write_string(row_index, 29, exploit_framework_metasploit) + worksheet.write_string( + row_index, 29, exploit_framework_metasploit + ) else: worksheet.write_blank(row_index, 29, None) if exploitability_ease is not None: - worksheet.write_string(row_index, 30, exploitability_ease) + worksheet.write_string( + row_index, 30, exploitability_ease + ) else: worksheet.write_blank(row_index, 30, None) end_time = time.time() elapsed_time = end_time - start_time - elapsed_time_parsed = time.strftime('%H:%M:%S', time.gmtime(elapsed_time)) + elapsed_time_parsed = time.strftime( + "%H:%M:%S", time.gmtime(elapsed_time) + ) - self.log_emitter('info ', file_to_pars_full_name, f'[elapsed_time={elapsed_time_parsed}]') - self.log_emitter('end ', file_to_pars_full_name) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[elapsed_time={elapsed_time_parsed}]", + ) + self.log_emitter("end ", file_to_pars_full_name) except Exception as e: number_of_files_with_errors += 1 traceback.print_exc() - self.log_emitter('info', - "ERROR Parsing [" + str(nessus_scan_file_number) + "/" + - str(len(list_of_source_files)) + "] nessus files") - self.log_emitter('info', e) + self.log_emitter( + "info", + "ERROR Parsing [" + + str(nessus_scan_file_number) + + "/" + + str(len(list_of_source_files)) + + "] nessus files", + ) + self.log_emitter("info", e) if number_of_rows > 0: worksheet.autofilter(0, 0, number_of_rows, number_of_columns - 1) if self.report_vulnerabilities_none_filter_out: - risk_factors = ['Critical', 'High', 'Medium', 'Low'] + risk_factors = ["Critical", "High", "Medium", "Low"] if not self.report_vulnerabilities_debug_data_enabled: worksheet.filter_column_list(12, risk_factors) else: @@ -2652,80 +3625,85 @@ def create_worksheet_for_noncompliance(self, workbook, list_of_source_files): :param workbook: workbook where spreadsheet are created :param list_of_source_files: list of selected files """ - report_name = 'noncompliance' + report_name = "noncompliance" self.report_counter += 1 - info_report = 'Report: ' + str(self.report_counter) + '/' + str(self.number_of_selected_reports) + info_report = ( + "Report: " + + str(self.report_counter) + + "/" + + str(self.number_of_selected_reports) + ) worksheet = workbook.add_worksheet(report_name) - cell_format_bold = workbook.add_format({'bold': True}) + cell_format_bold = workbook.add_format({"bold": True}) worksheet.set_row(0, None, cell_format_bold) # print('>>>>>>>>>>>>>>>> ', self.report_noncompliance_debug_data_enabled) if not self.report_noncompliance_debug_data_enabled: headers = [ - # 'Nessus scanner IP', - # 'Nessus scan name', - # 'Nessus file name', - 'Target', - 'Hostname', - 'FQDN', - 'IP', - 'Scanned', - 'Credentialed checks', - # 'Policy name', - 'Plugin ID', - 'Plugin name', - 'Plugin type', - # 'Risk Factor', - 'Plugin family', - # 'Compliance plugin file', - # 'Plugin file name', - 'Plugin version', - 'Plugin publication date', - 'Plugin modification date', - 'Check name', - 'Audit file name', - # 'Check ID', - 'Current value', - # 'Uname', - 'Description', - 'Check status', - 'Reference', - 'Error' + # "Nessus scanner IP", + # "Nessus scan name", + # "Nessus file name", + "Target", + "Hostname", + "FQDN", + "IP", + "Scanned", + "Credentialed checks", + # "Policy name", + "Plugin ID", + "Plugin name", + "Plugin type", + # "Risk Factor", + "Plugin family", + # "Compliance plugin file", + # "Plugin file name", + "Plugin version", + "Plugin publication date", + "Plugin modification date", + "Check name", + "Audit file name", + # "Check ID", + "Current value", + # "Uname", + "Description", + "Check status", + "Reference", + "Error", ] else: headers = [ - 'Nessus scanner IP', - 'Nessus scan name', - 'Nessus file name', - 'Target', - 'Hostname', - 'FQDN', - 'IP', - 'Scanned', - 'Credentialed checks', - 'Policy name', - 'Plugin ID', - 'Plugin name', - 'Plugin type', - 'Risk Factor', - 'Plugin family', - 'Compliance plugin file', - 'Plugin file name', - 'Plugin version', - 'Plugin publication date', - 'Plugin modification date', - 'Check name', - 'Audit file name', - 'Check ID', - 'Current value', - 'Uname', - 'Description', - 'Check status', - 'Reference', - 'Error' + "Nessus scanner IP", + "Nessus scan name", + "Nessus file name", + "Target", + "Hostname", + "FQDN", + "IP", + "Scanned", + "Credentialed checks", + "Policy name", + "Plugin ID", + "Plugin name", + "Plugin type", + "Risk Factor", + "Plugin family", + "Compliance plugin file", + "Plugin file name", + "Plugin version", + "Plugin publication date", + "Plugin modification date", + "Check name", + "Audit file name", + "Check ID", + "Current value", + "Uname", + "Description", + "Check status", + "Reference", + "Error", ] number_of_columns = len(headers) # print('Number of columns: ' + str(number_of_columns)) @@ -2735,10 +3713,13 @@ def create_worksheet_for_noncompliance(self, workbook, list_of_source_files): debug_columns_list = [0, 1, 2, 9, 13, 15, 16, 22, 24] for column_index, header in enumerate(headers): - if self.report_noncompliance_debug_data_enabled and column_index in debug_columns_list: + if ( + self.report_noncompliance_debug_data_enabled + and column_index in debug_columns_list + ): cell_format_bold_blue = workbook.add_format() cell_format_bold_blue.set_bold() - cell_format_bold_blue.set_font_color('blue') + cell_format_bold_blue.set_font_color("blue") worksheet.write(0, column_index, header, cell_format_bold_blue) else: worksheet.write(0, column_index, header) @@ -2752,64 +3733,107 @@ def create_worksheet_for_noncompliance(self, workbook, list_of_source_files): row_index = 0 row_index_per_file = 0 for scan_file in list_of_source_files: - source_file_type = '' + source_file_type = "" files_to_pars = [] - if fnmatch.fnmatch(scan_file, '*.nessus'): + if fnmatch.fnmatch(scan_file, "*.nessus"): files_to_pars.append(scan_file) - source_file_type = 'nessus' - elif fnmatch.fnmatch(scan_file, '*.zip'): - source_file_type = 'zip' + source_file_type = "nessus" + elif fnmatch.fnmatch(scan_file, "*.zip"): + source_file_type = "zip" zip_source = zipfile.ZipFile(scan_file) zip_files_list = zip_source.namelist() for zip_file in zip_files_list: - if fnmatch.fnmatch(zip_file, '*.nessus'): + if fnmatch.fnmatch(zip_file, "*.nessus"): files_to_pars.append(zip_file) for file_to_pars in files_to_pars: row_index_per_file += 1 nessus_scan_file_number = nessus_scan_file_number + 1 - info_file = ', File: ' + str(row_index_per_file) + '/' + str(len(list_of_source_files)) + info_file = ( + ", File: " + + str(row_index_per_file) + + "/" + + str(len(list_of_source_files)) + ) try: - file_to_pars_full_name = '' + file_to_pars_full_name = "" source_file_size = 0 - if source_file_type == 'nessus': + if source_file_type == "nessus": root = nfr.file.nessus_scan_file_root_element(file_to_pars) scan_file_source = nfr.scan.scan_file_source(root) - file_to_pars_full_name = nfr.file.nessus_scan_file_name_with_path(file_to_pars) - source_file_size = nfa.utilities.size_of_file_human(file_to_pars_full_name) - elif source_file_type == 'zip': - source_file_size = nfa.utilities.size_of_file_inside_zip_human(zip_source, file_to_pars) - file_to_pars_full_name = f'{nfr.file.nessus_scan_file_name_with_path(scan_file)} [' \ - f'{zip_source.getinfo(file_to_pars).filename}]' + file_to_pars_full_name = ( + nfr.file.nessus_scan_file_name_with_path(file_to_pars) + ) + source_file_size = nfa.utilities.size_of_file_human( + file_to_pars_full_name + ) + elif source_file_type == "zip": + source_file_size = nfa.utilities.size_of_file_inside_zip_human( + zip_source, file_to_pars + ) + file_to_pars_full_name = ( + f"{nfr.file.nessus_scan_file_name_with_path(scan_file)} [" + f"{zip_source.getinfo(file_to_pars).filename}]" + ) file_to_pars = zip_source.open(file_to_pars) root = nfr.file.nessus_scan_file_root_element(file_to_pars) scan_file_source = nfr.scan.scan_file_source(root) start_time = time.time() - self.log_emitter('start', file_to_pars_full_name) + self.log_emitter("start", file_to_pars_full_name) self.file_analysis_started.emit(1) - self.log_emitter('info ', file_to_pars_full_name, f'[scan_file_source={scan_file_source}]') - self.log_emitter('info ', file_to_pars_full_name, f'[source_file_size={source_file_size}]') - self.log_emitter('info ', file_to_pars_full_name, f'[report_type={report_name}]') - self.log_emitter('info ', file_to_pars_full_name, - f'[number_of_target_hosts={nfr.scan.number_of_target_hosts(root)}]') - self.log_emitter('info ', file_to_pars_full_name, - f'[number_of_scanned_hosts={nfr.scan.number_of_scanned_hosts(root)}]') - self.log_emitter('info ', file_to_pars_full_name, - f'[number_of_scanned_hosts_with_credentials=' - f'{nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(root)}]') - - date_format = workbook.add_format({'num_format': 'yyyy-mm-dd hh:mm:ss'}) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[scan_file_source={scan_file_source}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[source_file_size={source_file_size}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[report_type={report_name}]" + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[number_of_target_hosts={nfr.scan.number_of_target_hosts(root)}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[number_of_scanned_hosts={nfr.scan.number_of_scanned_hosts(root)}]", + ) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[number_of_scanned_hosts_with_credentials=" + f"{nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(root)}]", + ) + + date_format = workbook.add_format( + {"num_format": "yyyy-mm-dd hh:mm:ss"} + ) scan_report_name = nfr.scan.report_name(root) - nessus_scan_file_name_with_path = nfr.file.nessus_scan_file_name_with_path(file_to_pars_full_name) + nessus_scan_file_name_with_path = ( + nfr.file.nessus_scan_file_name_with_path(file_to_pars_full_name) + ) scan_policy_name = nfr.scan.policy_name(root) report_host_counter = 0 number_of_report_hosts = nfr.scan.number_of_scanned_hosts(root) - info_host = ', Host: ' + str(report_host_counter) + '/' + str(number_of_report_hosts) + info_host = ( + ", Host: " + + str(report_host_counter) + + "/" + + str(number_of_report_hosts) + ) info_final = info_report + info_file + info_host self.print_status_bar_info.emit(info_final) @@ -2817,7 +3841,12 @@ def create_worksheet_for_noncompliance(self, workbook, list_of_source_files): report_host_counter += 1 self.progress.emit(report_host_counter, number_of_report_hosts) - info_host = ', Host: ' + str(report_host_counter) + '/' + str(number_of_report_hosts) + info_host = ( + ", Host: " + + str(report_host_counter) + + "/" + + str(number_of_report_hosts) + ) info_final = info_report + info_file + info_host self.print_status_bar_info.emit(info_final) @@ -2827,79 +3856,158 @@ def create_worksheet_for_noncompliance(self, workbook, list_of_source_files): host_resolved_hostname = nfr.host.resolved_hostname(report_host) host_resolved_fqdn = nfr.host.resolved_fqdn(report_host) host_resolved_ip = nfr.host.resolved_ip(report_host) - host_credentialed_checks = nfr.host.credentialed_checks(root, report_host) + host_credentialed_checks = nfr.host.credentialed_checks( + root, report_host + ) for report_item in report_items_per_host: if nfr.plugin.compliance_plugin(report_item): - compliance_plugin = 'true' + compliance_plugin = "true" row_index += 1 number_of_rows += 1 - compliance_check_name = \ - nfr.plugin.compliance_check_item_value(report_item, 'cm:compliance-check-name') - compliance_check_audit_file = \ - nfr.plugin.compliance_check_item_value(report_item, 'cm:compliance-audit-file') - compliance_check_check_id = \ - nfr.plugin.compliance_check_item_value(report_item, 'cm:compliance-check-id') - compliance_check_actual_value = \ - nfr.plugin.compliance_check_item_value(report_item, 'cm:compliance-actual-value') - compliance_check_uname = \ - nfr.plugin.compliance_check_item_value(report_item, 'cm:compliance-uname') - compliance_check_info = \ - nfr.plugin.compliance_check_item_value(report_item, 'cm:compliance-info') - compliance_check_result = \ - nfr.plugin.compliance_check_item_value(report_item, 'cm:compliance-result') - compliance_check_reference = \ - nfr.plugin.compliance_check_item_value(report_item, 'cm:compliance-reference') - compliance_check_error = \ - nfr.plugin.compliance_check_item_value(report_item, 'cm:compliance-error') - - plugin_id = int(nfr.plugin.report_item_value(report_item, 'pluginID')) - plugin_name = nfr.plugin.report_item_value(report_item, 'pluginName') - plugin_type = nfr.plugin.report_item_value(report_item, 'plugin_type') - risk_factor = nfr.plugin.report_item_value(report_item, 'risk_factor') - plugin_family = nfr.plugin.report_item_value(report_item, 'pluginFamily') - plugin_file_name = nfr.plugin.report_item_value(report_item, 'fname') - plugin_version = nfr.plugin.report_item_value(report_item, 'script_version') - plugin_publication_date = \ - nfr.plugin.report_item_value(report_item, 'plugin_publication_date') - plugin_modification_date = \ - nfr.plugin.report_item_value(report_item, 'plugin_modification_date') + compliance_check_name = ( + nfr.plugin.compliance_check_item_value( + report_item, "cm:compliance-check-name" + ) + ) + compliance_check_audit_file = ( + nfr.plugin.compliance_check_item_value( + report_item, "cm:compliance-audit-file" + ) + ) + compliance_check_check_id = ( + nfr.plugin.compliance_check_item_value( + report_item, "cm:compliance-check-id" + ) + ) + compliance_check_actual_value = ( + nfr.plugin.compliance_check_item_value( + report_item, "cm:compliance-actual-value" + ) + ) + compliance_check_uname = ( + nfr.plugin.compliance_check_item_value( + report_item, "cm:compliance-uname" + ) + ) + compliance_check_info = ( + nfr.plugin.compliance_check_item_value( + report_item, "cm:compliance-info" + ) + ) + compliance_check_result = ( + nfr.plugin.compliance_check_item_value( + report_item, "cm:compliance-result" + ) + ) + compliance_check_reference = ( + nfr.plugin.compliance_check_item_value( + report_item, "cm:compliance-reference" + ) + ) + compliance_check_error = ( + nfr.plugin.compliance_check_item_value( + report_item, "cm:compliance-error" + ) + ) + + plugin_id = int( + nfr.plugin.report_item_value( + report_item, "pluginID" + ) + ) + plugin_name = nfr.plugin.report_item_value( + report_item, "pluginName" + ) + plugin_type = nfr.plugin.report_item_value( + report_item, "plugin_type" + ) + risk_factor = nfr.plugin.report_item_value( + report_item, "risk_factor" + ) + plugin_family = nfr.plugin.report_item_value( + report_item, "pluginFamily" + ) + plugin_file_name = nfr.plugin.report_item_value( + report_item, "fname" + ) + plugin_version = nfr.plugin.report_item_value( + report_item, "script_version" + ) + plugin_publication_date = nfr.plugin.report_item_value( + report_item, "plugin_publication_date" + ) + plugin_modification_date = nfr.plugin.report_item_value( + report_item, "plugin_modification_date" + ) if not self.report_noncompliance_debug_data_enabled: worksheet.write(row_index, 0, host_report_host_name) - worksheet.write(row_index, 1, host_resolved_hostname) + worksheet.write( + row_index, 1, host_resolved_hostname + ) worksheet.write(row_index, 2, host_resolved_fqdn) worksheet.write(row_index, 3, host_resolved_ip) - worksheet.write(row_index, 4, 'yes') - worksheet.write(row_index, 5, host_credentialed_checks) + worksheet.write(row_index, 4, "yes") + worksheet.write( + row_index, 5, host_credentialed_checks + ) worksheet.write(row_index, 6, plugin_id) worksheet.write(row_index, 7, plugin_name) worksheet.write(row_index, 8, plugin_type) worksheet.write(row_index, 9, plugin_family) worksheet.write(row_index, 10, plugin_version) - worksheet.write_datetime(row_index, 11, - nfr.plugin.plugin_date(plugin_publication_date), - date_format) - worksheet.write_datetime(row_index, 12, - nfr.plugin.plugin_date(plugin_modification_date), - date_format) - worksheet.write(row_index, 13, compliance_check_name) - worksheet.write(row_index, 14, compliance_check_audit_file) - worksheet.write(row_index, 15, compliance_check_actual_value) - worksheet.write(row_index, 16, compliance_check_info) - worksheet.write(row_index, 17, compliance_check_result) - worksheet.write(row_index, 18, compliance_check_reference) - worksheet.write(row_index, 19, compliance_check_error) + worksheet.write_datetime( + row_index, + 11, + nfr.plugin.plugin_date(plugin_publication_date), + date_format, + ) + worksheet.write_datetime( + row_index, + 12, + nfr.plugin.plugin_date( + plugin_modification_date + ), + date_format, + ) + worksheet.write( + row_index, 13, compliance_check_name + ) + worksheet.write( + row_index, 14, compliance_check_audit_file + ) + worksheet.write( + row_index, 15, compliance_check_actual_value + ) + worksheet.write( + row_index, 16, compliance_check_info + ) + worksheet.write( + row_index, 17, compliance_check_result + ) + worksheet.write( + row_index, 18, compliance_check_reference + ) + worksheet.write( + row_index, 19, compliance_check_error + ) else: worksheet.write(row_index, 0, host_scanner_ip) worksheet.write(row_index, 1, scan_report_name) - worksheet.write(row_index, 2, nessus_scan_file_name_with_path) + worksheet.write( + row_index, 2, nessus_scan_file_name_with_path + ) worksheet.write(row_index, 3, host_report_host_name) - worksheet.write(row_index, 4, host_resolved_hostname) + worksheet.write( + row_index, 4, host_resolved_hostname + ) worksheet.write(row_index, 5, host_resolved_fqdn) worksheet.write(row_index, 6, host_resolved_ip) - worksheet.write(row_index, 7, 'yes') - worksheet.write(row_index, 8, host_credentialed_checks) + worksheet.write(row_index, 7, "yes") + worksheet.write( + row_index, 8, host_credentialed_checks + ) worksheet.write(row_index, 9, scan_policy_name) worksheet.write(row_index, 10, plugin_id) worksheet.write(row_index, 11, plugin_name) @@ -2909,41 +4017,78 @@ def create_worksheet_for_noncompliance(self, workbook, list_of_source_files): worksheet.write(row_index, 15, compliance_plugin) worksheet.write(row_index, 16, plugin_file_name) worksheet.write(row_index, 17, plugin_version) - worksheet.write_datetime(row_index, 18, - nfr.plugin.plugin_date(plugin_publication_date), - date_format) - worksheet.write_datetime(row_index, 19, - nfr.plugin.plugin_date(plugin_modification_date), - date_format) - worksheet.write(row_index, 20, compliance_check_name) - worksheet.write(row_index, 21, compliance_check_audit_file) - worksheet.write(row_index, 22, compliance_check_check_id) - worksheet.write(row_index, 23, compliance_check_actual_value) - worksheet.write(row_index, 24, compliance_check_uname) - worksheet.write(row_index, 25, compliance_check_info) - worksheet.write(row_index, 26, compliance_check_result) - worksheet.write(row_index, 27, compliance_check_reference) - worksheet.write(row_index, 28, compliance_check_error) + worksheet.write_datetime( + row_index, + 18, + nfr.plugin.plugin_date(plugin_publication_date), + date_format, + ) + worksheet.write_datetime( + row_index, + 19, + nfr.plugin.plugin_date( + plugin_modification_date + ), + date_format, + ) + worksheet.write( + row_index, 20, compliance_check_name + ) + worksheet.write( + row_index, 21, compliance_check_audit_file + ) + worksheet.write( + row_index, 22, compliance_check_check_id + ) + worksheet.write( + row_index, 23, compliance_check_actual_value + ) + worksheet.write( + row_index, 24, compliance_check_uname + ) + worksheet.write( + row_index, 25, compliance_check_info + ) + worksheet.write( + row_index, 26, compliance_check_result + ) + worksheet.write( + row_index, 27, compliance_check_reference + ) + worksheet.write( + row_index, 28, compliance_check_error + ) end_time = time.time() elapsed_time = end_time - start_time - elapsed_time_parsed = time.strftime('%H:%M:%S', time.gmtime(elapsed_time)) + elapsed_time_parsed = time.strftime( + "%H:%M:%S", time.gmtime(elapsed_time) + ) - self.log_emitter('info ', file_to_pars_full_name, f'[elapsed_time={elapsed_time_parsed}]') - self.log_emitter('end ', file_to_pars_full_name) + self.log_emitter( + "info ", + file_to_pars_full_name, + f"[elapsed_time={elapsed_time_parsed}]", + ) + self.log_emitter("end ", file_to_pars_full_name) except Exception as e: number_of_files_with_errors += 1 traceback.print_exc() - self.log_emitter('info', - "ERROR Parsing [" + str(nessus_scan_file_number) + "/" + - str(len(list_of_source_files)) + "] nessus files") - self.log_emitter('info', e) + self.log_emitter( + "info", + "ERROR Parsing [" + + str(nessus_scan_file_number) + + "/" + + str(len(list_of_source_files)) + + "] nessus files", + ) + self.log_emitter("info", e) if number_of_rows > 0: worksheet.autofilter(0, 0, number_of_rows, number_of_columns - 1) - def log_emitter(self, action_name, source_file_name, additional_info=''): + def log_emitter(self, action_name, source_file_name, additional_info=""): """ Function emits information from thread to display it in GUI. @@ -2951,7 +4096,7 @@ def log_emitter(self, action_name, source_file_name, additional_info=''): :param source_file_name: information about related source file :param additional_info: add more information if needed """ - notification = f'[action={action_name}] [source_file_name={source_file_name}] {additional_info}' + notification = f"[action={action_name}] [source_file_name={source_file_name}] {additional_info}" self.signal.emit(notification) diff --git a/nessus_file_analyzer/dialogs/about.py b/nessus_file_analyzer/dialogs/about.py index 5007b20..8a2417f 100644 --- a/nessus_file_analyzer/dialogs/about.py +++ b/nessus_file_analyzer/dialogs/about.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -u""" +""" nessus file analyzer by LimberDuck (pronounced *ˈlɪm.bɚ dʌk*) is a GUI tool which enables you to parse multiple nessus files containing the results of scans performed by using Nessus by (C) Tenable, Inc. and exports parsed @@ -45,12 +45,21 @@ def __init__(self, parent=None): msg_box.setWindowTitle(self.tr("About " + self.appName)) msg_box.setTextFormat(Qt.RichText) # msg_box.setIconPixmap(QPixmap(ComicTaggerSettings.getGraphic('about.png'))) - msg_box.setText("
" - + self.appName + " v" + self.version + " [" + self.release_date + "]
" - + "Copyright " + copyright_info + "

" - + "{0}

".format(website) - + "{0}

".format(email) - + "License: {1}".format(license_link, license_name)) + msg_box.setText( + "
" + + self.appName + + " v" + + self.version + + " [" + + self.release_date + + "]
" + + "Copyright " + + copyright_info + + "

" + + "{0}

".format(website) + + "{0}

".format(email) + + "License: {1}".format(license_link, license_name) + ) msg_box.setStandardButtons(QMessageBox.Ok) msg_box.exec_() diff --git a/nessus_file_analyzer/ico.py b/nessus_file_analyzer/ico.py index 710064b..5f71238 100644 --- a/nessus_file_analyzer/ico.py +++ b/nessus_file_analyzer/ico.py @@ -1,4 +1,3 @@ -ico = \ -''' +ico = """ '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' -''' \ No newline at end of file +""" diff --git a/nessus_file_analyzer/ico_generator.py b/nessus_file_analyzer/ico_generator.py index 930db09..2c7a6c8 100644 --- a/nessus_file_analyzer/ico_generator.py +++ b/nessus_file_analyzer/ico_generator.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -u""" +""" nessus file analyzer by LimberDuck (pronounced *ˈlɪm.bɚ dʌk*) is a GUI tool which enables you to parse multiple nessus files containing the results of scans performed by using Nessus by (C) Tenable, Inc. and exports parsed @@ -22,6 +22,6 @@ from nessus_file_analyzer import utilities -png_filename = '../icons/LimberDuck-nessus-file-analyzer.png' +png_filename = "../icons/LimberDuck-nessus-file-analyzer.png" print(utilities.file_to_base64(utilities.png_to_ico(png_filename))) diff --git a/nessus_file_analyzer/utilities.py b/nessus_file_analyzer/utilities.py index a3b0382..f5862b6 100644 --- a/nessus_file_analyzer/utilities.py +++ b/nessus_file_analyzer/utilities.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -u""" +""" nessus file analyzer by LimberDuck (pronounced *ˈlɪm.bɚ dʌk*) is a GUI tool which enables you to parse multiple nessus files containing the results of scans performed by using Nessus by (C) Tenable, Inc. and exports parsed @@ -33,7 +33,7 @@ def file_to_base64(filename): :param filename: input file name with path :return: base64 string """ - with open(filename, 'rb') as image_file: + with open(filename, "rb") as image_file: encoded_string = base64.b64encode(image_file.read()) return encoded_string @@ -45,7 +45,7 @@ def png_to_ico(filename): :return: ico file name """ filename_without_extension = os.path.splitext(filename)[0] - target_file_name = filename_without_extension + '.ico' + target_file_name = filename_without_extension + ".ico" img = imageio.imread(filename) imageio.imwrite(target_file_name, img) @@ -64,9 +64,9 @@ def base64_to_ico(ico_in_base64, filename): :return: ico file name """ filename_without_extension = os.path.splitext(filename)[0] - target_file_name = filename_without_extension + '.ico' + target_file_name = filename_without_extension + ".ico" icondata = base64.b64decode(ico_in_base64) - iconfile = open(target_file_name, 'wb') + iconfile = open(target_file_name, "wb") iconfile.write(icondata) iconfile.close() @@ -79,14 +79,14 @@ def check_file_encoding(file): :param file: input file path :return: file encoding eg. 'ascii', 'utf8' """ - raw_data = open(file, 'rb').read() + raw_data = open(file, "rb").read() result = chardet.detect(raw_data) - char_enc = result['encoding'] + char_enc = result["encoding"] return char_enc -def size_human(size, suffix='B'): +def size_human(size, suffix="B"): """ Function convert provided size into human readable form :param size: number @@ -94,14 +94,14 @@ def size_human(size, suffix='B'): :return: file size in human readable form """ - for unit in [' b', ' Ki', ' Mi', ' Gi', ' Ti', ' Pi', ' Ei', ' Zi']: + for unit in [" b", " Ki", " Mi", " Gi", " Ti", " Pi", " Ei", " Zi"]: if abs(size) < 1024.0: - return '%3.1f%s%s' % (size, unit, suffix) + return "%3.1f%s%s" % (size, unit, suffix) size /= 1024.0 - return '%.1f%s%s' % (size, 'Yi', suffix) + return "%.1f%s%s" % (size, "Yi", suffix) -def size_of_file_human(file, suffix='B'): +def size_of_file_human(file, suffix="B"): """ Function convert size of provided file into human readable form :param file: source file name with path @@ -114,7 +114,7 @@ def size_of_file_human(file, suffix='B'): return file_real_size_human -def size_of_file_inside_zip_human(zip_file, file_inside_zip, suffix='B'): +def size_of_file_inside_zip_human(zip_file, file_inside_zip, suffix="B"): """ Function convert size of file from inside of provided zip file into human readable form :param zip_file: source zip file @@ -129,7 +129,7 @@ def size_of_file_inside_zip_human(zip_file, file_inside_zip, suffix='B'): file_compress_size = zip_file.getinfo(file_inside_zip).compress_size file_compress_size_human = size_human(file_compress_size, suffix) - return f'{file_compress_size_human} [{file_real_size_human}]' + return f"{file_compress_size_human} [{file_real_size_human}]" def csv_file_row_counter(file, source_file_delimiter): @@ -140,9 +140,9 @@ def csv_file_row_counter(file, source_file_delimiter): :return: number of rows """ source_file_encoding = check_file_encoding(file) - file = open(file, 'r', encoding=source_file_encoding) - csv.register_dialect('colons', delimiter=source_file_delimiter) - reader = csv.reader(file, dialect='colons') + file = open(file, "r", encoding=source_file_encoding) + csv.register_dialect("colons", delimiter=source_file_delimiter) + reader = csv.reader(file, dialect="colons") row_count = sum(1 for row in reader) return row_count diff --git a/requirements.txt b/requirements.txt index bf654bf..f8551e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -chardet>=4.0.0 -imageio>=2.19.1 -nessus-file-reader>=0.4.1 -PyQt5>=5.15.6 -XlsxWriter>=3.0.3 +chardet>=5.2.0 +imageio>=2.37.0 +nessus-file-reader>=0.4.3 +PyQt5>=5.15.11 +XlsxWriter>=3.2.2 diff --git a/setup.py b/setup.py index 4f7b182..72f0e08 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ with open("README.md", "r") as fh: long_description = fh.read() -with open('requirements.txt') as f: +with open("requirements.txt") as f: required = f.read().splitlines() about = {} @@ -17,17 +17,15 @@ author="Damian Krawczyk", author_email="damian.krawczyk@limberduck.org", description="nessus file analyzer by LimberDuck is a GUI tool which enables you to parse nessus scan files from " - "Nessus and Tenable.SC by (C) Tenable, Inc. and exports results to a Microsoft Excel Workbook for " - "effortless analysis.", + "Nessus and Tenable.SC by (C) Tenable, Inc. and exports results to a Microsoft Excel Workbook for " + "effortless analysis.", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/LimberDuck/nessus-file-analyzer", packages=setuptools.find_packages(), install_requires=required, entry_points={ - "gui_scripts": [ - "nessus-file-analyzer = nessus_file_analyzer.__main__:main" - ] + "gui_scripts": ["nessus-file-analyzer = nessus_file_analyzer.__main__:main"] }, classifiers=[ "Programming Language :: Python :: 3.9", From 63ef8e87905fde0253cfd4d22e264d2cccc99b63 Mon Sep 17 00:00:00 2001 From: Damian Krawczyk Date: Thu, 20 Feb 2025 21:24:28 +0100 Subject: [PATCH 2/3] documentation link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9ca7e3..0fce043 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ decrease our workload and focus on data analysis. ## Documentation -Visit https://nessus-file-analyzer.readthedocs.io to find out more. +Visit https://limberduck.org/en/latest/tools/nessus-file-analyzer/ or https://nessus-file-analyzer.readthedocs.io to find out more. ## Installation From 212c0a6428dc648e5b2c459cbefd7694171ec559 Mon Sep 17 00:00:00 2001 From: Damian Krawczyk Date: Thu, 20 Feb 2025 22:00:22 +0100 Subject: [PATCH 3/3] p3.8 removed --- .github/workflows/python-package.yml | 2 +- CHANGELOG.md | 2 +- setup.py | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index c0f7ce7..045bc6d 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] steps: - uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index f5ad756..ae82f27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - tests for python - added: 3.10, 3.11, 3.12, 3.13 - - removed: 3.7 + - removed: 3.7, 3.8 ## [0.7.2] - 2022-05-13 diff --git a/setup.py b/setup.py index 72f0e08..03015ed 100644 --- a/setup.py +++ b/setup.py @@ -28,9 +28,11 @@ "gui_scripts": ["nessus-file-analyzer = nessus_file_analyzer.__main__:main"] }, classifiers=[ + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.7", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Operating System :: OS Independent", "Development Status :: 4 - Beta",