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,18 +141,17 @@ 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"
@@ -134,3 +160,34 @@ def test_vulnerabilities_federation_v2(self, mock_check_fed, mock_commit, mock_c
134160 util_tests .check_results_and_expected_files (result_advisories_yml , expected_advisories_yml )
135161 util_tests .check_results_and_expected_files (result_advisory1_yml , expected_advisory1_yml )
136162 util_tests .check_results_and_expected_files (result_advisory2_yml , expected_advisory2_yml )
163+
164+ @patch (
165+ "vulnerabilities.pipelines.exporters.federate_vulnerabilities.FederatePackageVulnerabilities.clone_federation_repository"
166+ )
167+ @patch ("vulnerabilities.pipes.federatedcode.commit_and_push_changes" )
168+ @patch ("vulnerabilities.pipes.federatedcode.check_federatedcode_configured_and_available" )
169+ def test_vulnerabilities_federation_schema (self , mock_check_fed , mock_commit , mock_clone ):
170+ mock_check_fed .return_value = None
171+ mock_commit .return_value = None
172+ mock_clone .__name__ = "clone_federation_repository"
173+
174+ pipeline = FederatePackageVulnerabilities ()
175+ pipeline .repo = Repo .init (self .working_dir )
176+ pipeline .repo_path = self .working_dir
177+ pipeline .log = self .logger .write
178+ exit_code , _ = pipeline .execute ()
179+
180+ self .assertEqual (exit_code , 0 )
181+
182+ with LATEST_FEDERATEDCODE_ADVISORY_SCHEMA .open ("r" , encoding = "utf-8" ) as f :
183+ validator = Draft7Validator (json .load (f ))
184+
185+ result_advisory1_yml = saneyaml .load (
186+ next (self .working_dir .rglob ("ADV-001.yml" )).read_text (encoding = "utf-8" )
187+ )
188+ result_advisory2_yml = saneyaml .load (
189+ next (self .working_dir .rglob ("ADV-002.yml" )).read_text (encoding = "utf-8" )
190+ )
191+
192+ validator .validate (result_advisory1_yml )
193+ validator .validate (result_advisory2_yml )
0 commit comments