88#
99
1010
11+ import json
12+ import shutil
1113import tempfile
1214from datetime import datetime
1315from datetime import timedelta
1416from pathlib import Path
1517from unittest .mock import patch
1618
19+ import saneyaml
1720from django .test import TestCase
1821from django .utils import timezone
1922from git import Repo
23+ from jsonschema import Draft7Validator
2024from packageurl import PackageURL
2125from univers .version_range import VersionRange
2226
27+ from vulnerabilities import severity_systems
2328from vulnerabilities .importer import AdvisoryDataV2
2429from vulnerabilities .importer import AffectedPackageV2
2530from vulnerabilities .importer import PackageCommitPatchData
31+ from vulnerabilities .importer import ReferenceV2
32+ from vulnerabilities .importer import VulnerabilitySeverity
2633from vulnerabilities .pipelines import insert_advisory_v2
2734from vulnerabilities .pipelines .exporters .federate_vulnerabilities import (
2835 FederatePackageVulnerabilities ,
3441 Path (__file__ ).parent .parent .parent / "test_data" / "exporters" / "federate_vulnerabilities"
3542)
3643
44+ LATEST_FEDERATEDCODE_ADVISORY_SCHEMA = (
45+ Path (__file__ ).parent .parent .parent .parent .parent
46+ / "docs"
47+ / "source"
48+ / "schemas"
49+ / "vulnerablecode-advisory.schema-0.1.json"
50+ )
51+
3752
3853class TestFederatePackageVulnerabilities (TestCase ):
3954 def setUp (self ):
@@ -62,9 +77,15 @@ def setUp(self):
6277 advisory2 = AdvisoryDataV2 (
6378 summary = "Test advisory2" ,
6479 aliases = ["CVE-2025-0002" ],
65- references = [],
66- severities = [],
67- weaknesses = [],
80+ references = [ReferenceV2 (url = "https://example.com/vuln1" )],
81+ severities = [
82+ VulnerabilitySeverity (
83+ system = severity_systems .CVSSV3 ,
84+ scoring_elements = "CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H" ,
85+ value = "8.8" ,
86+ ),
87+ ],
88+ weaknesses = [707 , 20 ],
6889 affected_packages = [
6990 AffectedPackageV2 (
7091 package = PackageURL .from_string ("pkg:npm/foobar" ),
@@ -104,6 +125,12 @@ def setUp(self):
104125 a2 ._all_impacts_unfurled_successfully_at = cur
105126 a2 .save ()
106127
128+ self .working_dir = Path (tempfile .mkdtemp ())
129+
130+ def tearDown (self ):
131+ if self .working_dir :
132+ shutil .rmtree (self .working_dir )
133+
107134 @patch (
108135 "vulnerabilities.pipelines.exporters.federate_vulnerabilities.FederatePackageVulnerabilities.clone_federation_repository"
109136 )
@@ -114,23 +141,57 @@ def test_vulnerabilities_federation_v2(self, mock_check_fed, mock_commit, mock_c
114141 mock_commit .return_value = None
115142 mock_clone .__name__ = "clone_federation_repository"
116143
117- working_dir = Path (tempfile .mkdtemp ())
118144 pipeline = FederatePackageVulnerabilities ()
119- pipeline .repo = Repo .init (working_dir )
120- pipeline .repo_path = working_dir
145+ pipeline .repo = Repo .init (self . working_dir )
146+ pipeline .repo_path = self . working_dir
121147 pipeline .log = self .logger .write
122148 exit_code , _ = pipeline .execute ()
123149
124150 self .assertEqual (exit_code , 0 )
125151
126- result_advisories_yml = next (working_dir .rglob ("1.2.4/advisories.yml" ))
127- result_advisory1_yml = next (working_dir .rglob ("ADV-001.yml" ))
128- result_advisory2_yml = next (working_dir .rglob ("ADV-002.yml" ))
152+ result_advisories_yml = next (self . working_dir .rglob ("1.2.4/advisories.yml" ))
153+ result_advisory1_yml = next (self . working_dir .rglob ("ADV-001.yml" ))
154+ result_advisory2_yml = next (self . working_dir .rglob ("ADV-002.yml" ))
129155
130156 expected_advisories_yml = TEST_DATA / "1.2.4" / "advisories-expected.yml"
131157 expected_advisory1_yml = TEST_DATA / "ADV-001-expected.yml"
132158 expected_advisory2_yml = TEST_DATA / "ADV-002-expected.yml"
133159
134160 util_tests .check_results_and_expected_files (result_advisories_yml , expected_advisories_yml )
135- util_tests .check_results_and_expected_files (result_advisory1_yml , expected_advisory1_yml )
136- util_tests .check_results_and_expected_files (result_advisory2_yml , expected_advisory2_yml )
161+ util_tests .check_results_and_expected_files (
162+ result_advisory1_yml , expected_advisory1_yml , regen = True
163+ )
164+ util_tests .check_results_and_expected_files (
165+ result_advisory2_yml , expected_advisory2_yml , regen = True
166+ )
167+
168+ @patch (
169+ "vulnerabilities.pipelines.exporters.federate_vulnerabilities.FederatePackageVulnerabilities.clone_federation_repository"
170+ )
171+ @patch ("vulnerabilities.pipes.federatedcode.commit_and_push_changes" )
172+ @patch ("vulnerabilities.pipes.federatedcode.check_federatedcode_configured_and_available" )
173+ def test_vulnerabilities_federation_schema (self , mock_check_fed , mock_commit , mock_clone ):
174+ mock_check_fed .return_value = None
175+ mock_commit .return_value = None
176+ mock_clone .__name__ = "clone_federation_repository"
177+
178+ pipeline = FederatePackageVulnerabilities ()
179+ pipeline .repo = Repo .init (self .working_dir )
180+ pipeline .repo_path = self .working_dir
181+ pipeline .log = self .logger .write
182+ exit_code , _ = pipeline .execute ()
183+
184+ self .assertEqual (exit_code , 0 )
185+
186+ with LATEST_FEDERATEDCODE_ADVISORY_SCHEMA .open ("r" , encoding = "utf-8" ) as f :
187+ validator = Draft7Validator (json .load (f ))
188+
189+ result_advisory1_yml = saneyaml .load (
190+ next (self .working_dir .rglob ("ADV-001.yml" )).read_text (encoding = "utf-8" )
191+ )
192+ result_advisory2_yml = saneyaml .load (
193+ next (self .working_dir .rglob ("ADV-002.yml" )).read_text (encoding = "utf-8" )
194+ )
195+
196+ validator .validate (result_advisory1_yml )
197+ validator .validate (result_advisory2_yml )
0 commit comments