1616
1717from aboutcode .hashid import get_core_purl
1818from aboutcode .pipeline import LoopProgress
19- from fedcode .activitypub import Activity
20- from fedcode .activitypub import UpdateActivity
21- from fedcode .models import Note
2219from fedcode .models import Package
2320from fedcode .models import Repository
2421from fedcode .models import Vulnerability
@@ -97,12 +94,16 @@ def sync_vulnerabilities(repository, logger):
9794 diff .change_type , repository .admin , yaml_data_a_blob , yaml_data_b_blob , logger
9895 )
9996
100- if a_name == "purls.yml" or b_name == "purls.yml" :
97+ elif a_name == "purls.yml" or b_name == "purls.yml" :
10198 pkg_handler (
102- diff .change_type , repository .admin , yaml_data_a_blob , yaml_data_b_blob , logger
99+ diff .change_type ,
100+ repository .admin ,
101+ yaml_data_a_blob ,
102+ yaml_data_b_blob ,
103+ logger ,
103104 )
104105
105- if a_name .startswith ("VCID" ) or b_name .startswith ("VCID" ):
106+ elif a_name .startswith ("VCID" ) or b_name .startswith ("VCID" ):
106107 vul_handler (diff .change_type , repository , yaml_data_a_blob , yaml_data_b_blob , logger )
107108
108109 repository .last_imported_commit = latest_commit_hash
@@ -112,116 +113,130 @@ def sync_vulnerabilities(repository, logger):
112113
113114def vul_handler (change_type , repo_obj , yaml_data_a_blob , yaml_data_b_blob , logger ):
114115 """
115- VCID-XXXX-XXXX-XXXX.yml
116+ Handle changes in VCID-XXXX-XXXX-XXXX.yml
116117 """
117118 vulnerability_a_id = yaml_data_a_blob .get ("vulnerability_id" ) if yaml_data_a_blob else None
118119 vulnerability_b_id = yaml_data_b_blob .get ("vulnerability_id" ) if yaml_data_b_blob else None
119120
120- if change_type == "A" : # A for added paths
121+ if change_type == "A" : # Added
121122 Vulnerability .objects .get_or_create (
122123 id = vulnerability_b_id ,
123124 repo = repo_obj ,
124125 )
125- elif change_type in [
126- "M" ,
127- "R" ,
128- ]: # R for renamed paths , M for paths with modified data
126+
127+ elif change_type in ["M" , "R" ]: # Modified or Renamed
129128 with transaction .atomic ():
130- Vulnerability .objects .get (id = vulnerability_a_id , repo = repo_obj ).delete ()
131- Vulnerability .objects .create (id = vulnerability_b_id , repo = repo_obj )
129+ if vulnerability_a_id and vulnerability_a_id != vulnerability_b_id :
130+ # renamed or id changed
131+ Vulnerability .objects .filter (id = vulnerability_a_id , repo = repo_obj ).delete ()
132+
133+ # update_or_create to avoid losing relations unnecessarily
134+ Vulnerability .objects .update_or_create (
135+ id = vulnerability_b_id ,
136+ repo = repo_obj ,
137+ defaults = {}, # add fields parsed from yaml_data_b_blob here
138+ )
139+
140+ elif change_type == "D" : # Deleted
141+ if vulnerability_a_id :
142+ Vulnerability .objects .filter (id = vulnerability_a_id , repo = repo_obj ).delete ()
132143
133- elif change_type == "D" : # D for deleted paths
134- Vulnerability .objects .get (
135- id = yaml_data_b_blob .get ("vulnerability_id" ),
136- repo = repo_obj ,
137- ).delete ()
138144 else :
139145 logger (f"Invalid Vulnerability File" , level = logging .ERROR )
140146
141147
142148def pkg_handler (change_type , default_service , yaml_data_a_blob , yaml_data_b_blob , logger ):
143149 """
144- purls.yml
150+ Handle changes in purls.yml
145151 """
146152
147- if change_type == "A" :
148- for purl in yaml_data_b_blob :
153+ if change_type == "A" : # Added packages
154+ for purl in yaml_data_b_blob or [] :
149155 core_purl = get_core_purl (purl )
150- pkg , _ = Package .objects .get_or_create (purl = core_purl , service = default_service )
151-
152- # elif change_type == "M":
153- # pkg = Package.objects.get(purl=package_a, service=default_service)
154- # pkg.purl = package_b
155- # pkg.save()
156- #
157- # for version_a, version_b in zip_longest(
158- # yaml_data_a_blob, yaml_data_b_blob
159- # ):
160- # if version_b and not version_a:
161- # utils.create_note(pkg, version_b)
162- #
163- # if version_a and not version_b:
164- # utils.delete_note(pkg, version_a)
165- #
166- # if version_a and version_b:
167- # note = Note.objects.get(acct=pkg.acct, content=saneyaml.dump(version_a))
168- # if note.content == saneyaml.dump(version_b):
169- # continue
170- #
171- # note.content = saneyaml.dump(version_b)
172- # note.save()
173- #
174- # update_activity = UpdateActivity(actor=pkg.to_ap, object=note.to_ap)
175- # Activity.federate(
176- # targets=pkg.followers_inboxes,
177- # body=update_activity.to_ap(),
178- # key_id=pkg.key_id,
179- # )
180- #
181- # elif change_type == "D":
182- # pkg = Package.objects.get(purl=package_a, service=default_service)
183- # for version in yaml_data_a_blob:
184- # utils.delete_note(pkg, version)
185- # pkg.delete()
156+ Package .objects .get_or_create (purl = core_purl , service = default_service )
186157
158+ elif change_type == "M" : # Modified packages
159+ for package_a , package_b in zip_longest (yaml_data_a_blob or [], yaml_data_b_blob or []):
160+ if not package_a or not package_b :
161+ continue # skip if one side missing
187162
188- def note_handler (change_type , default_service , yaml_data_a_blob , yaml_data_b_blob , logger ):
189- """
190- vulnerabilities.yml
191- """
192- if change_type == "A" :
193- for pkg_status in yaml_data_b_blob :
194- purl = pkg_status .get ("purl" )
163+ core_purl_a = get_core_purl (package_a )
164+ core_purl_b = get_core_purl (package_b )
165+
166+ try :
167+ pkg = Package .objects .get (purl = core_purl_a , service = default_service )
168+ except Package .DoesNotExist :
169+ logger (f"Package not found for { core_purl_a } " , level = logging .ERROR )
170+ continue
171+
172+ # Update package purl if changed
173+ if pkg .purl != core_purl_b :
174+ pkg .purl = core_purl_b
175+ pkg .save ()
176+
177+ elif change_type == "D" : # Deleted packages
178+ for purl in yaml_data_a_blob or []:
195179 if not purl :
196- logger (f "Invalid Vulnerability File " , level = logging .ERROR )
197- return
180+ logger ("Invalid PURL in deleted entry " , level = logging .ERROR )
181+ continue
198182 core_purl = get_core_purl (purl )
199- pkg_b , _ = Package .objects .get_or_create (purl = core_purl , service = default_service )
200- temp = saneyaml .dump (pkg_status )
201- utils .create_note (pkg_b , temp )
202-
203- # elif change_type == "M":
204- # for pkg_status_a, pkg_status_b in zip_longest(
205- # yaml_data_a_blob, yaml_data_b_blob
206- # ):
207- # if pkg_status_a and not pkg_status_b:
208- # utils.create_note(pkg_a, pkg_status_b)
209- #
210- # if pkg_status_a and not pkg_status_b:
211- # utils.delete_note(pkg_a, pkg_status_b)
212- #
213- # if pkg_status_a and pkg_status_b:
214- # utils.update_note(pkg_a, saneyaml.dump(pkg_status_a), saneyaml.dump(pkg_status_b))
215- #
216- # elif change_type == "D":
217- # for pkg_status in yaml_data_a_blob:
218- # purl = pkg_status.get("purl")
219- # if not purl:
220- # logger(f"Invalid Vulnerability File", level=logging.ERROR)
221- # return
222- # core_purl = get_core_purl(purl)
223- # pkg_a, _ = Package.objects.get_or_create(purl=core_purl, service=default_service)
224- # temp = saneyaml.dump(pkg_status)
225- # utils.delete_note(pkg_a, temp)
183+ try :
184+ pkg = Package .objects .get (purl = core_purl , service = default_service )
185+ pkg .delete ()
186+ except Package .DoesNotExist :
187+ logger (f"Package not found for deletion: { core_purl } " , level = logging .WARNING )
188+
226189 else :
227- logger (f"Invalid Vulnerability File" , level = logging .ERROR )
190+ logger (f"Unknown change_type: { change_type } " , level = logging .ERROR )
191+
192+
193+ def note_handler (change_type , default_service , yaml_data_a_blob , yaml_data_b_blob , logger ):
194+ """
195+ Handle notes from vulnerabilities.yml changes.
196+ Uses zip_longest so both old (A) and new (B) entries are processed together.
197+ """
198+
199+ for pkg_status_a , pkg_status_b in zip_longest (yaml_data_a_blob or [], yaml_data_b_blob or []):
200+ pkg_a = pkg_b = None
201+
202+ # Resolve old package
203+ if pkg_status_a :
204+ purl_a = pkg_status_a .get ("purl" )
205+ if not purl_a :
206+ logger ("Invalid Vulnerability File: missing purl in old entry" , level = logging .ERROR )
207+ else :
208+ core_purl_a = get_core_purl (purl_a )
209+ pkg_a , _ = Package .objects .get_or_create (purl = core_purl_a , service = default_service )
210+
211+ # Resolve new package
212+ if pkg_status_b :
213+ purl_b = pkg_status_b .get ("purl" )
214+ if not purl_b :
215+ logger ("Invalid Vulnerability File: missing purl in new entry" , level = logging .ERROR )
216+ else :
217+ core_purl_b = get_core_purl (purl_b )
218+ pkg_b , _ = Package .objects .get_or_create (purl = core_purl_b , service = default_service )
219+
220+ if change_type == "A" : # Added entries
221+ if pkg_status_b and pkg_b :
222+ utils .create_note (pkg_b , saneyaml .dump (pkg_status_b ))
223+
224+ elif change_type == "M" : # Modified entries
225+ # Deleted entry
226+ if pkg_status_a and not pkg_status_b and pkg_a :
227+ utils .delete_note (pkg_a , saneyaml .dump (pkg_status_a ))
228+
229+ # Added entry
230+ elif pkg_status_b and not pkg_status_a and pkg_b :
231+ utils .create_note (pkg_b , saneyaml .dump (pkg_status_b ))
232+
233+ # Updated entry
234+ elif pkg_status_a and pkg_status_b and pkg_b :
235+ utils .update_note (pkg_b , saneyaml .dump (pkg_status_a ), saneyaml .dump (pkg_status_b ))
236+
237+ elif change_type == "D" : # Deleted entries
238+ if pkg_status_a and pkg_a :
239+ utils .delete_note (pkg_a , saneyaml .dump (pkg_status_a ))
240+
241+ else :
242+ logger (f"Unknown change_type: { change_type } " , level = logging .ERROR )
0 commit comments