2020from vulnerabilities .importer import AffectedPackageV2
2121from vulnerabilities .importer import PackageCommitPatchData
2222from vulnerabilities .importer import PatchData
23+ from vulnerabilities .importer import ReferenceV2
24+ from vulnerabilities .models import AdvisoryReference
2325from vulnerabilities .models import AdvisoryV2
2426from vulnerabilities .models import ImpactedPackage
2527from vulnerabilities .models import PackageCommitPatch
2628from vulnerabilities .models import PackageV2
29+ from vulnerabilities .models import Patch
2730from vulnerabilities .pipelines import VulnerableCodeBaseImporterPipelineV2
31+ from vulnerabilities .pipes .advisory import classify_patch_source
2832
2933
3034class DummyImporter (VulnerableCodeBaseImporterPipelineV2 ):
@@ -57,13 +61,13 @@ def dummy_advisory():
5761 introduced_by_commit_patches = [
5862 PackageCommitPatchData (
5963 commit_hash = "9ff29db8ec3adefefce0d37c3c9b5b2c22e59fac" ,
60- vcs_url = "https://github.com/aboutcode-org/vulnerablecode " ,
64+ vcs_url = "https://github.com/abc/def " ,
6165 )
6266 ],
6367 fixed_by_commit_patches = [
6468 PackageCommitPatchData (
6569 commit_hash = "ab99939678dc36b3bee0f366493df1aeef521df4" ,
66- vcs_url = "https://github.com/aboutcode-org/vulnerablecode " ,
70+ vcs_url = "https://github.com/abc/def " ,
6771 )
6872 ],
6973 ),
@@ -74,13 +78,13 @@ def dummy_advisory():
7478 introduced_by_commit_patches = [
7579 PackageCommitPatchData (
7680 commit_hash = "9ff29db8ec3adefefce0d37c3c9b5b2c22e59fac" ,
77- vcs_url = "https://github.com/aboutcode-org/vulnerablecode " ,
81+ vcs_url = "https://github.com/abc/def " ,
7882 )
7983 ],
8084 fixed_by_commit_patches = [
8185 PackageCommitPatchData (
8286 commit_hash = "ab99939678dc36b3bee0f366493df1aeef521df4" ,
83- vcs_url = "https://github.com/aboutcode-org/vulnerablecode " ,
87+ vcs_url = "https://github.com/abc/def " ,
8488 )
8589 ],
8690 ),
@@ -127,3 +131,201 @@ def test_advisory_import_atomicity(dummy_importer):
127131 assert ImpactedPackage .objects .count () == 2
128132 assert PackageCommitPatch .objects .count () == 2
129133 assert PackageV2 .objects .count () == 4
134+
135+
136+ @pytest .fixture
137+ def patch_source_samples ():
138+ return [
139+ {"url" : "https://github.com/abc/def" , "commit_hash" : None , "patch_text" : None }, # PatchData
140+ {
141+ "url" : "https://github.com/abc/def" ,
142+ "commit_hash" : None ,
143+ "patch_text" : "+1-2" ,
144+ }, # PatchData
145+ {
146+ "url" : "https://github.com/abc/def" ,
147+ "commit_hash" : "be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
148+ "patch_text" : None ,
149+ }, # PackageCommitPatchData
150+ {
151+ "url" : "https://github.com/abc/def" ,
152+ "commit_hash" : "be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
153+ "patch_text" : "+1-2" ,
154+ }, # PackageCommitPatchData
155+ {
156+ "url" : "https://github.com/abc/def/commit/be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
157+ "commit_hash" : None ,
158+ "patch_text" : None ,
159+ }, # PackageCommitPatchData
160+ {
161+ "url" : "https://github.com/abc/def/commit/be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
162+ "commit_hash" : None ,
163+ "patch_text" : "+1-2" ,
164+ }, # PackageCommitPatchData
165+ {
166+ "url" : "https://github.com/abc/def/commit/a2a5b42fb829b4a873c832b805680fc19199a07e" ,
167+ "commit_hash" : "a2a5b42fb829b4a873c832b805680fc19199a07e" ,
168+ "patch_text" : None ,
169+ }, # PackageCommitPatchData
170+ {
171+ "url" : "https://github.com/abc/def/commit/a2a5b42fb829b4a873c832b805680fc19199a07e" ,
172+ "commit_hash" : "a2a5b42fb829b4a873c832b805680fc19199a07e" ,
173+ "patch_text" : "+1-2" ,
174+ }, # PackageCommitPatchData
175+ {
176+ "url" : "https://unknown.com/abc/def" ,
177+ "commit_hash" : None ,
178+ "patch_text" : None ,
179+ }, # PatchData
180+ {
181+ "url" : "https://unknown.com/abc/def" ,
182+ "commit_hash" : None ,
183+ "patch_text" : "+1-2" ,
184+ }, # PatchData
185+ {
186+ "url" : "https://unknown.com/abc/def" ,
187+ "commit_hash" : "8eb1b04ca4ae6fc0a0ef46f1b0c042f64db28ff9" ,
188+ "patch_text" : None ,
189+ }, # ReferenceV2
190+ {
191+ "url" : "https://unknown.com/abc/def" ,
192+ "commit_hash" : "8eb1b04ca4ae6fc0a0ef46f1b0c042f64db28ff9" ,
193+ "patch_text" : "+1-2" ,
194+ }, # ReferenceV2
195+ {
196+ "url" : "https://unknown.com/abc/def/be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
197+ "commit_hash" : None ,
198+ "patch_text" : None ,
199+ }, # PatchData
200+ {
201+ "url" : "https://unknown.com/abc/def/be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
202+ "commit_hash" : None ,
203+ "patch_text" : "+1-2" ,
204+ }, # PatchData
205+ {
206+ "url" : "https://unknown.com/abc/def/be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
207+ "commit_hash" : "be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
208+ "patch_text" : None ,
209+ }, # ReferenceV2
210+ {
211+ "url" : "https://unknown.com/abc/def/be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
212+ "commit_hash" : "be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
213+ "patch_text" : "+1-2" ,
214+ }, # ReferenceV2
215+ ]
216+
217+
218+ @pytest .fixture
219+ def dumpy_patch_advisory (patch_source_samples ):
220+ references = []
221+ patches = []
222+ affected_packages = []
223+ for entry in patch_source_samples :
224+ url = entry ["url" ]
225+ commit_hash = entry ["commit_hash" ]
226+ patch_text = entry ["patch_text" ]
227+
228+ base_purl , patch_obj = classify_patch_source (
229+ url = url , commit_hash = commit_hash , patch_text = patch_text
230+ )
231+
232+ if isinstance (patch_obj , PackageCommitPatchData ):
233+ # For testing only: commit hashes starting with "a" are treated as introduced_by_commit_patches,
234+ # all others are treated as fixed_by_commit_patches.
235+ if patch_obj .commit_hash .startswith ("a" ):
236+ affected_package = AffectedPackageV2 (
237+ package = base_purl ,
238+ introduced_by_commit_patches = [patch_obj ],
239+ )
240+ else :
241+ affected_package = AffectedPackageV2 (
242+ package = base_purl ,
243+ fixed_by_commit_patches = [patch_obj ],
244+ )
245+ affected_packages .append (affected_package )
246+ elif isinstance (patch_obj , PatchData ):
247+ patches .append (patch_obj )
248+ elif isinstance (patch_obj , ReferenceV2 ):
249+ references .append (patch_obj )
250+
251+ return AdvisoryData (
252+ summary = "Test patch advisory" ,
253+ aliases = ["CVE-2025-0001" ],
254+ affected_packages = affected_packages ,
255+ references_v2 = references ,
256+ patches = patches ,
257+ advisory_id = "ADV-1234" ,
258+ date_published = datetime .now () - timedelta (days = 10 ),
259+ url = "https://example.com/advisory/1" ,
260+ )
261+
262+
263+ @pytest .mark .django_db
264+ def test_patch_advisory (dumpy_patch_advisory ):
265+ dumpy_patch_importer = DummyImporter ()
266+ dumpy_patch_importer ._advisories = [dumpy_patch_advisory ]
267+ dumpy_patch_importer .collect_and_store_advisories ()
268+ assert AdvisoryV2 .objects .count () == 1
269+ adv = AdvisoryV2 .objects .get (advisory_id = "ADV-1234" )
270+
271+ assert ImpactedPackage .objects .count () == 6
272+ assert [
273+ (
274+ package_commit_patch .commit_hash ,
275+ package_commit_patch .vcs_url ,
276+ package_commit_patch .patch_text ,
277+ package_commit_patch .patch_checksum ,
278+ )
279+ for package_commit_patch in PackageCommitPatch .objects .all ()
280+ ] == [
281+ (
282+ "be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
283+ "https://github.com/abc/def" ,
284+ "+1-2" ,
285+ "a5d6b89c35224d4ed69910a18fb544ca3fb26f62db53bc2769ce8a8d5cf8874c191186d170cb6e8896b0aaa8eaed891e7e819c4c0c7af499397c84761d6fb22d" ,
286+ ),
287+ (
288+ "a2a5b42fb829b4a873c832b805680fc19199a07e" ,
289+ "https://github.com/abc/def" ,
290+ "+1-2" ,
291+ "a5d6b89c35224d4ed69910a18fb544ca3fb26f62db53bc2769ce8a8d5cf8874c191186d170cb6e8896b0aaa8eaed891e7e819c4c0c7af499397c84761d6fb22d" ,
292+ ),
293+ ]
294+ assert (
295+ PackageCommitPatch .objects .count () == 2
296+ ) # Only 2 are created because the 6 inputs include duplicates with the VCS URL and commit_hash
297+ assert Patch .objects .count () == 6
298+ assert [
299+ (patch .patch_text , patch .patch_url , patch .patch_checksum ) for patch in adv .patches .all ()
300+ ] == [
301+ (None , "https://github.com/abc/def" , None ),
302+ (
303+ "+1-2" ,
304+ "https://github.com/abc/def" ,
305+ "a5d6b89c35224d4ed69910a18fb544ca3fb26f62db53bc2769ce8a8d5cf8874c191186d170cb6e8896b0aaa8eaed891e7e819c4c0c7af499397c84761d6fb22d" ,
306+ ),
307+ (None , "https://unknown.com/abc/def" , None ),
308+ (
309+ "+1-2" ,
310+ "https://unknown.com/abc/def" ,
311+ "a5d6b89c35224d4ed69910a18fb544ca3fb26f62db53bc2769ce8a8d5cf8874c191186d170cb6e8896b0aaa8eaed891e7e819c4c0c7af499397c84761d6fb22d" ,
312+ ),
313+ (None , "https://unknown.com/abc/def/be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" , None ),
314+ (
315+ "+1-2" ,
316+ "https://unknown.com/abc/def/be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
317+ "a5d6b89c35224d4ed69910a18fb544ca3fb26f62db53bc2769ce8a8d5cf8874c191186d170cb6e8896b0aaa8eaed891e7e819c4c0c7af499397c84761d6fb22d" ,
318+ ),
319+ ]
320+
321+ assert (
322+ AdvisoryReference .objects .count () == 2
323+ ) # Only 2 are created because the 6 inputs include duplicates with the same URL and reference ID.
324+ assert [(ref .url , ref .reference_type , ref .reference_id ) for ref in adv .references .all ()] == [
325+ ("https://unknown.com/abc/def" , "commit" , "8eb1b04ca4ae6fc0a0ef46f1b0c042f64db28ff9" ),
326+ (
327+ "https://unknown.com/abc/def/be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
328+ "commit" ,
329+ "be891173be2fbdc897116bf5aa4fc9fdc8dc4f3d" ,
330+ ),
331+ ]
0 commit comments