diff --git a/docs/source/schemas/vulnerablecode-advisory.schema-0.1.json b/docs/source/schemas/vulnerablecode-advisory.schema-0.1.json index 8c26c663a..52280d41f 100644 --- a/docs/source/schemas/vulnerablecode-advisory.schema-0.1.json +++ b/docs/source/schemas/vulnerablecode-advisory.schema-0.1.json @@ -55,7 +55,7 @@ } }, "required": [ - "package" + "purl" ], "title": "Affected Package", "type": "object" @@ -66,14 +66,14 @@ "title": "Version Control Systems URL", "type": "string" }, - "commit_hash": { + "commit": { "title": "Commit Hash", "type": "string" } }, "required": [ "vcs_url", - "commit_hash" + "commit" ], "title": "Package Commit Patch", "type": "object" diff --git a/vulnerabilities/pipes/export.py b/vulnerabilities/pipes/export.py index 506e32b51..ccef8d082 100644 --- a/vulnerabilities/pipes/export.py +++ b/vulnerabilities/pipes/export.py @@ -15,7 +15,7 @@ from vulnerabilities.models import ImpactedPackageFixedBy from vulnerabilities.models import PackageV2 -# Warning! +# Note! # Any changes to the structure of the advisory exporter also require updating the advisory # schema documentation at `docs/source/advisory-schema.rst`. diff --git a/vulnerabilities/tests/pipelines/exporters/test_federate_vulnerabilities.py b/vulnerabilities/tests/pipelines/exporters/test_federate_vulnerabilities.py index e855dd2c0..d418a621f 100644 --- a/vulnerabilities/tests/pipelines/exporters/test_federate_vulnerabilities.py +++ b/vulnerabilities/tests/pipelines/exporters/test_federate_vulnerabilities.py @@ -8,21 +8,28 @@ # +import json +import shutil import tempfile from datetime import datetime from datetime import timedelta from pathlib import Path from unittest.mock import patch +import saneyaml from django.test import TestCase from django.utils import timezone from git import Repo +from jsonschema import Draft7Validator from packageurl import PackageURL from univers.version_range import VersionRange +from vulnerabilities import severity_systems from vulnerabilities.importer import AdvisoryDataV2 from vulnerabilities.importer import AffectedPackageV2 from vulnerabilities.importer import PackageCommitPatchData +from vulnerabilities.importer import ReferenceV2 +from vulnerabilities.importer import VulnerabilitySeverity from vulnerabilities.pipelines import insert_advisory_v2 from vulnerabilities.pipelines.exporters.federate_vulnerabilities import ( FederatePackageVulnerabilities, @@ -34,6 +41,14 @@ Path(__file__).parent.parent.parent / "test_data" / "exporters" / "federate_vulnerabilities" ) +LATEST_FEDERATEDCODE_ADVISORY_SCHEMA = ( + Path(__file__).parent.parent.parent.parent.parent + / "docs" + / "source" + / "schemas" + / "vulnerablecode-advisory.schema-0.1.json" +) + class TestFederatePackageVulnerabilities(TestCase): def setUp(self): @@ -62,9 +77,15 @@ def setUp(self): advisory2 = AdvisoryDataV2( summary="Test advisory2", aliases=["CVE-2025-0002"], - references=[], - severities=[], - weaknesses=[], + references=[ReferenceV2(url="https://example.com/vuln1")], + severities=[ + VulnerabilitySeverity( + system=severity_systems.CVSSV3, + scoring_elements="CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", + value="8.8", + ), + ], + weaknesses=[707, 20], affected_packages=[ AffectedPackageV2( package=PackageURL.from_string("pkg:npm/foobar"), @@ -104,6 +125,12 @@ def setUp(self): a2._all_impacts_unfurled_successfully_at = cur a2.save() + self.working_dir = Path(tempfile.mkdtemp()) + + def tearDown(self): + if self.working_dir: + shutil.rmtree(self.working_dir) + @patch( "vulnerabilities.pipelines.exporters.federate_vulnerabilities.FederatePackageVulnerabilities.clone_federation_repository" ) @@ -114,18 +141,17 @@ def test_vulnerabilities_federation_v2(self, mock_check_fed, mock_commit, mock_c mock_commit.return_value = None mock_clone.__name__ = "clone_federation_repository" - working_dir = Path(tempfile.mkdtemp()) pipeline = FederatePackageVulnerabilities() - pipeline.repo = Repo.init(working_dir) - pipeline.repo_path = working_dir + pipeline.repo = Repo.init(self.working_dir) + pipeline.repo_path = self.working_dir pipeline.log = self.logger.write exit_code, _ = pipeline.execute() self.assertEqual(exit_code, 0) - result_advisories_yml = next(working_dir.rglob("1.2.4/advisories.yml")) - result_advisory1_yml = next(working_dir.rglob("ADV-001.yml")) - result_advisory2_yml = next(working_dir.rglob("ADV-002.yml")) + result_advisories_yml = next(self.working_dir.rglob("1.2.4/advisories.yml")) + result_advisory1_yml = next(self.working_dir.rglob("ADV-001.yml")) + result_advisory2_yml = next(self.working_dir.rglob("ADV-002.yml")) expected_advisories_yml = TEST_DATA / "1.2.4" / "advisories-expected.yml" expected_advisory1_yml = TEST_DATA / "ADV-001-expected.yml" @@ -134,3 +160,34 @@ def test_vulnerabilities_federation_v2(self, mock_check_fed, mock_commit, mock_c util_tests.check_results_and_expected_files(result_advisories_yml, expected_advisories_yml) util_tests.check_results_and_expected_files(result_advisory1_yml, expected_advisory1_yml) util_tests.check_results_and_expected_files(result_advisory2_yml, expected_advisory2_yml) + + @patch( + "vulnerabilities.pipelines.exporters.federate_vulnerabilities.FederatePackageVulnerabilities.clone_federation_repository" + ) + @patch("vulnerabilities.pipes.federatedcode.commit_and_push_changes") + @patch("vulnerabilities.pipes.federatedcode.check_federatedcode_configured_and_available") + def test_vulnerabilities_federation_schema(self, mock_check_fed, mock_commit, mock_clone): + mock_check_fed.return_value = None + mock_commit.return_value = None + mock_clone.__name__ = "clone_federation_repository" + + pipeline = FederatePackageVulnerabilities() + pipeline.repo = Repo.init(self.working_dir) + pipeline.repo_path = self.working_dir + pipeline.log = self.logger.write + exit_code, _ = pipeline.execute() + + self.assertEqual(exit_code, 0) + + with LATEST_FEDERATEDCODE_ADVISORY_SCHEMA.open("r", encoding="utf-8") as f: + validator = Draft7Validator(json.load(f)) + + result_advisory1_yml = saneyaml.load( + next(self.working_dir.rglob("ADV-001.yml")).read_text(encoding="utf-8") + ) + result_advisory2_yml = saneyaml.load( + next(self.working_dir.rglob("ADV-002.yml")).read_text(encoding="utf-8") + ) + + validator.validate(result_advisory1_yml) + validator.validate(result_advisory2_yml) diff --git a/vulnerabilities/tests/test_data/exporters/federate_vulnerabilities/ADV-002-expected.yml b/vulnerabilities/tests/test_data/exporters/federate_vulnerabilities/ADV-002-expected.yml index 79903f2dc..14fa46564 100644 --- a/vulnerabilities/tests/test_data/exporters/federate_vulnerabilities/ADV-002-expected.yml +++ b/vulnerabilities/tests/test_data/exporters/federate_vulnerabilities/ADV-002-expected.yml @@ -12,6 +12,16 @@ impacted_packages: - vcs_url: https://foobar.vcs/ commit: 982f801f introduced_in_commits: [] -severities: [] -weaknesses: [] -references: [] +severities: + - score: '8.8' + scoring_system: cvssv3 + scoring_elements: CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H + published_at: + url: +weaknesses: + - CWE-707 + - CWE-20 +references: + - url: https://example.com/vuln1 + reference_type: + reference_id: