From 4a501e0577e483f980808a82cf4b1a485d503952 Mon Sep 17 00:00:00 2001 From: ozgen Date: Mon, 14 Sep 2026 14:45:39 +0200 Subject: [PATCH] add: add export_delta_scan_report request support --- gvm/protocols/gmp/_gmpnext.py | 53 ++++++ .../gmp/requests/next/_scan_report.py | 82 ++++++++ .../gmpnext/entities/scan_reports/__init__.py | 4 + .../test_export_delta_scan_report.py | 178 ++++++++++++++++++ .../gmpnext/entities/test_scan_reports.py | 7 + 5 files changed, 324 insertions(+) create mode 100644 tests/protocols/gmpnext/entities/scan_reports/test_export_delta_scan_report.py diff --git a/gvm/protocols/gmp/_gmpnext.py b/gvm/protocols/gmp/_gmpnext.py index c9b55dd1..b2a702e9 100644 --- a/gvm/protocols/gmp/_gmpnext.py +++ b/gvm/protocols/gmp/_gmpnext.py @@ -1813,6 +1813,59 @@ def export_scan_report( ) ) + def export_delta_scan_report( + self, + report_id: EntityID, + delta_report_id: EntityID, + format_id: EntityID, + *, + config_id: EntityID | None = None, + filter_string: str | None = None, + ignore_pagination: bool = False, + lean: bool = False, + notes_details: bool = False, + overrides_details: bool = False, + result_tags: bool = False, + ) -> T: + """Request an asynchronous export of a delta scan report. + + If an identical export is already pending or running, the existing + report export is returned instead of creating a duplicate. + + Args: + report_id: UUID of the scan report to export. + delta_report_id: UUID of the report used for delta comparison. + format_id: UUID of the report format to apply. + config_id: UUID of an optional report configuration. + filter_string: Filter term to apply while generating the report. + ignore_pagination: Whether pagination settings in the filter + should be ignored. + lean: Whether lean report data should be generated. + notes_details: Whether note details should be included. + overrides_details: Whether override details should be included. + result_tags: Whether result tags should be included. + + Returns: + A request for the export_delta_scan_report GMP command. + + Raises: + RequiredArgument: If report_id or delta_report_id is not provided. + """ + return self._send_request_and_transform_response( + ScanReports.export_delta_scan_report( + report_id=report_id, + delta_report_id=delta_report_id, + format_id=format_id, + config_id=config_id, + filter_string=filter_string, + ignore_pagination=ignore_pagination, + lean=lean, + notes_details=notes_details, + overrides_details=overrides_details, + result_tags=result_tags, + ) + ) + def export_audit_report( self, report_id: EntityID, diff --git a/gvm/protocols/gmp/requests/next/_scan_report.py b/gvm/protocols/gmp/requests/next/_scan_report.py index 50e4334f..bb0c28f5 100644 --- a/gvm/protocols/gmp/requests/next/_scan_report.py +++ b/gvm/protocols/gmp/requests/next/_scan_report.py @@ -115,3 +115,85 @@ def export_scan_report( ) return cmd + + @classmethod + def export_delta_scan_report( + cls, + report_id: EntityID, + delta_report_id: EntityID, + format_id: EntityID, + *, + config_id: EntityID | None = None, + filter_string: str | None = None, + ignore_pagination: bool = False, + lean: bool = False, + notes_details: bool = False, + overrides_details: bool = False, + result_tags: bool = False, + ) -> Request: + """Request an asynchronous export of a delta scan report. + + If an identical export is already pending or running, the existing + report export is returned instead of creating a duplicate. + + Args: + report_id: UUID of the scan report to export. + delta_report_id: UUID of the report used for delta comparison. + format_id: UUID of the report format to apply. + config_id: UUID of an optional report configuration. + filter_string: Filter term to apply while generating the report. + ignore_pagination: Whether pagination settings in the filter + should be ignored. + lean: Whether lean report data should be generated. + notes_details: Whether note details should be included. + overrides_details: Whether override details should be included. + result_tags: Whether result tags should be included. + + Returns: + A request for the export_delta_scan_report GMP command. + + Raises: + RequiredArgument: If report_id or delta_report_id is not provided. + """ + if not report_id: + raise RequiredArgument( + function=cls.export_delta_scan_report.__name__, + argument="report_id", + ) + + if not delta_report_id: + raise RequiredArgument( + function=cls.export_delta_scan_report.__name__, + argument="delta_report_id", + ) + + cmd = XmlCommand("export_delta_scan_report") + cmd.set_attribute("report_id", str(report_id)) + cmd.set_attribute("delta_report_id", str(delta_report_id)) + cmd.set_attribute("format_id", str(format_id)) + + if config_id: + cmd.set_attribute("config_id", str(config_id)) + + if filter_string is not None: + cmd.set_attribute("filter", filter_string) + + cmd.set_attribute( + "ignore_pagination", + to_bool(ignore_pagination), + ) + cmd.set_attribute("lean", to_bool(lean)) + cmd.set_attribute( + "notes_details", + to_bool(notes_details), + ) + cmd.set_attribute( + "overrides_details", + to_bool(overrides_details), + ) + cmd.set_attribute( + "result_tags", + to_bool(result_tags), + ) + + return cmd diff --git a/tests/protocols/gmpnext/entities/scan_reports/__init__.py b/tests/protocols/gmpnext/entities/scan_reports/__init__.py index de5bb5e4..698b9f54 100644 --- a/tests/protocols/gmpnext/entities/scan_reports/__init__.py +++ b/tests/protocols/gmpnext/entities/scan_reports/__init__.py @@ -3,6 +3,9 @@ # SPDX-License-Identifier: GPL-3.0-or-later # +from .test_export_delta_scan_report import ( + GmpExportDeltaScanReportTestMixin, +) from .test_export_scan_report import ( GmpExportScanReportTestMixin, ) @@ -11,6 +14,7 @@ ) __all__ = ( + "GmpExportDeltaScanReportTestMixin", "GmpExportScanReportTestMixin", "GmpGetScanReportTestMixin", ) diff --git a/tests/protocols/gmpnext/entities/scan_reports/test_export_delta_scan_report.py b/tests/protocols/gmpnext/entities/scan_reports/test_export_delta_scan_report.py new file mode 100644 index 00000000..a89dab08 --- /dev/null +++ b/tests/protocols/gmpnext/entities/scan_reports/test_export_delta_scan_report.py @@ -0,0 +1,178 @@ +# SPDX-FileCopyrightText: 2026 Greenbone AG +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +from gvm.errors import RequiredArgument + + +class GmpExportDeltaScanReportTestMixin: + def test_export_delta_scan_report_without_report_id(self): + with self.assertRaises(RequiredArgument): + self.gmp.export_delta_scan_report( + None, + delta_report_id="d1", + format_id="f1", + ) + + with self.assertRaises(RequiredArgument): + self.gmp.export_delta_scan_report( + "", + delta_report_id="d1", + format_id="f1", + ) + + def test_export_delta_scan_report_without_delta_report_id(self): + with self.assertRaises(RequiredArgument): + self.gmp.export_delta_scan_report( + report_id="r1", + delta_report_id=None, + format_id="f1", + ) + + with self.assertRaises(RequiredArgument): + self.gmp.export_delta_scan_report( + report_id="r1", + delta_report_id="", + format_id="f1", + ) + + def test_export_delta_scan_report(self): + self.gmp.export_delta_scan_report( + report_id="r1", + delta_report_id="d1", + format_id="f1", + ) + + self.connection.send.has_been_called_with( + b'' + ) + + def test_export_delta_scan_report_with_config_id(self): + self.gmp.export_delta_scan_report( + report_id="r1", + delta_report_id="d1", + format_id="f1", + config_id="c1", + ) + + self.connection.send.has_been_called_with( + b'' + ) + + def test_export_delta_scan_report_with_filter_string(self): + self.gmp.export_delta_scan_report( + report_id="r1", + delta_report_id="d1", + format_id="f1", + filter_string="levels=hml", + ) + + self.connection.send.has_been_called_with( + b'' + ) + + def test_export_delta_scan_report_with_ignore_pagination(self): + self.gmp.export_delta_scan_report( + report_id="r1", + delta_report_id="d1", + format_id="f1", + ignore_pagination=True, + ) + + self.connection.send.has_been_called_with( + b'' + ) + + def test_export_delta_scan_report_with_lean(self): + self.gmp.export_delta_scan_report( + report_id="r1", + delta_report_id="d1", + format_id="f1", + lean=True, + ) + + self.connection.send.has_been_called_with( + b'' + ) + + def test_export_delta_scan_report_with_notes_details(self): + self.gmp.export_delta_scan_report( + report_id="r1", + delta_report_id="d1", + format_id="f1", + notes_details=True, + ) + + self.connection.send.has_been_called_with( + b'' + ) + + def test_export_delta_scan_report_with_overrides_details(self): + self.gmp.export_delta_scan_report( + report_id="r1", + delta_report_id="d1", + format_id="f1", + overrides_details=True, + ) + + self.connection.send.has_been_called_with( + b'' + ) + + def test_export_delta_scan_report_with_result_tags(self): + self.gmp.export_delta_scan_report( + report_id="r1", + delta_report_id="d1", + format_id="f1", + result_tags=True, + ) + + self.connection.send.has_been_called_with( + b'' + ) + + def test_export_delta_scan_report_with_all_arguments(self): + self.gmp.export_delta_scan_report( + report_id="r1", + delta_report_id="d1", + format_id="f1", + config_id="c1", + filter_string="levels=hml", + ignore_pagination=True, + lean=True, + notes_details=True, + overrides_details=True, + result_tags=True, + ) + + self.connection.send.has_been_called_with( + b'' + ) diff --git a/tests/protocols/gmpnext/entities/test_scan_reports.py b/tests/protocols/gmpnext/entities/test_scan_reports.py index 9a889296..2383bb88 100644 --- a/tests/protocols/gmpnext/entities/test_scan_reports.py +++ b/tests/protocols/gmpnext/entities/test_scan_reports.py @@ -5,6 +5,7 @@ from ...gmpnext import GMPTestCase from .scan_reports import ( + GmpExportDeltaScanReportTestMixin, GmpExportScanReportTestMixin, GmpGetScanReportTestMixin, ) @@ -16,3 +17,9 @@ class GmpGetScanReportTestCase(GmpGetScanReportTestMixin, GMPTestCase): class GmpExportScanReportTestCase(GmpExportScanReportTestMixin, GMPTestCase): pass + + +class GmpExportDeltaScanReportTestCase( + GmpExportDeltaScanReportTestMixin, GMPTestCase +): + pass