From caba72540b47afb27f0bb294733424e98e85c25d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kalemba?= <5924586+pkalemba@users.noreply.github.com> Date: Mon, 24 Jun 2024 12:31:33 +0000 Subject: [PATCH 1/2] Fix severity when report contains no vulnerabilities --- harborapi/models/scanner.py | 12 ++++++++++-- tests/models/test_scanner.py | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/harborapi/models/scanner.py b/harborapi/models/scanner.py index d11b50b5..f14781ed 100644 --- a/harborapi/models/scanner.py +++ b/harborapi/models/scanner.py @@ -401,7 +401,7 @@ class HarborVulnerabilityReport(BaseModel): default=None, description="The scanner used to generate the report." ) severity: Optional[Severity] = Field( - default=None, description="The overall severity of the vulnerabilities." + default=Severity.unknown, description="The overall severity of the vulnerabilities." ) vulnerabilities: List[VulnerabilityItem] = Field( default_factory=list, description="The list of vulnerabilities found." @@ -410,7 +410,15 @@ class HarborVulnerabilityReport(BaseModel): def __repr__(self) -> str: return f"HarborVulnerabilityReport(generated_at={self.generated_at}, artifact={self.artifact}, scanner={self.scanner}, severity={self.severity}, vulnerabilities=list(len={len(self.vulnerabilities)}))" - + + @field_validator("severity", mode="before") + @classmethod + def _severity_none_is_default( + cls, v: Optional[Severity], info: ValidationInfo + ) -> Severity: + if not info.field_name: + raise ValueError("Validator is not attached to a field.") + return v or cls.model_fields[info.field_name].default @property def fixable(self) -> List[VulnerabilityItem]: return [v for v in self.vulnerabilities if v.fixable] diff --git a/tests/models/test_scanner.py b/tests/models/test_scanner.py index bb8fb380..e2f37fd5 100644 --- a/tests/models/test_scanner.py +++ b/tests/models/test_scanner.py @@ -157,6 +157,21 @@ def test_vulnerability_item_severity_none() -> None: assert v.severity is not None assert v.severity == Severity.unknown +def test_harborvulnerabilityreport_severity_none() -> None: + """Passing None to HarborVulnerabilityReport.severity should assign it Severity.unknown""" + v = HarborVulnerabilityReport( + severity=None, + ) + assert v.severity is not None + assert v.severity == Severity.unknown + +def test_harborvulnerabilityreport_severity_empty_str() -> None: + """Passing empty string to HarborVulnerabilityReport.severity should assign it Severity.unknown""" + v = HarborVulnerabilityReport( + severity="", + ) + assert v.severity is not None + assert v.severity == Severity.unknown @pytest.mark.parametrize("scanner_name", ["Trivy", "Clair"]) def test_vulnerability_item_get_cvss_score(scanner_name: str) -> None: From 575ca9d2db615e46a7b4e31f2e1d7c4dd827859a Mon Sep 17 00:00:00 2001 From: pederhan Date: Mon, 26 Aug 2024 14:18:17 +0200 Subject: [PATCH 2/2] Add validator to code gen fragment --- .../fragments/scanner/harborvulnerabilityreport.py | 11 ++++++++++- harborapi/models/scanner.py | 10 ++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/codegen/ast/fragments/scanner/harborvulnerabilityreport.py b/codegen/ast/fragments/scanner/harborvulnerabilityreport.py index 11342fbc..83d50b36 100644 --- a/codegen/ast/fragments/scanner/harborvulnerabilityreport.py +++ b/codegen/ast/fragments/scanner/harborvulnerabilityreport.py @@ -29,7 +29,7 @@ class HarborVulnerabilityReport(BaseModel): description="The scanner used to generate the report.", ) severity: Optional[Severity] = Field( - default=None, + default=Severity.unknown, description="The overall severity of the vulnerabilities.", ) vulnerabilities: List[VulnerabilityItem] = Field( @@ -37,6 +37,15 @@ class HarborVulnerabilityReport(BaseModel): description="The list of vulnerabilities found.", ) + @field_validator("severity", mode="before") + @classmethod + def _severity_none_is_default( + cls, v: Optional[Severity], info: ValidationInfo + ) -> Severity: + if not info.field_name: + raise ValueError("Validator is not attached to a field.") + return v or cls.model_fields[info.field_name].default + def __repr__(self) -> str: return f"HarborVulnerabilityReport(generated_at={self.generated_at}, artifact={self.artifact}, scanner={self.scanner}, severity={self.severity}, vulnerabilities=list(len={len(self.vulnerabilities)}))" diff --git a/harborapi/models/scanner.py b/harborapi/models/scanner.py index f14781ed..897323dc 100644 --- a/harborapi/models/scanner.py +++ b/harborapi/models/scanner.py @@ -401,16 +401,14 @@ class HarborVulnerabilityReport(BaseModel): default=None, description="The scanner used to generate the report." ) severity: Optional[Severity] = Field( - default=Severity.unknown, description="The overall severity of the vulnerabilities." + default=Severity.unknown, + description="The overall severity of the vulnerabilities.", ) vulnerabilities: List[VulnerabilityItem] = Field( default_factory=list, description="The list of vulnerabilities found." ) model_config = ConfigDict(ignored_types=(cached_property,)) - def __repr__(self) -> str: - return f"HarborVulnerabilityReport(generated_at={self.generated_at}, artifact={self.artifact}, scanner={self.scanner}, severity={self.severity}, vulnerabilities=list(len={len(self.vulnerabilities)}))" - @field_validator("severity", mode="before") @classmethod def _severity_none_is_default( @@ -419,6 +417,10 @@ def _severity_none_is_default( if not info.field_name: raise ValueError("Validator is not attached to a field.") return v or cls.model_fields[info.field_name].default + + def __repr__(self) -> str: + return f"HarborVulnerabilityReport(generated_at={self.generated_at}, artifact={self.artifact}, scanner={self.scanner}, severity={self.severity}, vulnerabilities=list(len={len(self.vulnerabilities)}))" + @property def fixable(self) -> List[VulnerabilityItem]: return [v for v in self.vulnerabilities if v.fixable]