diff --git a/gvm/protocols/gmp/_gmpnext.py b/gvm/protocols/gmp/_gmpnext.py index 33841f61..41261ed3 100644 --- a/gvm/protocols/gmp/_gmpnext.py +++ b/gvm/protocols/gmp/_gmpnext.py @@ -16,6 +16,7 @@ AgentInstallerInstructions, Agents, AliveTest, + AuditReport, Credentials, CredentialStoreCredentialType, CredentialStores, @@ -37,6 +38,7 @@ ) from .requests.v224 import AliveTest as AliveTestV224 from .requests.v224 import HostsOrdering +from .requests.v226 import ReportFormatType class GMPNext(GMPv227[T]): @@ -1671,3 +1673,58 @@ def get_scan_report( filter_id=filter_id, ) ) + + def get_audit_report( # type: ignore[override] + self, + audit_report_id: EntityID, + *, + filter_string: str | None = None, + filter_id: str | None = None, + ) -> T: + """Request a structured summary of a single audit report. + + Args: + audit_report_id: UUID of an existing audit report. + filter_string: Filter term to apply to the report results. + filter_id: UUID of a saved filter to apply to the report results. + + Returns: + A request for the get_audit_report GMP command. + + Raises: + RequiredArgument: If audit_report_id is not provided. + """ + return self._send_request_and_transform_response( + AuditReport.get_audit_report( + audit_report_id=audit_report_id, + filter_string=filter_string, + filter_id=filter_id, + ) + ) + + def get_audit_report_legacy( + self, + report_id: EntityID, + *, + filter_string: str | None = None, + filter_id: str | None = None, + delta_report_id: EntityID | None = None, + report_format_id: str | ReportFormatType | None = None, + ignore_pagination: bool | None = None, + details: bool | None = True, + ) -> T: + """Request an audit report using the legacy command. + + Deprecated: + Use ``get_audit_report`` instead. This method will be removed + in the next major release. + """ + return super().get_audit_report( + report_id, + filter_string=filter_string, + filter_id=filter_id, + delta_report_id=delta_report_id, + report_format_id=report_format_id, + ignore_pagination=ignore_pagination, + details=details, + ) diff --git a/gvm/protocols/gmp/requests/next/__init__.py b/gvm/protocols/gmp/requests/next/__init__.py index e400a361..a4b8f046 100644 --- a/gvm/protocols/gmp/requests/next/__init__.py +++ b/gvm/protocols/gmp/requests/next/__init__.py @@ -8,6 +8,7 @@ AgentInstallerInstructions, ) from gvm.protocols.gmp.requests.next._agents import Agents +from gvm.protocols.gmp.requests.next._audit_report import AuditReport from gvm.protocols.gmp.requests.next._credential_stores import CredentialStores from gvm.protocols.gmp.requests.next._credentials import ( Credentials, @@ -131,6 +132,7 @@ "AlertMethod", "Alerts", "AliveTest", + "AuditReport", "AuditReports", "Audits", "Authentication", diff --git a/gvm/protocols/gmp/requests/next/_audit_report.py b/gvm/protocols/gmp/requests/next/_audit_report.py new file mode 100644 index 00000000..67a92a19 --- /dev/null +++ b/gvm/protocols/gmp/requests/next/_audit_report.py @@ -0,0 +1,39 @@ +from gvm.errors import RequiredArgument +from gvm.protocols.core import Request +from gvm.protocols.gmp.requests import EntityID +from gvm.xml import XmlCommand + + +class AuditReport: + @classmethod + def get_audit_report( + cls, + audit_report_id: EntityID, + *, + filter_string: str | None = None, + filter_id: str | None = None, + ) -> Request: + """Request a structured summary of a single audit report. + + Args: + audit_report_id: UUID of an existing audit report. + filter_string: Filter term to apply to the report results. + filter_id: UUID of a saved filter to apply to the report results. + + Returns: + A request for the get_audit_report GMP command. + + Raises: + RequiredArgument: If audit_report_id is not provided. + """ + if not audit_report_id: + raise RequiredArgument( + function=cls.get_audit_report.__name__, + argument="audit_report_id", + ) + + cmd = XmlCommand("get_audit_report") + cmd.set_attribute("audit_report_id", str(audit_report_id)) + cmd.add_filter(filter_string, filter_id) + + return cmd diff --git a/tests/protocols/gmpnext/entities/audit_report/__init__.py b/tests/protocols/gmpnext/entities/audit_report/__init__.py new file mode 100644 index 00000000..13df4618 --- /dev/null +++ b/tests/protocols/gmpnext/entities/audit_report/__init__.py @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2026 Greenbone AG +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +from .test_get_audit_report import ( + GmpGetAuditReportTestMixin, +) +from .test_get_audit_report_legacy import ( + GmpGetAuditReportLegacyTestMixin, +) + +__all__ = ( + "GmpGetAuditReportLegacyTestMixin", + "GmpGetAuditReportTestMixin", +) diff --git a/tests/protocols/gmpnext/entities/audit_report/test_get_audit_report.py b/tests/protocols/gmpnext/entities/audit_report/test_get_audit_report.py new file mode 100644 index 00000000..03d4bf56 --- /dev/null +++ b/tests/protocols/gmpnext/entities/audit_report/test_get_audit_report.py @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: 2026 Greenbone AG +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +from gvm.errors import RequiredArgument + + +class GmpGetAuditReportTestMixin: + def test_get_audit_report_without_id(self): + with self.assertRaises(RequiredArgument): + self.gmp.get_audit_report(None) + + with self.assertRaises(RequiredArgument): + self.gmp.get_audit_report("") + + def test_get_audit_report_with_id(self): + self.gmp.get_audit_report(audit_report_id="r1") + + self.connection.send.has_been_called_with( + b'' + ) + + def test_get_audit_report_with_filter_string(self): + self.gmp.get_audit_report( + audit_report_id="r1", + filter_string="name=foo", + ) + + self.connection.send.has_been_called_with( + b'' + ) + + def test_get_audit_report_with_filter_id(self): + self.gmp.get_audit_report( + audit_report_id="r1", + filter_id="f1", + ) + + self.connection.send.has_been_called_with( + b'' + ) + + def test_get_audit_report_with_filter_string_and_filter_id(self): + self.gmp.get_audit_report( + audit_report_id="r1", + filter_string="name=foo", + filter_id="f1", + ) + + self.connection.send.has_been_called_with( + b'' + ) diff --git a/tests/protocols/gmpnext/entities/audit_report/test_get_audit_report_legacy.py b/tests/protocols/gmpnext/entities/audit_report/test_get_audit_report_legacy.py new file mode 100644 index 00000000..9ca8e04d --- /dev/null +++ b/tests/protocols/gmpnext/entities/audit_report/test_get_audit_report_legacy.py @@ -0,0 +1,85 @@ +# SPDX-FileCopyrightText: 2026 Greenbone AG +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +from gvm.errors import RequiredArgument +from gvm.protocols.gmp.requests.v226 import ReportFormatType + + +class GmpGetAuditReportLegacyTestMixin: + def test_get_audit_report_legacy_without_id(self): + with self.assertRaises(RequiredArgument): + self.gmp.get_audit_report_legacy(None) + + with self.assertRaises(RequiredArgument): + self.gmp.get_audit_report_legacy("") + + def test_get_audit_report_legacy_with_filter_string(self): + self.gmp.get_audit_report_legacy( + report_id="r1", filter_string="name=foo" + ) + + self.connection.send.has_been_called_with( + b'' + ) + + def test_get_audit_report_legacy_with_filter_id(self): + self.gmp.get_audit_report_legacy(report_id="r1", filter_id="f1") + + self.connection.send.has_been_called_with( + b'' + ) + + def test_get_audit_report_legacy_with_report_format_id(self): + self.gmp.get_audit_report_legacy(report_id="r1", report_format_id="bar") + + self.connection.send.has_been_called_with( + b'' + ) + + def test_get_audit_report_legacy_with_report_format_type(self): + self.gmp.get_audit_report_legacy( + report_id="r1", report_format_id=ReportFormatType.TXT + ) + report_format_id = ReportFormatType.from_string("txt").value + + self.connection.send.has_been_called_with( + ''.encode() + ) + + def test_get_audit_report_legacy_with_delta_report_id(self): + self.gmp.get_audit_report_legacy(report_id="r1", delta_report_id="r2") + + self.connection.send.has_been_called_with( + b'' + ) + + def test_get_audit_report_legacy_with_ignore_pagination(self): + self.gmp.get_audit_report_legacy(report_id="r1", ignore_pagination=True) + + self.connection.send.has_been_called_with( + b'' + ) + + self.gmp.get_audit_report_legacy( + report_id="r1", ignore_pagination=False + ) + + self.connection.send.has_been_called_with( + b'' + ) + + def test_get_audit_report_legacy_with_details(self): + self.gmp.get_audit_report_legacy(report_id="r1", details=True) + + self.connection.send.has_been_called_with( + b'' + ) + + self.gmp.get_audit_report_legacy(report_id="r1", details=False) + + self.connection.send.has_been_called_with( + b'' + ) diff --git a/tests/protocols/gmpnext/entities/test_audit_report.py b/tests/protocols/gmpnext/entities/test_audit_report.py new file mode 100644 index 00000000..b77d3608 --- /dev/null +++ b/tests/protocols/gmpnext/entities/test_audit_report.py @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2026 Greenbone AG +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +from ...gmpnext import GMPTestCase +from ...gmpnext.entities.audit_report import ( + GmpGetAuditReportLegacyTestMixin, + GmpGetAuditReportTestMixin, +) + + +class GmpGetAuditReportTestCase(GmpGetAuditReportTestMixin, GMPTestCase): + pass + + +class GmpGetAuditReportLegacyTestCase( + GmpGetAuditReportLegacyTestMixin, GMPTestCase +): + pass