blob: 4d090ebf94d921ed67e3fc50f8af35eb243d5dfe [file] [log] [blame]
Wei Lidec97b12023-04-07 16:45:17 -07001#!/usr/bin/env python3
2#
3# Copyright (C) 2023 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Generate the SBOM of the current target product in SPDX format.
19Usage example:
20 generate-sbom.py --output_file out/target/product/vsoc_x86_64/sbom.spdx \
21 --metadata out/target/product/vsoc_x86_64/sbom-metadata.csv \
Wei Lidec97b12023-04-07 16:45:17 -070022 --build_version $(cat out/target/product/vsoc_x86_64/build_fingerprint.txt) \
23 --product_mfr=Google
24"""
25
26import argparse
27import csv
28import datetime
29import google.protobuf.text_format as text_format
30import hashlib
31import os
32import metadata_file_pb2
33import sbom_data
34import sbom_writers
35
36
37# Package type
38PKG_SOURCE = 'SOURCE'
39PKG_UPSTREAM = 'UPSTREAM'
40PKG_PREBUILT = 'PREBUILT'
41
42# Security tag
43NVD_CPE23 = 'NVD-CPE2.3:'
44
45# Report
46ISSUE_NO_METADATA = 'No metadata generated in Make for installed files:'
47ISSUE_NO_METADATA_FILE = 'No METADATA file found for installed file:'
48ISSUE_METADATA_FILE_INCOMPLETE = 'METADATA file incomplete:'
49ISSUE_UNKNOWN_SECURITY_TAG_TYPE = 'Unknown security tag type:'
50ISSUE_INSTALLED_FILE_NOT_EXIST = 'Non-exist installed files:'
51INFO_METADATA_FOUND_FOR_PACKAGE = 'METADATA file found for packages:'
52
Wei Li6f407ba2023-04-19 12:39:07 -070053SOONG_PREBUILT_MODULE_TYPES = [
54 'android_app_import',
55 'android_library_import',
56 'cc_prebuilt_binary',
57 'cc_prebuilt_library',
58 'cc_prebuilt_library_headers',
59 'cc_prebuilt_library_shared',
60 'cc_prebuilt_library_static',
61 'cc_prebuilt_object',
62 'dex_import',
63 'java_import',
64 'java_sdk_library_import',
65 'java_system_modules_import',
66 'libclang_rt_prebuilt_library_static',
67 'libclang_rt_prebuilt_library_shared',
68 'llvm_prebuilt_library_static',
69 'ndk_prebuilt_object',
70 'ndk_prebuilt_shared_stl',
71 'nkd_prebuilt_static_stl',
72 'prebuilt_apex',
73 'prebuilt_bootclasspath_fragment',
74 'prebuilt_dsp',
75 'prebuilt_firmware',
76 'prebuilt_kernel_modules',
77 'prebuilt_rfsa',
78 'prebuilt_root',
79 'rust_prebuilt_dylib',
80 'rust_prebuilt_library',
81 'rust_prebuilt_rlib',
82 'vndk_prebuilt_shared',
83]
84
Wei Lidec97b12023-04-07 16:45:17 -070085
86def get_args():
87 parser = argparse.ArgumentParser()
88 parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Print more information.')
89 parser.add_argument('--output_file', required=True, help='The generated SBOM file in SPDX format.')
90 parser.add_argument('--metadata', required=True, help='The SBOM metadata file path.')
Wei Lidec97b12023-04-07 16:45:17 -070091 parser.add_argument('--build_version', required=True, help='The build version.')
92 parser.add_argument('--product_mfr', required=True, help='The product manufacturer.')
93 parser.add_argument('--json', action='store_true', default=False, help='Generated SBOM file in SPDX JSON format')
Wei Lifd7e6512023-05-05 10:49:28 -070094 parser.add_argument('--unbundled_apk', action='store_true', default=False, help='Generate SBOM for unbundled APKs')
95 parser.add_argument('--unbundled_apex', action='store_true', default=False, help='Generate SBOM for unbundled APEXs')
Wei Lidec97b12023-04-07 16:45:17 -070096
97 return parser.parse_args()
98
99
100def log(*info):
101 if args.verbose:
102 for i in info:
103 print(i)
104
105
Wei Lidec97b12023-04-07 16:45:17 -0700106def new_package_id(package_name, type):
Wei Lic134b762023-10-17 23:52:30 -0700107 return f'SPDXRef-{type}-{sbom_data.encode_for_spdxid(package_name)}'
Wei Lidec97b12023-04-07 16:45:17 -0700108
109
110def new_file_id(file_path):
Wei Lic134b762023-10-17 23:52:30 -0700111 return f'SPDXRef-{sbom_data.encode_for_spdxid(file_path)}'
Wei Lidec97b12023-04-07 16:45:17 -0700112
113
114def checksum(file_path):
Wei Lidec97b12023-04-07 16:45:17 -0700115 h = hashlib.sha1()
116 if os.path.islink(file_path):
117 h.update(os.readlink(file_path).encode('utf-8'))
118 else:
119 with open(file_path, 'rb') as f:
120 h.update(f.read())
121 return f'SHA1: {h.hexdigest()}'
122
123
124def is_soong_prebuilt_module(file_metadata):
Wei Li6f407ba2023-04-19 12:39:07 -0700125 return (file_metadata['soong_module_type'] and
126 file_metadata['soong_module_type'] in SOONG_PREBUILT_MODULE_TYPES)
Wei Lidec97b12023-04-07 16:45:17 -0700127
128
129def is_source_package(file_metadata):
130 module_path = file_metadata['module_path']
131 return module_path.startswith('external/') and not is_prebuilt_package(file_metadata)
132
133
134def is_prebuilt_package(file_metadata):
135 module_path = file_metadata['module_path']
136 if module_path:
137 return (module_path.startswith('prebuilts/') or
138 is_soong_prebuilt_module(file_metadata) or
139 file_metadata['is_prebuilt_make_module'])
140
141 kernel_module_copy_files = file_metadata['kernel_module_copy_files']
142 if kernel_module_copy_files and not kernel_module_copy_files.startswith('ANDROID-GEN:'):
143 return True
144
145 return False
146
147
148def get_source_package_info(file_metadata, metadata_file_path):
149 """Return source package info exists in its METADATA file, currently including name, security tag
150 and external SBOM reference.
151
152 See go/android-spdx and go/android-sbom-gen for more details.
153 """
154 if not metadata_file_path:
155 return file_metadata['module_path'], []
156
157 metadata_proto = metadata_file_protos[metadata_file_path]
158 external_refs = []
159 for tag in metadata_proto.third_party.security.tag:
160 if tag.lower().startswith((NVD_CPE23 + 'cpe:2.3:').lower()):
161 external_refs.append(
162 sbom_data.PackageExternalRef(category=sbom_data.PackageExternalRefCategory.SECURITY,
163 type=sbom_data.PackageExternalRefType.cpe23Type,
164 locator=tag.removeprefix(NVD_CPE23)))
165 elif tag.lower().startswith((NVD_CPE23 + 'cpe:/').lower()):
166 external_refs.append(
167 sbom_data.PackageExternalRef(category=sbom_data.PackageExternalRefCategory.SECURITY,
168 type=sbom_data.PackageExternalRefType.cpe22Type,
169 locator=tag.removeprefix(NVD_CPE23)))
170
171 if metadata_proto.name:
172 return metadata_proto.name, external_refs
173 else:
174 return os.path.basename(metadata_file_path), external_refs # return the directory name only as package name
175
176
177def get_prebuilt_package_name(file_metadata, metadata_file_path):
178 """Return name of a prebuilt package, which can be from the METADATA file, metadata file path,
179 module path or kernel module's source path if the installed file is a kernel module.
180
181 See go/android-spdx and go/android-sbom-gen for more details.
182 """
183 name = None
184 if metadata_file_path:
185 metadata_proto = metadata_file_protos[metadata_file_path]
186 if metadata_proto.name:
187 name = metadata_proto.name
188 else:
189 name = metadata_file_path
190 elif file_metadata['module_path']:
191 name = file_metadata['module_path']
192 elif file_metadata['kernel_module_copy_files']:
193 src_path = file_metadata['kernel_module_copy_files'].split(':')[0]
194 name = os.path.dirname(src_path)
195
196 return name.removeprefix('prebuilts/').replace('/', '-')
197
198
199def get_metadata_file_path(file_metadata):
200 """Search for METADATA file of a package and return its path."""
201 metadata_path = ''
202 if file_metadata['module_path']:
203 metadata_path = file_metadata['module_path']
204 elif file_metadata['kernel_module_copy_files']:
205 metadata_path = os.path.dirname(file_metadata['kernel_module_copy_files'].split(':')[0])
206
207 while metadata_path and not os.path.exists(metadata_path + '/METADATA'):
208 metadata_path = os.path.dirname(metadata_path)
209
210 return metadata_path
211
212
213def get_package_version(metadata_file_path):
214 """Return a package's version in its METADATA file."""
215 if not metadata_file_path:
216 return None
217 metadata_proto = metadata_file_protos[metadata_file_path]
218 return metadata_proto.third_party.version
219
220
221def get_package_homepage(metadata_file_path):
222 """Return a package's homepage URL in its METADATA file."""
223 if not metadata_file_path:
224 return None
225 metadata_proto = metadata_file_protos[metadata_file_path]
226 if metadata_proto.third_party.homepage:
227 return metadata_proto.third_party.homepage
228 for url in metadata_proto.third_party.url:
229 if url.type == metadata_file_pb2.URL.Type.HOMEPAGE:
230 return url.value
231
232 return None
233
234
235def get_package_download_location(metadata_file_path):
236 """Return a package's code repository URL in its METADATA file."""
237 if not metadata_file_path:
238 return None
239 metadata_proto = metadata_file_protos[metadata_file_path]
240 if metadata_proto.third_party.url:
241 urls = sorted(metadata_proto.third_party.url, key=lambda url: url.type)
242 if urls[0].type != metadata_file_pb2.URL.Type.HOMEPAGE:
243 return urls[0].value
244 elif len(urls) > 1:
245 return urls[1].value
246
247 return None
248
249
250def get_sbom_fragments(installed_file_metadata, metadata_file_path):
251 """Return SPDX fragment of source/prebuilt packages, which usually contains a SOURCE/PREBUILT
Wei Li16e7aa32023-05-15 15:11:43 -0700252 package, a UPSTREAM package and an external SBOM document reference if sbom_ref defined in its
253 METADATA file.
Wei Lidec97b12023-04-07 16:45:17 -0700254
255 See go/android-spdx and go/android-sbom-gen for more details.
256 """
257 external_doc_ref = None
258 packages = []
259 relationships = []
260
261 # Info from METADATA file
262 homepage = get_package_homepage(metadata_file_path)
263 version = get_package_version(metadata_file_path)
264 download_location = get_package_download_location(metadata_file_path)
265
266 if is_source_package(installed_file_metadata):
267 # Source fork packages
268 name, external_refs = get_source_package_info(installed_file_metadata, metadata_file_path)
269 source_package_id = new_package_id(name, PKG_SOURCE)
270 source_package = sbom_data.Package(id=source_package_id, name=name, version=args.build_version,
Wei Li52908252023-04-14 18:49:42 -0700271 download_location=sbom_data.VALUE_NONE,
Wei Lidec97b12023-04-07 16:45:17 -0700272 supplier='Organization: ' + args.product_mfr,
273 external_refs=external_refs)
274
275 upstream_package_id = new_package_id(name, PKG_UPSTREAM)
276 upstream_package = sbom_data.Package(id=upstream_package_id, name=name, version=version,
Wei Li52908252023-04-14 18:49:42 -0700277 supplier=('Organization: ' + homepage) if homepage else sbom_data.VALUE_NOASSERTION,
Wei Lidec97b12023-04-07 16:45:17 -0700278 download_location=download_location)
279 packages += [source_package, upstream_package]
280 relationships.append(sbom_data.Relationship(id1=source_package_id,
281 relationship=sbom_data.RelationshipType.VARIANT_OF,
282 id2=upstream_package_id))
283 elif is_prebuilt_package(installed_file_metadata):
284 # Prebuilt fork packages
285 name = get_prebuilt_package_name(installed_file_metadata, metadata_file_path)
286 prebuilt_package_id = new_package_id(name, PKG_PREBUILT)
287 prebuilt_package = sbom_data.Package(id=prebuilt_package_id,
288 name=name,
Wei Li52908252023-04-14 18:49:42 -0700289 download_location=sbom_data.VALUE_NONE,
Wei Li16e7aa32023-05-15 15:11:43 -0700290 version=version if version else args.build_version,
Wei Lidec97b12023-04-07 16:45:17 -0700291 supplier='Organization: ' + args.product_mfr)
Wei Lidec97b12023-04-07 16:45:17 -0700292
Wei Li16e7aa32023-05-15 15:11:43 -0700293 upstream_package_id = new_package_id(name, PKG_UPSTREAM)
294 upstream_package = sbom_data.Package(id=upstream_package_id, name=name, version = version,
295 supplier=('Organization: ' + homepage) if homepage else sbom_data.VALUE_NOASSERTION,
296 download_location=download_location)
297 packages += [prebuilt_package, upstream_package]
298 relationships.append(sbom_data.Relationship(id1=prebuilt_package_id,
299 relationship=sbom_data.RelationshipType.VARIANT_OF,
300 id2=upstream_package_id))
301
302 if metadata_file_path:
303 metadata_proto = metadata_file_protos[metadata_file_path]
304 if metadata_proto.third_party.WhichOneof('sbom') == 'sbom_ref':
305 sbom_url = metadata_proto.third_party.sbom_ref.url
306 sbom_checksum = metadata_proto.third_party.sbom_ref.checksum
307 upstream_element_id = metadata_proto.third_party.sbom_ref.element_id
308 if sbom_url and sbom_checksum and upstream_element_id:
309 doc_ref_id = f'DocumentRef-{PKG_UPSTREAM}-{encode_for_spdxid(name)}'
310 external_doc_ref = sbom_data.DocumentExternalReference(id=doc_ref_id,
311 uri=sbom_url,
312 checksum=sbom_checksum)
313 relationships.append(
314 sbom_data.Relationship(id1=upstream_package_id,
315 relationship=sbom_data.RelationshipType.VARIANT_OF,
316 id2=doc_ref_id + ':' + upstream_element_id))
Wei Lidec97b12023-04-07 16:45:17 -0700317
318 return external_doc_ref, packages, relationships
319
320
Wei Lifd7e6512023-05-05 10:49:28 -0700321def save_report(report_file_path, report):
322 with open(report_file_path, 'w', encoding='utf-8') as report_file:
Wei Lidec97b12023-04-07 16:45:17 -0700323 for type, issues in report.items():
324 report_file.write(type + '\n')
325 for issue in issues:
326 report_file.write('\t' + issue + '\n')
327 report_file.write('\n')
328
329
330# Validate the metadata generated by Make for installed files and report if there is no metadata.
331def installed_file_has_metadata(installed_file_metadata, report):
332 installed_file = installed_file_metadata['installed_file']
333 module_path = installed_file_metadata['module_path']
334 product_copy_files = installed_file_metadata['product_copy_files']
335 kernel_module_copy_files = installed_file_metadata['kernel_module_copy_files']
336 is_platform_generated = installed_file_metadata['is_platform_generated']
337
338 if (not module_path and
339 not product_copy_files and
340 not kernel_module_copy_files and
341 not is_platform_generated and
342 not installed_file.endswith('.fsv_meta')):
343 report[ISSUE_NO_METADATA].append(installed_file)
344 return False
345
346 return True
347
348
349def report_metadata_file(metadata_file_path, installed_file_metadata, report):
350 if metadata_file_path:
351 report[INFO_METADATA_FOUND_FOR_PACKAGE].append(
352 'installed_file: {}, module_path: {}, METADATA file: {}'.format(
353 installed_file_metadata['installed_file'],
354 installed_file_metadata['module_path'],
355 metadata_file_path + '/METADATA'))
356
357 package_metadata = metadata_file_pb2.Metadata()
358 with open(metadata_file_path + '/METADATA', 'rt') as f:
359 text_format.Parse(f.read(), package_metadata)
360
361 if not metadata_file_path in metadata_file_protos:
362 metadata_file_protos[metadata_file_path] = package_metadata
363 if not package_metadata.name:
364 report[ISSUE_METADATA_FILE_INCOMPLETE].append(f'{metadata_file_path}/METADATA does not has "name"')
365
366 if not package_metadata.third_party.version:
367 report[ISSUE_METADATA_FILE_INCOMPLETE].append(
368 f'{metadata_file_path}/METADATA does not has "third_party.version"')
369
370 for tag in package_metadata.third_party.security.tag:
371 if not tag.startswith(NVD_CPE23):
372 report[ISSUE_UNKNOWN_SECURITY_TAG_TYPE].append(
373 f'Unknown security tag type: {tag} in {metadata_file_path}/METADATA')
374 else:
375 report[ISSUE_NO_METADATA_FILE].append(
376 "installed_file: {}, module_path: {}".format(
377 installed_file_metadata['installed_file'], installed_file_metadata['module_path']))
378
379
Wei Lifd7e6512023-05-05 10:49:28 -0700380def generate_sbom_for_unbundled_apk():
Wei Lidec97b12023-04-07 16:45:17 -0700381 with open(args.metadata, newline='') as sbom_metadata_file:
382 reader = csv.DictReader(sbom_metadata_file)
383 doc = sbom_data.Document(name=args.build_version,
384 namespace=f'https://www.google.com/sbom/spdx/android/{args.build_version}',
385 creators=['Organization: ' + args.product_mfr])
386 for installed_file_metadata in reader:
387 installed_file = installed_file_metadata['installed_file']
Wei Lifd7e6512023-05-05 10:49:28 -0700388 if args.output_file != installed_file_metadata['build_output_path'] + '.spdx.json':
Wei Lidec97b12023-04-07 16:45:17 -0700389 continue
390
391 module_path = installed_file_metadata['module_path']
392 package_id = new_package_id(module_path, PKG_PREBUILT)
393 package = sbom_data.Package(id=package_id,
394 name=module_path,
395 version=args.build_version,
396 supplier='Organization: ' + args.product_mfr)
397 file_id = new_file_id(installed_file)
Wei Lifd7e6512023-05-05 10:49:28 -0700398 file = sbom_data.File(id=file_id,
399 name=installed_file,
400 checksum=checksum(installed_file_metadata['build_output_path']))
Wei Lidec97b12023-04-07 16:45:17 -0700401 relationship = sbom_data.Relationship(id1=file_id,
402 relationship=sbom_data.RelationshipType.GENERATED_FROM,
403 id2=package_id)
404 doc.add_package(package)
405 doc.files.append(file)
406 doc.describes = file_id
407 doc.add_relationship(relationship)
408 doc.created = datetime.datetime.now(tz=datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
409 break
410
Wei Li49af9392023-04-12 17:35:26 -0700411 with open(args.output_file, 'w', encoding='utf-8') as file:
412 sbom_writers.JSONWriter.write(doc, file)
413 fragment_file = args.output_file.removesuffix('.spdx.json') + '-fragment.spdx'
414 with open(fragment_file, 'w', encoding='utf-8') as file:
Wei Lidec97b12023-04-07 16:45:17 -0700415 sbom_writers.TagValueWriter.write(doc, file, fragment=True)
416
417
418def main():
419 global args
420 args = get_args()
421 log('Args:', vars(args))
422
Wei Lifd7e6512023-05-05 10:49:28 -0700423 if args.unbundled_apk:
424 generate_sbom_for_unbundled_apk()
Wei Lidec97b12023-04-07 16:45:17 -0700425 return
426
427 global metadata_file_protos
428 metadata_file_protos = {}
429
Wei Lidec97b12023-04-07 16:45:17 -0700430 product_package = sbom_data.Package(id=sbom_data.SPDXID_PRODUCT,
431 name=sbom_data.PACKAGE_NAME_PRODUCT,
Wei Li52908252023-04-14 18:49:42 -0700432 download_location=sbom_data.VALUE_NONE,
Wei Lidec97b12023-04-07 16:45:17 -0700433 version=args.build_version,
434 supplier='Organization: ' + args.product_mfr,
435 files_analyzed=True)
Wei Lifd7e6512023-05-05 10:49:28 -0700436
437 doc = sbom_data.Document(name=args.build_version,
438 namespace=f'https://www.google.com/sbom/spdx/android/{args.build_version}',
439 creators=['Organization: ' + args.product_mfr])
440 if not args.unbundled_apex:
441 doc.packages.append(product_package)
Wei Lidec97b12023-04-07 16:45:17 -0700442
443 doc.packages.append(sbom_data.Package(id=sbom_data.SPDXID_PLATFORM,
444 name=sbom_data.PACKAGE_NAME_PLATFORM,
Wei Li52908252023-04-14 18:49:42 -0700445 download_location=sbom_data.VALUE_NONE,
Wei Lidec97b12023-04-07 16:45:17 -0700446 version=args.build_version,
447 supplier='Organization: ' + args.product_mfr))
448
449 # Report on some issues and information
450 report = {
451 ISSUE_NO_METADATA: [],
452 ISSUE_NO_METADATA_FILE: [],
453 ISSUE_METADATA_FILE_INCOMPLETE: [],
454 ISSUE_UNKNOWN_SECURITY_TAG_TYPE: [],
455 ISSUE_INSTALLED_FILE_NOT_EXIST: [],
456 INFO_METADATA_FOUND_FOR_PACKAGE: [],
457 }
458
459 # Scan the metadata in CSV file and create the corresponding package and file records in SPDX
460 with open(args.metadata, newline='') as sbom_metadata_file:
461 reader = csv.DictReader(sbom_metadata_file)
462 for installed_file_metadata in reader:
463 installed_file = installed_file_metadata['installed_file']
464 module_path = installed_file_metadata['module_path']
465 product_copy_files = installed_file_metadata['product_copy_files']
466 kernel_module_copy_files = installed_file_metadata['kernel_module_copy_files']
Wei Lifd7e6512023-05-05 10:49:28 -0700467 build_output_path = installed_file_metadata['build_output_path']
Wei Lid2636952023-05-30 15:03:03 -0700468 is_static_lib = installed_file_metadata['is_static_lib']
Wei Lidec97b12023-04-07 16:45:17 -0700469
470 if not installed_file_has_metadata(installed_file_metadata, report):
471 continue
Wei Lid2636952023-05-30 15:03:03 -0700472 if not is_static_lib and not (os.path.islink(build_output_path) or os.path.isfile(build_output_path)):
473 # Ignore non-existing static library files for now since they are not shipped on devices.
Wei Lidec97b12023-04-07 16:45:17 -0700474 report[ISSUE_INSTALLED_FILE_NOT_EXIST].append(installed_file)
475 continue
476
477 file_id = new_file_id(installed_file)
Wei Lid2636952023-05-30 15:03:03 -0700478 # TODO(b/285453664): Soong should report the information of statically linked libraries to Make.
479 # This happens when a different sanitized version of static libraries is used in linking.
480 # As a workaround, use the following SHA1 checksum for static libraries created by Soong, if .a files could not be
481 # located correctly because Soong doesn't report the information to Make.
482 sha1 = 'SHA1: da39a3ee5e6b4b0d3255bfef95601890afd80709' # SHA1 of empty string
483 if os.path.islink(build_output_path) or os.path.isfile(build_output_path):
484 sha1 = checksum(build_output_path)
485 doc.files.append(sbom_data.File(id=file_id,
486 name=installed_file,
487 checksum=sha1))
488
489 if not is_static_lib:
490 if not args.unbundled_apex:
491 product_package.file_ids.append(file_id)
492 elif len(doc.files) > 1:
493 doc.add_relationship(sbom_data.Relationship(doc.files[0].id, sbom_data.RelationshipType.CONTAINS, file_id))
Wei Lidec97b12023-04-07 16:45:17 -0700494
495 if is_source_package(installed_file_metadata) or is_prebuilt_package(installed_file_metadata):
496 metadata_file_path = get_metadata_file_path(installed_file_metadata)
497 report_metadata_file(metadata_file_path, installed_file_metadata, report)
498
499 # File from source fork packages or prebuilt fork packages
500 external_doc_ref, pkgs, rels = get_sbom_fragments(installed_file_metadata, metadata_file_path)
501 if len(pkgs) > 0:
502 if external_doc_ref:
503 doc.add_external_ref(external_doc_ref)
504 for p in pkgs:
505 doc.add_package(p)
506 for rel in rels:
507 doc.add_relationship(rel)
508 fork_package_id = pkgs[0].id # The first package should be the source/prebuilt fork package
509 doc.add_relationship(sbom_data.Relationship(id1=file_id,
510 relationship=sbom_data.RelationshipType.GENERATED_FROM,
511 id2=fork_package_id))
512 elif module_path or installed_file_metadata['is_platform_generated']:
513 # File from PLATFORM package
514 doc.add_relationship(sbom_data.Relationship(id1=file_id,
515 relationship=sbom_data.RelationshipType.GENERATED_FROM,
516 id2=sbom_data.SPDXID_PLATFORM))
517 elif product_copy_files:
518 # Format of product_copy_files: <source path>:<dest path>
519 src_path = product_copy_files.split(':')[0]
520 # So far product_copy_files are copied from directory system, kernel, hardware, frameworks and device,
521 # so process them as files from PLATFORM package
522 doc.add_relationship(sbom_data.Relationship(id1=file_id,
523 relationship=sbom_data.RelationshipType.GENERATED_FROM,
524 id2=sbom_data.SPDXID_PLATFORM))
525 elif installed_file.endswith('.fsv_meta'):
526 # See build/make/core/Makefile:2988
527 doc.add_relationship(sbom_data.Relationship(id1=file_id,
528 relationship=sbom_data.RelationshipType.GENERATED_FROM,
529 id2=sbom_data.SPDXID_PLATFORM))
530 elif kernel_module_copy_files.startswith('ANDROID-GEN'):
531 # For the four files generated for _dlkm, _ramdisk partitions
532 # See build/make/core/Makefile:323
533 doc.add_relationship(sbom_data.Relationship(id1=file_id,
534 relationship=sbom_data.RelationshipType.GENERATED_FROM,
535 id2=sbom_data.SPDXID_PLATFORM))
536
Wei Lid2636952023-05-30 15:03:03 -0700537 # Process static libraries and whole static libraries the installed file links to
538 static_libs = installed_file_metadata['static_libraries']
539 whole_static_libs = installed_file_metadata['whole_static_libraries']
540 all_static_libs = (static_libs + ' ' + whole_static_libs).strip()
541 if all_static_libs:
542 for lib in all_static_libs.split(' '):
543 doc.add_relationship(sbom_data.Relationship(id1=file_id,
544 relationship=sbom_data.RelationshipType.STATIC_LINK,
545 id2=new_file_id(lib + '.a')))
Wei Lifd7e6512023-05-05 10:49:28 -0700546
547 if args.unbundled_apex:
548 doc.describes = doc.files[0].id
Wei Lidec97b12023-04-07 16:45:17 -0700549
550 # Save SBOM records to output file
Wei Lid2636952023-05-30 15:03:03 -0700551 doc.generate_packages_verification_code()
Wei Lidec97b12023-04-07 16:45:17 -0700552 doc.created = datetime.datetime.now(tz=datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
Wei Lifd7e6512023-05-05 10:49:28 -0700553 prefix = args.output_file
554 if prefix.endswith('.spdx'):
555 prefix = prefix.removesuffix('.spdx')
556 elif prefix.endswith('.spdx.json'):
557 prefix = prefix.removesuffix('.spdx.json')
558
559 output_file = prefix + '.spdx'
560 if args.unbundled_apex:
561 output_file = prefix + '-fragment.spdx'
562 with open(output_file, 'w', encoding="utf-8") as file:
563 sbom_writers.TagValueWriter.write(doc, file, fragment=args.unbundled_apex)
Wei Lidec97b12023-04-07 16:45:17 -0700564 if args.json:
Wei Lifd7e6512023-05-05 10:49:28 -0700565 with open(prefix + '.spdx.json', 'w', encoding="utf-8") as file:
Wei Lidec97b12023-04-07 16:45:17 -0700566 sbom_writers.JSONWriter.write(doc, file)
567
Wei Lifd7e6512023-05-05 10:49:28 -0700568 save_report(prefix + '-gen-report.txt', report)
569
Wei Lidec97b12023-04-07 16:45:17 -0700570
571if __name__ == '__main__':
572 main()