@@ -211,6 +211,49 @@ def check_sha1(codebase):
211211 )
212212
213213
214+ def update_json_package_files (spdx_json ):
215+ """
216+ Ensure SPDX JSON packages list their file members explicitly.
217+ """
218+ packages = spdx_json .get ('packages' ) or []
219+ files = spdx_json .get ('files' ) or []
220+ if not packages or not files :
221+ return spdx_json
222+
223+ relationships = spdx_json .get ('relationships' ) or []
224+ package_file_map = {}
225+ for relationship in relationships :
226+ if relationship .get ('relationshipType' ) != 'CONTAINS' :
227+ continue
228+ package_id = relationship .get ('spdxElementId' )
229+ file_id = relationship .get ('relatedSpdxElement' )
230+ if not package_id or not file_id :
231+ continue
232+ package_file_map .setdefault (package_id , set ()).add (file_id )
233+
234+ if not package_file_map and len (packages ) == 1 :
235+ package_id = packages [0 ].get ('SPDXID' )
236+ if package_id :
237+ file_ids = {f .get ('SPDXID' ) for f in files if f .get ('SPDXID' )}
238+ if file_ids :
239+ package_file_map [package_id ] = file_ids
240+
241+ for package in packages :
242+ package_id = package .get ('SPDXID' )
243+ if not package_id :
244+ continue
245+ file_ids = package_file_map .get (package_id )
246+ if file_ids :
247+ package ['hasFiles' ] = sorted (file_ids )
248+
249+ if not spdx_json .get ('documentDescribes' ):
250+ described = [p .get ('SPDXID' ) for p in packages if p .get ('SPDXID' )]
251+ if described :
252+ spdx_json ['documentDescribes' ] = described
253+
254+ return spdx_json
255+
256+
214257def write_spdx (
215258 codebase ,
216259 output_file ,
@@ -454,6 +497,8 @@ def write_spdx(
454497 result = result .decode ('utf-8' )
455498
456499 if as_json :
457- result = json .dumps (json .loads (result ), indent = 4 , ensure_ascii = False )
500+ spdx_json = json .loads (result )
501+ spdx_json = update_json_package_files (spdx_json )
502+ result = json .dumps (spdx_json , indent = 4 , ensure_ascii = False )
458503
459504 output_file .write (result )
0 commit comments