1*9e94795aSAndroid Build Coastguard Worker# !/usr/bin/env python3 2*9e94795aSAndroid Build Coastguard Worker# 3*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2024 The Android Open Source Project 4*9e94795aSAndroid Build Coastguard Worker# 5*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*9e94795aSAndroid Build Coastguard Worker# 9*9e94795aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*9e94795aSAndroid Build Coastguard Worker# 11*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*9e94795aSAndroid Build Coastguard Worker# limitations under the License. 16*9e94795aSAndroid Build Coastguard Worker 17*9e94795aSAndroid Build Coastguard Worker""" 18*9e94795aSAndroid Build Coastguard WorkerGenerate the SBOM of the current target product in SPDX format. 19*9e94795aSAndroid Build Coastguard WorkerUsage example: 20*9e94795aSAndroid Build Coastguard Worker gen_sbom.py --output_file out/soong/sbom/aosp_cf_x86_64_phone/sbom.spdx \ 21*9e94795aSAndroid Build Coastguard Worker --metadata out/soong/metadata/aosp_cf_x86_64_phone/metadata.db \ 22*9e94795aSAndroid Build Coastguard Worker --product_out out/target/vsoc_x86_64 23*9e94795aSAndroid Build Coastguard Worker --soong_out out/soong 24*9e94795aSAndroid Build Coastguard Worker --build_version $(cat out/target/product/vsoc_x86_64/build_fingerprint.txt) \ 25*9e94795aSAndroid Build Coastguard Worker --product_mfr=Google 26*9e94795aSAndroid Build Coastguard Worker""" 27*9e94795aSAndroid Build Coastguard Worker 28*9e94795aSAndroid Build Coastguard Workerimport argparse 29*9e94795aSAndroid Build Coastguard Workerimport compliance_metadata 30*9e94795aSAndroid Build Coastguard Workerimport datetime 31*9e94795aSAndroid Build Coastguard Workerimport google.protobuf.text_format as text_format 32*9e94795aSAndroid Build Coastguard Workerimport hashlib 33*9e94795aSAndroid Build Coastguard Workerimport os 34*9e94795aSAndroid Build Coastguard Workerimport pathlib 35*9e94795aSAndroid Build Coastguard Workerimport queue 36*9e94795aSAndroid Build Coastguard Workerimport metadata_file_pb2 37*9e94795aSAndroid Build Coastguard Workerimport sbom_data 38*9e94795aSAndroid Build Coastguard Workerimport sbom_writers 39*9e94795aSAndroid Build Coastguard Worker 40*9e94795aSAndroid Build Coastguard Worker# Package type 41*9e94795aSAndroid Build Coastguard WorkerPKG_SOURCE = 'SOURCE' 42*9e94795aSAndroid Build Coastguard WorkerPKG_UPSTREAM = 'UPSTREAM' 43*9e94795aSAndroid Build Coastguard WorkerPKG_PREBUILT = 'PREBUILT' 44*9e94795aSAndroid Build Coastguard Worker 45*9e94795aSAndroid Build Coastguard Worker# Security tag 46*9e94795aSAndroid Build Coastguard WorkerNVD_CPE23 = 'NVD-CPE2.3:' 47*9e94795aSAndroid Build Coastguard Worker 48*9e94795aSAndroid Build Coastguard Worker# Report 49*9e94795aSAndroid Build Coastguard WorkerISSUE_NO_METADATA = 'No metadata generated in Make for installed files:' 50*9e94795aSAndroid Build Coastguard WorkerISSUE_NO_METADATA_FILE = 'No METADATA file found for installed file:' 51*9e94795aSAndroid Build Coastguard WorkerISSUE_METADATA_FILE_INCOMPLETE = 'METADATA file incomplete:' 52*9e94795aSAndroid Build Coastguard WorkerISSUE_UNKNOWN_SECURITY_TAG_TYPE = 'Unknown security tag type:' 53*9e94795aSAndroid Build Coastguard WorkerISSUE_INSTALLED_FILE_NOT_EXIST = 'Non-existent installed files:' 54*9e94795aSAndroid Build Coastguard WorkerISSUE_NO_MODULE_FOUND_FOR_STATIC_DEP = 'No module found for static dependency files:' 55*9e94795aSAndroid Build Coastguard WorkerINFO_METADATA_FOUND_FOR_PACKAGE = 'METADATA file found for packages:' 56*9e94795aSAndroid Build Coastguard Worker 57*9e94795aSAndroid Build Coastguard WorkerSOONG_PREBUILT_MODULE_TYPES = [ 58*9e94795aSAndroid Build Coastguard Worker 'android_app_import', 59*9e94795aSAndroid Build Coastguard Worker 'android_library_import', 60*9e94795aSAndroid Build Coastguard Worker 'cc_prebuilt_binary', 61*9e94795aSAndroid Build Coastguard Worker 'cc_prebuilt_library', 62*9e94795aSAndroid Build Coastguard Worker 'cc_prebuilt_library_headers', 63*9e94795aSAndroid Build Coastguard Worker 'cc_prebuilt_library_shared', 64*9e94795aSAndroid Build Coastguard Worker 'cc_prebuilt_library_static', 65*9e94795aSAndroid Build Coastguard Worker 'cc_prebuilt_object', 66*9e94795aSAndroid Build Coastguard Worker 'dex_import', 67*9e94795aSAndroid Build Coastguard Worker 'java_import', 68*9e94795aSAndroid Build Coastguard Worker 'java_sdk_library_import', 69*9e94795aSAndroid Build Coastguard Worker 'java_system_modules_import', 70*9e94795aSAndroid Build Coastguard Worker 'libclang_rt_prebuilt_library_static', 71*9e94795aSAndroid Build Coastguard Worker 'libclang_rt_prebuilt_library_shared', 72*9e94795aSAndroid Build Coastguard Worker 'llvm_prebuilt_library_static', 73*9e94795aSAndroid Build Coastguard Worker 'ndk_prebuilt_object', 74*9e94795aSAndroid Build Coastguard Worker 'ndk_prebuilt_shared_stl', 75*9e94795aSAndroid Build Coastguard Worker 'nkd_prebuilt_static_stl', 76*9e94795aSAndroid Build Coastguard Worker 'prebuilt_apex', 77*9e94795aSAndroid Build Coastguard Worker 'prebuilt_bootclasspath_fragment', 78*9e94795aSAndroid Build Coastguard Worker 'prebuilt_dsp', 79*9e94795aSAndroid Build Coastguard Worker 'prebuilt_firmware', 80*9e94795aSAndroid Build Coastguard Worker 'prebuilt_kernel_modules', 81*9e94795aSAndroid Build Coastguard Worker 'prebuilt_rfsa', 82*9e94795aSAndroid Build Coastguard Worker 'prebuilt_root', 83*9e94795aSAndroid Build Coastguard Worker 'rust_prebuilt_dylib', 84*9e94795aSAndroid Build Coastguard Worker 'rust_prebuilt_library', 85*9e94795aSAndroid Build Coastguard Worker 'rust_prebuilt_rlib', 86*9e94795aSAndroid Build Coastguard Worker 'vndk_prebuilt_shared', 87*9e94795aSAndroid Build Coastguard Worker] 88*9e94795aSAndroid Build Coastguard Worker 89*9e94795aSAndroid Build Coastguard WorkerTHIRD_PARTY_IDENTIFIER_TYPES = [ 90*9e94795aSAndroid Build Coastguard Worker # Types defined in metadata_file.proto 91*9e94795aSAndroid Build Coastguard Worker 'Git', 92*9e94795aSAndroid Build Coastguard Worker 'SVN', 93*9e94795aSAndroid Build Coastguard Worker 'Hg', 94*9e94795aSAndroid Build Coastguard Worker 'Darcs', 95*9e94795aSAndroid Build Coastguard Worker 'VCS', 96*9e94795aSAndroid Build Coastguard Worker 'Archive', 97*9e94795aSAndroid Build Coastguard Worker 'PrebuiltByAlphabet', 98*9e94795aSAndroid Build Coastguard Worker 'LocalSource', 99*9e94795aSAndroid Build Coastguard Worker 'Other', 100*9e94795aSAndroid Build Coastguard Worker # OSV ecosystems defined at https://ossf.github.io/osv-schema/#affectedpackage-field. 101*9e94795aSAndroid Build Coastguard Worker 'Go', 102*9e94795aSAndroid Build Coastguard Worker 'npm', 103*9e94795aSAndroid Build Coastguard Worker 'OSS-Fuzz', 104*9e94795aSAndroid Build Coastguard Worker 'PyPI', 105*9e94795aSAndroid Build Coastguard Worker 'RubyGems', 106*9e94795aSAndroid Build Coastguard Worker 'crates.io', 107*9e94795aSAndroid Build Coastguard Worker 'Hackage', 108*9e94795aSAndroid Build Coastguard Worker 'GHC', 109*9e94795aSAndroid Build Coastguard Worker 'Packagist', 110*9e94795aSAndroid Build Coastguard Worker 'Maven', 111*9e94795aSAndroid Build Coastguard Worker 'NuGet', 112*9e94795aSAndroid Build Coastguard Worker 'Linux', 113*9e94795aSAndroid Build Coastguard Worker 'Debian', 114*9e94795aSAndroid Build Coastguard Worker 'Alpine', 115*9e94795aSAndroid Build Coastguard Worker 'Hex', 116*9e94795aSAndroid Build Coastguard Worker 'Android', 117*9e94795aSAndroid Build Coastguard Worker 'GitHub Actions', 118*9e94795aSAndroid Build Coastguard Worker 'Pub', 119*9e94795aSAndroid Build Coastguard Worker 'ConanCenter', 120*9e94795aSAndroid Build Coastguard Worker 'Rocky Linux', 121*9e94795aSAndroid Build Coastguard Worker 'AlmaLinux', 122*9e94795aSAndroid Build Coastguard Worker 'Bitnami', 123*9e94795aSAndroid Build Coastguard Worker 'Photon OS', 124*9e94795aSAndroid Build Coastguard Worker 'CRAN', 125*9e94795aSAndroid Build Coastguard Worker 'Bioconductor', 126*9e94795aSAndroid Build Coastguard Worker 'SwiftURL' 127*9e94795aSAndroid Build Coastguard Worker] 128*9e94795aSAndroid Build Coastguard Worker 129*9e94795aSAndroid Build Coastguard Worker 130*9e94795aSAndroid Build Coastguard Workerdef get_args(): 131*9e94795aSAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 132*9e94795aSAndroid Build Coastguard Worker parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Print more information.') 133*9e94795aSAndroid Build Coastguard Worker parser.add_argument('-d', '--debug', action='store_true', default=False, help='Debug mode') 134*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--output_file', required=True, help='The generated SBOM file in SPDX format.') 135*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--metadata', required=True, help='The metadata DB file path.') 136*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--product_out', required=True, help='The path of PRODUCT_OUT, e.g. out/target/product/vsoc_x86_64.') 137*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--soong_out', required=True, help='The path of Soong output directory, e.g. out/soong') 138*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--build_version', required=True, help='The build version.') 139*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--product_mfr', required=True, help='The product manufacturer.') 140*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--json', action='store_true', default=False, help='Generated SBOM file in SPDX JSON format') 141*9e94795aSAndroid Build Coastguard Worker 142*9e94795aSAndroid Build Coastguard Worker return parser.parse_args() 143*9e94795aSAndroid Build Coastguard Worker 144*9e94795aSAndroid Build Coastguard Worker 145*9e94795aSAndroid Build Coastguard Workerdef log(*info): 146*9e94795aSAndroid Build Coastguard Worker if args.verbose: 147*9e94795aSAndroid Build Coastguard Worker for i in info: 148*9e94795aSAndroid Build Coastguard Worker print(i) 149*9e94795aSAndroid Build Coastguard Worker 150*9e94795aSAndroid Build Coastguard Worker 151*9e94795aSAndroid Build Coastguard Workerdef new_package_id(package_name, type): 152*9e94795aSAndroid Build Coastguard Worker return f'SPDXRef-{type}-{sbom_data.encode_for_spdxid(package_name)}' 153*9e94795aSAndroid Build Coastguard Worker 154*9e94795aSAndroid Build Coastguard Worker 155*9e94795aSAndroid Build Coastguard Workerdef new_file_id(file_path): 156*9e94795aSAndroid Build Coastguard Worker return f'SPDXRef-{sbom_data.encode_for_spdxid(file_path)}' 157*9e94795aSAndroid Build Coastguard Worker 158*9e94795aSAndroid Build Coastguard Worker 159*9e94795aSAndroid Build Coastguard Workerdef new_license_id(license_name): 160*9e94795aSAndroid Build Coastguard Worker return f'LicenseRef-{sbom_data.encode_for_spdxid(license_name)}' 161*9e94795aSAndroid Build Coastguard Worker 162*9e94795aSAndroid Build Coastguard Worker 163*9e94795aSAndroid Build Coastguard Workerdef checksum(file_path): 164*9e94795aSAndroid Build Coastguard Worker h = hashlib.sha1() 165*9e94795aSAndroid Build Coastguard Worker if os.path.islink(file_path): 166*9e94795aSAndroid Build Coastguard Worker h.update(os.readlink(file_path).encode('utf-8')) 167*9e94795aSAndroid Build Coastguard Worker else: 168*9e94795aSAndroid Build Coastguard Worker with open(file_path, 'rb') as f: 169*9e94795aSAndroid Build Coastguard Worker h.update(f.read()) 170*9e94795aSAndroid Build Coastguard Worker return f'SHA1: {h.hexdigest()}' 171*9e94795aSAndroid Build Coastguard Worker 172*9e94795aSAndroid Build Coastguard Worker 173*9e94795aSAndroid Build Coastguard Workerdef is_soong_prebuilt_module(file_metadata): 174*9e94795aSAndroid Build Coastguard Worker return (file_metadata['soong_module_type'] and 175*9e94795aSAndroid Build Coastguard Worker file_metadata['soong_module_type'] in SOONG_PREBUILT_MODULE_TYPES) 176*9e94795aSAndroid Build Coastguard Worker 177*9e94795aSAndroid Build Coastguard Worker 178*9e94795aSAndroid Build Coastguard Workerdef is_source_package(file_metadata): 179*9e94795aSAndroid Build Coastguard Worker module_path = file_metadata['module_path'] 180*9e94795aSAndroid Build Coastguard Worker return module_path.startswith('external/') and not is_prebuilt_package(file_metadata) 181*9e94795aSAndroid Build Coastguard Worker 182*9e94795aSAndroid Build Coastguard Worker 183*9e94795aSAndroid Build Coastguard Workerdef is_prebuilt_package(file_metadata): 184*9e94795aSAndroid Build Coastguard Worker module_path = file_metadata['module_path'] 185*9e94795aSAndroid Build Coastguard Worker if module_path: 186*9e94795aSAndroid Build Coastguard Worker return (module_path.startswith('prebuilts/') or 187*9e94795aSAndroid Build Coastguard Worker is_soong_prebuilt_module(file_metadata) or 188*9e94795aSAndroid Build Coastguard Worker file_metadata['is_prebuilt_make_module']) 189*9e94795aSAndroid Build Coastguard Worker 190*9e94795aSAndroid Build Coastguard Worker kernel_module_copy_files = file_metadata['kernel_module_copy_files'] 191*9e94795aSAndroid Build Coastguard Worker if kernel_module_copy_files and not kernel_module_copy_files.startswith('ANDROID-GEN:'): 192*9e94795aSAndroid Build Coastguard Worker return True 193*9e94795aSAndroid Build Coastguard Worker 194*9e94795aSAndroid Build Coastguard Worker return False 195*9e94795aSAndroid Build Coastguard Worker 196*9e94795aSAndroid Build Coastguard Worker 197*9e94795aSAndroid Build Coastguard Workerdef get_source_package_info(file_metadata, metadata_file_path): 198*9e94795aSAndroid Build Coastguard Worker """Return source package info exists in its METADATA file, currently including name, security tag 199*9e94795aSAndroid Build Coastguard Worker and external SBOM reference. 200*9e94795aSAndroid Build Coastguard Worker 201*9e94795aSAndroid Build Coastguard Worker See go/android-spdx and go/android-sbom-gen for more details. 202*9e94795aSAndroid Build Coastguard Worker """ 203*9e94795aSAndroid Build Coastguard Worker if not metadata_file_path: 204*9e94795aSAndroid Build Coastguard Worker return file_metadata['module_path'], [] 205*9e94795aSAndroid Build Coastguard Worker 206*9e94795aSAndroid Build Coastguard Worker metadata_proto = metadata_file_protos[metadata_file_path] 207*9e94795aSAndroid Build Coastguard Worker external_refs = [] 208*9e94795aSAndroid Build Coastguard Worker for tag in metadata_proto.third_party.security.tag: 209*9e94795aSAndroid Build Coastguard Worker if tag.lower().startswith((NVD_CPE23 + 'cpe:2.3:').lower()): 210*9e94795aSAndroid Build Coastguard Worker external_refs.append( 211*9e94795aSAndroid Build Coastguard Worker sbom_data.PackageExternalRef(category=sbom_data.PackageExternalRefCategory.SECURITY, 212*9e94795aSAndroid Build Coastguard Worker type=sbom_data.PackageExternalRefType.cpe23Type, 213*9e94795aSAndroid Build Coastguard Worker locator=tag.removeprefix(NVD_CPE23))) 214*9e94795aSAndroid Build Coastguard Worker elif tag.lower().startswith((NVD_CPE23 + 'cpe:/').lower()): 215*9e94795aSAndroid Build Coastguard Worker external_refs.append( 216*9e94795aSAndroid Build Coastguard Worker sbom_data.PackageExternalRef(category=sbom_data.PackageExternalRefCategory.SECURITY, 217*9e94795aSAndroid Build Coastguard Worker type=sbom_data.PackageExternalRefType.cpe22Type, 218*9e94795aSAndroid Build Coastguard Worker locator=tag.removeprefix(NVD_CPE23))) 219*9e94795aSAndroid Build Coastguard Worker 220*9e94795aSAndroid Build Coastguard Worker if metadata_proto.name: 221*9e94795aSAndroid Build Coastguard Worker return metadata_proto.name, external_refs 222*9e94795aSAndroid Build Coastguard Worker else: 223*9e94795aSAndroid Build Coastguard Worker return os.path.basename(metadata_file_path), external_refs # return the directory name only as package name 224*9e94795aSAndroid Build Coastguard Worker 225*9e94795aSAndroid Build Coastguard Worker 226*9e94795aSAndroid Build Coastguard Workerdef get_prebuilt_package_name(file_metadata, metadata_file_path): 227*9e94795aSAndroid Build Coastguard Worker """Return name of a prebuilt package, which can be from the METADATA file, metadata file path, 228*9e94795aSAndroid Build Coastguard Worker module path or kernel module's source path if the installed file is a kernel module. 229*9e94795aSAndroid Build Coastguard Worker 230*9e94795aSAndroid Build Coastguard Worker See go/android-spdx and go/android-sbom-gen for more details. 231*9e94795aSAndroid Build Coastguard Worker """ 232*9e94795aSAndroid Build Coastguard Worker name = None 233*9e94795aSAndroid Build Coastguard Worker if metadata_file_path: 234*9e94795aSAndroid Build Coastguard Worker metadata_proto = metadata_file_protos[metadata_file_path] 235*9e94795aSAndroid Build Coastguard Worker if metadata_proto.name: 236*9e94795aSAndroid Build Coastguard Worker name = metadata_proto.name 237*9e94795aSAndroid Build Coastguard Worker else: 238*9e94795aSAndroid Build Coastguard Worker name = metadata_file_path 239*9e94795aSAndroid Build Coastguard Worker elif file_metadata['module_path']: 240*9e94795aSAndroid Build Coastguard Worker name = file_metadata['module_path'] 241*9e94795aSAndroid Build Coastguard Worker elif file_metadata['kernel_module_copy_files']: 242*9e94795aSAndroid Build Coastguard Worker src_path = file_metadata['kernel_module_copy_files'].split(':')[0] 243*9e94795aSAndroid Build Coastguard Worker name = os.path.dirname(src_path) 244*9e94795aSAndroid Build Coastguard Worker 245*9e94795aSAndroid Build Coastguard Worker return name.removeprefix('prebuilts/').replace('/', '-') 246*9e94795aSAndroid Build Coastguard Worker 247*9e94795aSAndroid Build Coastguard Worker 248*9e94795aSAndroid Build Coastguard Workerdef get_metadata_file_path(file_metadata): 249*9e94795aSAndroid Build Coastguard Worker """Search for METADATA file of a package and return its path.""" 250*9e94795aSAndroid Build Coastguard Worker metadata_path = '' 251*9e94795aSAndroid Build Coastguard Worker if file_metadata['module_path']: 252*9e94795aSAndroid Build Coastguard Worker metadata_path = file_metadata['module_path'] 253*9e94795aSAndroid Build Coastguard Worker elif file_metadata['kernel_module_copy_files']: 254*9e94795aSAndroid Build Coastguard Worker metadata_path = os.path.dirname(file_metadata['kernel_module_copy_files'].split(':')[0]) 255*9e94795aSAndroid Build Coastguard Worker 256*9e94795aSAndroid Build Coastguard Worker while metadata_path and not os.path.exists(metadata_path + '/METADATA'): 257*9e94795aSAndroid Build Coastguard Worker metadata_path = os.path.dirname(metadata_path) 258*9e94795aSAndroid Build Coastguard Worker 259*9e94795aSAndroid Build Coastguard Worker return metadata_path 260*9e94795aSAndroid Build Coastguard Worker 261*9e94795aSAndroid Build Coastguard Worker 262*9e94795aSAndroid Build Coastguard Workerdef get_package_version(metadata_file_path): 263*9e94795aSAndroid Build Coastguard Worker """Return a package's version in its METADATA file.""" 264*9e94795aSAndroid Build Coastguard Worker if not metadata_file_path: 265*9e94795aSAndroid Build Coastguard Worker return None 266*9e94795aSAndroid Build Coastguard Worker metadata_proto = metadata_file_protos[metadata_file_path] 267*9e94795aSAndroid Build Coastguard Worker return metadata_proto.third_party.version 268*9e94795aSAndroid Build Coastguard Worker 269*9e94795aSAndroid Build Coastguard Worker 270*9e94795aSAndroid Build Coastguard Workerdef get_package_homepage(metadata_file_path): 271*9e94795aSAndroid Build Coastguard Worker """Return a package's homepage URL in its METADATA file.""" 272*9e94795aSAndroid Build Coastguard Worker if not metadata_file_path: 273*9e94795aSAndroid Build Coastguard Worker return None 274*9e94795aSAndroid Build Coastguard Worker metadata_proto = metadata_file_protos[metadata_file_path] 275*9e94795aSAndroid Build Coastguard Worker if metadata_proto.third_party.homepage: 276*9e94795aSAndroid Build Coastguard Worker return metadata_proto.third_party.homepage 277*9e94795aSAndroid Build Coastguard Worker for url in metadata_proto.third_party.url: 278*9e94795aSAndroid Build Coastguard Worker if url.type == metadata_file_pb2.URL.Type.HOMEPAGE: 279*9e94795aSAndroid Build Coastguard Worker return url.value 280*9e94795aSAndroid Build Coastguard Worker 281*9e94795aSAndroid Build Coastguard Worker return None 282*9e94795aSAndroid Build Coastguard Worker 283*9e94795aSAndroid Build Coastguard Worker 284*9e94795aSAndroid Build Coastguard Workerdef get_package_download_location(metadata_file_path): 285*9e94795aSAndroid Build Coastguard Worker """Return a package's code repository URL in its METADATA file.""" 286*9e94795aSAndroid Build Coastguard Worker if not metadata_file_path: 287*9e94795aSAndroid Build Coastguard Worker return None 288*9e94795aSAndroid Build Coastguard Worker metadata_proto = metadata_file_protos[metadata_file_path] 289*9e94795aSAndroid Build Coastguard Worker if metadata_proto.third_party.url: 290*9e94795aSAndroid Build Coastguard Worker urls = sorted(metadata_proto.third_party.url, key=lambda url: url.type) 291*9e94795aSAndroid Build Coastguard Worker if urls[0].type != metadata_file_pb2.URL.Type.HOMEPAGE: 292*9e94795aSAndroid Build Coastguard Worker return urls[0].value 293*9e94795aSAndroid Build Coastguard Worker elif len(urls) > 1: 294*9e94795aSAndroid Build Coastguard Worker return urls[1].value 295*9e94795aSAndroid Build Coastguard Worker 296*9e94795aSAndroid Build Coastguard Worker return None 297*9e94795aSAndroid Build Coastguard Worker 298*9e94795aSAndroid Build Coastguard Worker 299*9e94795aSAndroid Build Coastguard Workerdef get_license_text(license_files): 300*9e94795aSAndroid Build Coastguard Worker license_text = '' 301*9e94795aSAndroid Build Coastguard Worker for license_file in license_files: 302*9e94795aSAndroid Build Coastguard Worker if args.debug: 303*9e94795aSAndroid Build Coastguard Worker license_text += '#### Content from ' + license_file + '\n' 304*9e94795aSAndroid Build Coastguard Worker else: 305*9e94795aSAndroid Build Coastguard Worker license_text += pathlib.Path(license_file).read_text(errors='replace') + '\n\n' 306*9e94795aSAndroid Build Coastguard Worker return license_text 307*9e94795aSAndroid Build Coastguard Worker 308*9e94795aSAndroid Build Coastguard Worker 309*9e94795aSAndroid Build Coastguard Workerdef get_sbom_fragments(installed_file_metadata, metadata_file_path): 310*9e94795aSAndroid Build Coastguard Worker """Return SPDX fragment of source/prebuilt packages, which usually contains a SOURCE/PREBUILT 311*9e94795aSAndroid Build Coastguard Worker package, a UPSTREAM package and an external SBOM document reference if sbom_ref defined in its 312*9e94795aSAndroid Build Coastguard Worker METADATA file. 313*9e94795aSAndroid Build Coastguard Worker 314*9e94795aSAndroid Build Coastguard Worker See go/android-spdx and go/android-sbom-gen for more details. 315*9e94795aSAndroid Build Coastguard Worker """ 316*9e94795aSAndroid Build Coastguard Worker external_doc_ref = None 317*9e94795aSAndroid Build Coastguard Worker packages = [] 318*9e94795aSAndroid Build Coastguard Worker relationships = [] 319*9e94795aSAndroid Build Coastguard Worker licenses = [] 320*9e94795aSAndroid Build Coastguard Worker 321*9e94795aSAndroid Build Coastguard Worker # Info from METADATA file 322*9e94795aSAndroid Build Coastguard Worker homepage = get_package_homepage(metadata_file_path) 323*9e94795aSAndroid Build Coastguard Worker version = get_package_version(metadata_file_path) 324*9e94795aSAndroid Build Coastguard Worker download_location = get_package_download_location(metadata_file_path) 325*9e94795aSAndroid Build Coastguard Worker 326*9e94795aSAndroid Build Coastguard Worker lics = db.get_package_licenses(installed_file_metadata['module_path']) 327*9e94795aSAndroid Build Coastguard Worker if not lics: 328*9e94795aSAndroid Build Coastguard Worker lics = db.get_package_licenses(metadata_file_path) 329*9e94795aSAndroid Build Coastguard Worker 330*9e94795aSAndroid Build Coastguard Worker if lics: 331*9e94795aSAndroid Build Coastguard Worker for license_name, license_files in lics.items(): 332*9e94795aSAndroid Build Coastguard Worker if not license_files: 333*9e94795aSAndroid Build Coastguard Worker continue 334*9e94795aSAndroid Build Coastguard Worker license_id = new_license_id(license_name) 335*9e94795aSAndroid Build Coastguard Worker if license_name not in licenses_text: 336*9e94795aSAndroid Build Coastguard Worker licenses_text[license_name] = get_license_text(license_files.split(' ')) 337*9e94795aSAndroid Build Coastguard Worker licenses.append(sbom_data.License(id=license_id, name=license_name, text=licenses_text[license_name])) 338*9e94795aSAndroid Build Coastguard Worker 339*9e94795aSAndroid Build Coastguard Worker if is_source_package(installed_file_metadata): 340*9e94795aSAndroid Build Coastguard Worker # Source fork packages 341*9e94795aSAndroid Build Coastguard Worker name, external_refs = get_source_package_info(installed_file_metadata, metadata_file_path) 342*9e94795aSAndroid Build Coastguard Worker source_package_id = new_package_id(name, PKG_SOURCE) 343*9e94795aSAndroid Build Coastguard Worker source_package = sbom_data.Package(id=source_package_id, name=name, version=args.build_version, 344*9e94795aSAndroid Build Coastguard Worker download_location=sbom_data.VALUE_NONE, 345*9e94795aSAndroid Build Coastguard Worker supplier='Organization: ' + args.product_mfr, 346*9e94795aSAndroid Build Coastguard Worker external_refs=external_refs) 347*9e94795aSAndroid Build Coastguard Worker 348*9e94795aSAndroid Build Coastguard Worker upstream_package_id = new_package_id(name, PKG_UPSTREAM) 349*9e94795aSAndroid Build Coastguard Worker upstream_package = sbom_data.Package(id=upstream_package_id, name=name, version=version, 350*9e94795aSAndroid Build Coastguard Worker supplier=( 351*9e94795aSAndroid Build Coastguard Worker 'Organization: ' + homepage) if homepage else sbom_data.VALUE_NOASSERTION, 352*9e94795aSAndroid Build Coastguard Worker download_location=download_location) 353*9e94795aSAndroid Build Coastguard Worker packages += [source_package, upstream_package] 354*9e94795aSAndroid Build Coastguard Worker relationships.append(sbom_data.Relationship(id1=source_package_id, 355*9e94795aSAndroid Build Coastguard Worker relationship=sbom_data.RelationshipType.VARIANT_OF, 356*9e94795aSAndroid Build Coastguard Worker id2=upstream_package_id)) 357*9e94795aSAndroid Build Coastguard Worker 358*9e94795aSAndroid Build Coastguard Worker for license in licenses: 359*9e94795aSAndroid Build Coastguard Worker source_package.declared_license_ids.append(license.id) 360*9e94795aSAndroid Build Coastguard Worker upstream_package.declared_license_ids.append(license.id) 361*9e94795aSAndroid Build Coastguard Worker 362*9e94795aSAndroid Build Coastguard Worker elif is_prebuilt_package(installed_file_metadata): 363*9e94795aSAndroid Build Coastguard Worker # Prebuilt fork packages 364*9e94795aSAndroid Build Coastguard Worker name = get_prebuilt_package_name(installed_file_metadata, metadata_file_path) 365*9e94795aSAndroid Build Coastguard Worker prebuilt_package_id = new_package_id(name, PKG_PREBUILT) 366*9e94795aSAndroid Build Coastguard Worker prebuilt_package = sbom_data.Package(id=prebuilt_package_id, 367*9e94795aSAndroid Build Coastguard Worker name=name, 368*9e94795aSAndroid Build Coastguard Worker download_location=sbom_data.VALUE_NONE, 369*9e94795aSAndroid Build Coastguard Worker version=version if version else args.build_version, 370*9e94795aSAndroid Build Coastguard Worker supplier='Organization: ' + args.product_mfr) 371*9e94795aSAndroid Build Coastguard Worker 372*9e94795aSAndroid Build Coastguard Worker upstream_package_id = new_package_id(name, PKG_UPSTREAM) 373*9e94795aSAndroid Build Coastguard Worker upstream_package = sbom_data.Package(id=upstream_package_id, name=name, version=version, 374*9e94795aSAndroid Build Coastguard Worker supplier=( 375*9e94795aSAndroid Build Coastguard Worker 'Organization: ' + homepage) if homepage else sbom_data.VALUE_NOASSERTION, 376*9e94795aSAndroid Build Coastguard Worker download_location=download_location) 377*9e94795aSAndroid Build Coastguard Worker packages += [prebuilt_package, upstream_package] 378*9e94795aSAndroid Build Coastguard Worker relationships.append(sbom_data.Relationship(id1=prebuilt_package_id, 379*9e94795aSAndroid Build Coastguard Worker relationship=sbom_data.RelationshipType.VARIANT_OF, 380*9e94795aSAndroid Build Coastguard Worker id2=upstream_package_id)) 381*9e94795aSAndroid Build Coastguard Worker for license in licenses: 382*9e94795aSAndroid Build Coastguard Worker prebuilt_package.declared_license_ids.append(license.id) 383*9e94795aSAndroid Build Coastguard Worker upstream_package.declared_license_ids.append(license.id) 384*9e94795aSAndroid Build Coastguard Worker 385*9e94795aSAndroid Build Coastguard Worker if metadata_file_path: 386*9e94795aSAndroid Build Coastguard Worker metadata_proto = metadata_file_protos[metadata_file_path] 387*9e94795aSAndroid Build Coastguard Worker if metadata_proto.third_party.WhichOneof('sbom') == 'sbom_ref': 388*9e94795aSAndroid Build Coastguard Worker sbom_url = metadata_proto.third_party.sbom_ref.url 389*9e94795aSAndroid Build Coastguard Worker sbom_checksum = metadata_proto.third_party.sbom_ref.checksum 390*9e94795aSAndroid Build Coastguard Worker upstream_element_id = metadata_proto.third_party.sbom_ref.element_id 391*9e94795aSAndroid Build Coastguard Worker if sbom_url and sbom_checksum and upstream_element_id: 392*9e94795aSAndroid Build Coastguard Worker doc_ref_id = f'DocumentRef-{PKG_UPSTREAM}-{sbom_data.encode_for_spdxid(name)}' 393*9e94795aSAndroid Build Coastguard Worker external_doc_ref = sbom_data.DocumentExternalReference(id=doc_ref_id, 394*9e94795aSAndroid Build Coastguard Worker uri=sbom_url, 395*9e94795aSAndroid Build Coastguard Worker checksum=sbom_checksum) 396*9e94795aSAndroid Build Coastguard Worker relationships.append( 397*9e94795aSAndroid Build Coastguard Worker sbom_data.Relationship(id1=upstream_package_id, 398*9e94795aSAndroid Build Coastguard Worker relationship=sbom_data.RelationshipType.VARIANT_OF, 399*9e94795aSAndroid Build Coastguard Worker id2=doc_ref_id + ':' + upstream_element_id)) 400*9e94795aSAndroid Build Coastguard Worker 401*9e94795aSAndroid Build Coastguard Worker return external_doc_ref, packages, relationships, licenses 402*9e94795aSAndroid Build Coastguard Worker 403*9e94795aSAndroid Build Coastguard Worker 404*9e94795aSAndroid Build Coastguard Workerdef save_report(report_file_path, report): 405*9e94795aSAndroid Build Coastguard Worker with open(report_file_path, 'w', encoding='utf-8') as report_file: 406*9e94795aSAndroid Build Coastguard Worker for type, issues in report.items(): 407*9e94795aSAndroid Build Coastguard Worker report_file.write(type + '\n') 408*9e94795aSAndroid Build Coastguard Worker for issue in issues: 409*9e94795aSAndroid Build Coastguard Worker report_file.write('\t' + issue + '\n') 410*9e94795aSAndroid Build Coastguard Worker report_file.write('\n') 411*9e94795aSAndroid Build Coastguard Worker 412*9e94795aSAndroid Build Coastguard Worker 413*9e94795aSAndroid Build Coastguard Worker# Validate the metadata generated by Make for installed files and report if there is no metadata. 414*9e94795aSAndroid Build Coastguard Workerdef installed_file_has_metadata(installed_file_metadata, report): 415*9e94795aSAndroid Build Coastguard Worker installed_file = installed_file_metadata['installed_file'] 416*9e94795aSAndroid Build Coastguard Worker module_path = installed_file_metadata['module_path'] 417*9e94795aSAndroid Build Coastguard Worker product_copy_files = installed_file_metadata['product_copy_files'] 418*9e94795aSAndroid Build Coastguard Worker kernel_module_copy_files = installed_file_metadata['kernel_module_copy_files'] 419*9e94795aSAndroid Build Coastguard Worker is_platform_generated = installed_file_metadata['is_platform_generated'] 420*9e94795aSAndroid Build Coastguard Worker 421*9e94795aSAndroid Build Coastguard Worker if (not module_path and 422*9e94795aSAndroid Build Coastguard Worker not product_copy_files and 423*9e94795aSAndroid Build Coastguard Worker not kernel_module_copy_files and 424*9e94795aSAndroid Build Coastguard Worker not is_platform_generated and 425*9e94795aSAndroid Build Coastguard Worker not installed_file.endswith('.fsv_meta')): 426*9e94795aSAndroid Build Coastguard Worker report[ISSUE_NO_METADATA].append(installed_file) 427*9e94795aSAndroid Build Coastguard Worker return False 428*9e94795aSAndroid Build Coastguard Worker 429*9e94795aSAndroid Build Coastguard Worker return True 430*9e94795aSAndroid Build Coastguard Worker 431*9e94795aSAndroid Build Coastguard Worker 432*9e94795aSAndroid Build Coastguard Worker# Validate identifiers in a package's METADATA. 433*9e94795aSAndroid Build Coastguard Worker# 1) Only known identifier type is allowed 434*9e94795aSAndroid Build Coastguard Worker# 2) Only one identifier's primary_source can be true 435*9e94795aSAndroid Build Coastguard Workerdef validate_package_metadata(metadata_file_path, package_metadata): 436*9e94795aSAndroid Build Coastguard Worker primary_source_found = False 437*9e94795aSAndroid Build Coastguard Worker for identifier in package_metadata.third_party.identifier: 438*9e94795aSAndroid Build Coastguard Worker if identifier.type not in THIRD_PARTY_IDENTIFIER_TYPES: 439*9e94795aSAndroid Build Coastguard Worker sys.exit(f'Unknown value of third_party.identifier.type in {metadata_file_path}/METADATA: {identifier.type}.') 440*9e94795aSAndroid Build Coastguard Worker if primary_source_found and identifier.primary_source: 441*9e94795aSAndroid Build Coastguard Worker sys.exit( 442*9e94795aSAndroid Build Coastguard Worker f'Field "primary_source" is set to true in multiple third_party.identifier in {metadata_file_path}/METADATA.') 443*9e94795aSAndroid Build Coastguard Worker primary_source_found = identifier.primary_source 444*9e94795aSAndroid Build Coastguard Worker 445*9e94795aSAndroid Build Coastguard Worker 446*9e94795aSAndroid Build Coastguard Workerdef report_metadata_file(metadata_file_path, installed_file_metadata, report): 447*9e94795aSAndroid Build Coastguard Worker if metadata_file_path: 448*9e94795aSAndroid Build Coastguard Worker report[INFO_METADATA_FOUND_FOR_PACKAGE].append( 449*9e94795aSAndroid Build Coastguard Worker 'installed_file: {}, module_path: {}, METADATA file: {}'.format( 450*9e94795aSAndroid Build Coastguard Worker installed_file_metadata['installed_file'], 451*9e94795aSAndroid Build Coastguard Worker installed_file_metadata['module_path'], 452*9e94795aSAndroid Build Coastguard Worker metadata_file_path + '/METADATA')) 453*9e94795aSAndroid Build Coastguard Worker 454*9e94795aSAndroid Build Coastguard Worker package_metadata = metadata_file_pb2.Metadata() 455*9e94795aSAndroid Build Coastguard Worker with open(metadata_file_path + '/METADATA', 'rt') as f: 456*9e94795aSAndroid Build Coastguard Worker text_format.Parse(f.read(), package_metadata) 457*9e94795aSAndroid Build Coastguard Worker 458*9e94795aSAndroid Build Coastguard Worker validate_package_metadata(metadata_file_path, package_metadata) 459*9e94795aSAndroid Build Coastguard Worker 460*9e94795aSAndroid Build Coastguard Worker if not metadata_file_path in metadata_file_protos: 461*9e94795aSAndroid Build Coastguard Worker metadata_file_protos[metadata_file_path] = package_metadata 462*9e94795aSAndroid Build Coastguard Worker if not package_metadata.name: 463*9e94795aSAndroid Build Coastguard Worker report[ISSUE_METADATA_FILE_INCOMPLETE].append(f'{metadata_file_path}/METADATA does not has "name"') 464*9e94795aSAndroid Build Coastguard Worker 465*9e94795aSAndroid Build Coastguard Worker if not package_metadata.third_party.version: 466*9e94795aSAndroid Build Coastguard Worker report[ISSUE_METADATA_FILE_INCOMPLETE].append( 467*9e94795aSAndroid Build Coastguard Worker f'{metadata_file_path}/METADATA does not has "third_party.version"') 468*9e94795aSAndroid Build Coastguard Worker 469*9e94795aSAndroid Build Coastguard Worker for tag in package_metadata.third_party.security.tag: 470*9e94795aSAndroid Build Coastguard Worker if not tag.startswith(NVD_CPE23): 471*9e94795aSAndroid Build Coastguard Worker report[ISSUE_UNKNOWN_SECURITY_TAG_TYPE].append( 472*9e94795aSAndroid Build Coastguard Worker f'Unknown security tag type: {tag} in {metadata_file_path}/METADATA') 473*9e94795aSAndroid Build Coastguard Worker else: 474*9e94795aSAndroid Build Coastguard Worker report[ISSUE_NO_METADATA_FILE].append( 475*9e94795aSAndroid Build Coastguard Worker "installed_file: {}, module_path: {}".format( 476*9e94795aSAndroid Build Coastguard Worker installed_file_metadata['installed_file'], installed_file_metadata['module_path'])) 477*9e94795aSAndroid Build Coastguard Worker 478*9e94795aSAndroid Build Coastguard Worker 479*9e94795aSAndroid Build Coastguard Worker# If a file is from a source fork or prebuilt fork package, add its package information to SBOM 480*9e94795aSAndroid Build Coastguard Workerdef add_package_of_file(file_id, file_metadata, doc, report): 481*9e94795aSAndroid Build Coastguard Worker metadata_file_path = get_metadata_file_path(file_metadata) 482*9e94795aSAndroid Build Coastguard Worker report_metadata_file(metadata_file_path, file_metadata, report) 483*9e94795aSAndroid Build Coastguard Worker 484*9e94795aSAndroid Build Coastguard Worker external_doc_ref, pkgs, rels, licenses = get_sbom_fragments(file_metadata, metadata_file_path) 485*9e94795aSAndroid Build Coastguard Worker if len(pkgs) > 0: 486*9e94795aSAndroid Build Coastguard Worker if external_doc_ref: 487*9e94795aSAndroid Build Coastguard Worker doc.add_external_ref(external_doc_ref) 488*9e94795aSAndroid Build Coastguard Worker for p in pkgs: 489*9e94795aSAndroid Build Coastguard Worker doc.add_package(p) 490*9e94795aSAndroid Build Coastguard Worker for rel in rels: 491*9e94795aSAndroid Build Coastguard Worker doc.add_relationship(rel) 492*9e94795aSAndroid Build Coastguard Worker fork_package_id = pkgs[0].id # The first package should be the source/prebuilt fork package 493*9e94795aSAndroid Build Coastguard Worker doc.add_relationship(sbom_data.Relationship(id1=file_id, 494*9e94795aSAndroid Build Coastguard Worker relationship=sbom_data.RelationshipType.GENERATED_FROM, 495*9e94795aSAndroid Build Coastguard Worker id2=fork_package_id)) 496*9e94795aSAndroid Build Coastguard Worker for license in licenses: 497*9e94795aSAndroid Build Coastguard Worker doc.add_license(license) 498*9e94795aSAndroid Build Coastguard Worker 499*9e94795aSAndroid Build Coastguard Worker 500*9e94795aSAndroid Build Coastguard Worker# Add STATIC_LINK relationship for static dependencies of a file 501*9e94795aSAndroid Build Coastguard Workerdef add_static_deps_of_file(file_id, file_metadata, doc): 502*9e94795aSAndroid Build Coastguard Worker if not file_metadata['static_dep_files'] and not file_metadata['whole_static_dep_files']: 503*9e94795aSAndroid Build Coastguard Worker return 504*9e94795aSAndroid Build Coastguard Worker static_dep_files = [] 505*9e94795aSAndroid Build Coastguard Worker if file_metadata['static_dep_files']: 506*9e94795aSAndroid Build Coastguard Worker static_dep_files += file_metadata['static_dep_files'].split(' ') 507*9e94795aSAndroid Build Coastguard Worker if file_metadata['whole_static_dep_files']: 508*9e94795aSAndroid Build Coastguard Worker static_dep_files += file_metadata['whole_static_dep_files'].split(' ') 509*9e94795aSAndroid Build Coastguard Worker 510*9e94795aSAndroid Build Coastguard Worker for dep_file in static_dep_files: 511*9e94795aSAndroid Build Coastguard Worker # Static libs are not shipped on devices, so names are derived from .intermediates paths. 512*9e94795aSAndroid Build Coastguard Worker doc.add_relationship(sbom_data.Relationship(id1=file_id, 513*9e94795aSAndroid Build Coastguard Worker relationship=sbom_data.RelationshipType.STATIC_LINK, 514*9e94795aSAndroid Build Coastguard Worker id2=new_file_id( 515*9e94795aSAndroid Build Coastguard Worker dep_file.removeprefix(args.soong_out + '/.intermediates/')))) 516*9e94795aSAndroid Build Coastguard Worker 517*9e94795aSAndroid Build Coastguard Worker 518*9e94795aSAndroid Build Coastguard Workerdef add_licenses_of_file(file_id, file_metadata, doc): 519*9e94795aSAndroid Build Coastguard Worker lics = db.get_module_licenses(file_metadata.get('name', ''), file_metadata['module_path']) 520*9e94795aSAndroid Build Coastguard Worker if lics: 521*9e94795aSAndroid Build Coastguard Worker file = next(f for f in doc.files if file_id == f.id) 522*9e94795aSAndroid Build Coastguard Worker for license_name, license_files in lics.items(): 523*9e94795aSAndroid Build Coastguard Worker if not license_files: 524*9e94795aSAndroid Build Coastguard Worker continue 525*9e94795aSAndroid Build Coastguard Worker license_id = new_license_id(license_name) 526*9e94795aSAndroid Build Coastguard Worker file.concluded_license_ids.append(license_id) 527*9e94795aSAndroid Build Coastguard Worker if license_name not in licenses_text: 528*9e94795aSAndroid Build Coastguard Worker license_text = get_license_text(license_files.split(' ')) 529*9e94795aSAndroid Build Coastguard Worker licenses_text[license_name] = license_text 530*9e94795aSAndroid Build Coastguard Worker 531*9e94795aSAndroid Build Coastguard Worker doc.add_license(sbom_data.License(id=license_id, name=license_name, text=licenses_text[license_name])) 532*9e94795aSAndroid Build Coastguard Worker 533*9e94795aSAndroid Build Coastguard Worker 534*9e94795aSAndroid Build Coastguard Workerdef get_all_transitive_static_dep_files_of_installed_files(installed_files_metadata, db, report): 535*9e94795aSAndroid Build Coastguard Worker # Find all transitive static dep files of all installed files 536*9e94795aSAndroid Build Coastguard Worker q = queue.Queue() 537*9e94795aSAndroid Build Coastguard Worker for installed_file_metadata in installed_files_metadata: 538*9e94795aSAndroid Build Coastguard Worker if installed_file_metadata['static_dep_files']: 539*9e94795aSAndroid Build Coastguard Worker for f in installed_file_metadata['static_dep_files'].split(' '): 540*9e94795aSAndroid Build Coastguard Worker q.put(f) 541*9e94795aSAndroid Build Coastguard Worker if installed_file_metadata['whole_static_dep_files']: 542*9e94795aSAndroid Build Coastguard Worker for f in installed_file_metadata['whole_static_dep_files'].split(' '): 543*9e94795aSAndroid Build Coastguard Worker q.put(f) 544*9e94795aSAndroid Build Coastguard Worker 545*9e94795aSAndroid Build Coastguard Worker all_static_dep_files = {} 546*9e94795aSAndroid Build Coastguard Worker while not q.empty(): 547*9e94795aSAndroid Build Coastguard Worker dep_file = q.get() 548*9e94795aSAndroid Build Coastguard Worker if dep_file in all_static_dep_files: 549*9e94795aSAndroid Build Coastguard Worker # It has been processed 550*9e94795aSAndroid Build Coastguard Worker continue 551*9e94795aSAndroid Build Coastguard Worker 552*9e94795aSAndroid Build Coastguard Worker all_static_dep_files[dep_file] = True 553*9e94795aSAndroid Build Coastguard Worker soong_module = db.get_soong_module_of_built_file(dep_file) 554*9e94795aSAndroid Build Coastguard Worker if not soong_module: 555*9e94795aSAndroid Build Coastguard Worker # This should not happen, add to report[ISSUE_NO_MODULE_FOUND_FOR_STATIC_DEP] 556*9e94795aSAndroid Build Coastguard Worker report[ISSUE_NO_MODULE_FOUND_FOR_STATIC_DEP].append(f) 557*9e94795aSAndroid Build Coastguard Worker continue 558*9e94795aSAndroid Build Coastguard Worker 559*9e94795aSAndroid Build Coastguard Worker if soong_module['static_dep_files']: 560*9e94795aSAndroid Build Coastguard Worker for f in soong_module['static_dep_files'].split(' '): 561*9e94795aSAndroid Build Coastguard Worker if f not in all_static_dep_files: 562*9e94795aSAndroid Build Coastguard Worker q.put(f) 563*9e94795aSAndroid Build Coastguard Worker if soong_module['whole_static_dep_files']: 564*9e94795aSAndroid Build Coastguard Worker for f in soong_module['whole_static_dep_files'].split(' '): 565*9e94795aSAndroid Build Coastguard Worker if f not in all_static_dep_files: 566*9e94795aSAndroid Build Coastguard Worker q.put(f) 567*9e94795aSAndroid Build Coastguard Worker 568*9e94795aSAndroid Build Coastguard Worker return sorted(all_static_dep_files.keys()) 569*9e94795aSAndroid Build Coastguard Worker 570*9e94795aSAndroid Build Coastguard Worker 571*9e94795aSAndroid Build Coastguard Workerdef main(): 572*9e94795aSAndroid Build Coastguard Worker global args 573*9e94795aSAndroid Build Coastguard Worker args = get_args() 574*9e94795aSAndroid Build Coastguard Worker log('Args:', vars(args)) 575*9e94795aSAndroid Build Coastguard Worker 576*9e94795aSAndroid Build Coastguard Worker global db 577*9e94795aSAndroid Build Coastguard Worker db = compliance_metadata.MetadataDb(args.metadata) 578*9e94795aSAndroid Build Coastguard Worker if args.debug: 579*9e94795aSAndroid Build Coastguard Worker db.dump_debug_db(os.path.dirname(args.output_file) + '/compliance-metadata-debug.db') 580*9e94795aSAndroid Build Coastguard Worker 581*9e94795aSAndroid Build Coastguard Worker global metadata_file_protos 582*9e94795aSAndroid Build Coastguard Worker metadata_file_protos = {} 583*9e94795aSAndroid Build Coastguard Worker global licenses_text 584*9e94795aSAndroid Build Coastguard Worker licenses_text = {} 585*9e94795aSAndroid Build Coastguard Worker 586*9e94795aSAndroid Build Coastguard Worker product_package_id = sbom_data.SPDXID_PRODUCT 587*9e94795aSAndroid Build Coastguard Worker product_package_name = sbom_data.PACKAGE_NAME_PRODUCT 588*9e94795aSAndroid Build Coastguard Worker product_package = sbom_data.Package(id=product_package_id, 589*9e94795aSAndroid Build Coastguard Worker name=product_package_name, 590*9e94795aSAndroid Build Coastguard Worker download_location=sbom_data.VALUE_NONE, 591*9e94795aSAndroid Build Coastguard Worker version=args.build_version, 592*9e94795aSAndroid Build Coastguard Worker supplier='Organization: ' + args.product_mfr, 593*9e94795aSAndroid Build Coastguard Worker files_analyzed=True) 594*9e94795aSAndroid Build Coastguard Worker doc_name = args.build_version 595*9e94795aSAndroid Build Coastguard Worker doc = sbom_data.Document(name=doc_name, 596*9e94795aSAndroid Build Coastguard Worker namespace=f'https://www.google.com/sbom/spdx/android/{doc_name}', 597*9e94795aSAndroid Build Coastguard Worker creators=['Organization: ' + args.product_mfr], 598*9e94795aSAndroid Build Coastguard Worker describes=product_package_id) 599*9e94795aSAndroid Build Coastguard Worker 600*9e94795aSAndroid Build Coastguard Worker doc.packages.append(product_package) 601*9e94795aSAndroid Build Coastguard Worker doc.packages.append(sbom_data.Package(id=sbom_data.SPDXID_PLATFORM, 602*9e94795aSAndroid Build Coastguard Worker name=sbom_data.PACKAGE_NAME_PLATFORM, 603*9e94795aSAndroid Build Coastguard Worker download_location=sbom_data.VALUE_NONE, 604*9e94795aSAndroid Build Coastguard Worker version=args.build_version, 605*9e94795aSAndroid Build Coastguard Worker supplier='Organization: ' + args.product_mfr, 606*9e94795aSAndroid Build Coastguard Worker declared_license_ids=[sbom_data.SPDXID_LICENSE_APACHE])) 607*9e94795aSAndroid Build Coastguard Worker 608*9e94795aSAndroid Build Coastguard Worker # Report on some issues and information 609*9e94795aSAndroid Build Coastguard Worker report = { 610*9e94795aSAndroid Build Coastguard Worker ISSUE_NO_METADATA: [], 611*9e94795aSAndroid Build Coastguard Worker ISSUE_NO_METADATA_FILE: [], 612*9e94795aSAndroid Build Coastguard Worker ISSUE_METADATA_FILE_INCOMPLETE: [], 613*9e94795aSAndroid Build Coastguard Worker ISSUE_UNKNOWN_SECURITY_TAG_TYPE: [], 614*9e94795aSAndroid Build Coastguard Worker ISSUE_INSTALLED_FILE_NOT_EXIST: [], 615*9e94795aSAndroid Build Coastguard Worker ISSUE_NO_MODULE_FOUND_FOR_STATIC_DEP: [], 616*9e94795aSAndroid Build Coastguard Worker INFO_METADATA_FOUND_FOR_PACKAGE: [], 617*9e94795aSAndroid Build Coastguard Worker } 618*9e94795aSAndroid Build Coastguard Worker 619*9e94795aSAndroid Build Coastguard Worker # Get installed files and corresponding make modules' metadata if an installed file is from a make module. 620*9e94795aSAndroid Build Coastguard Worker installed_files_metadata = db.get_installed_files() 621*9e94795aSAndroid Build Coastguard Worker 622*9e94795aSAndroid Build Coastguard Worker # Find which Soong module an installed file is from and merge metadata from Make and Soong 623*9e94795aSAndroid Build Coastguard Worker for installed_file_metadata in installed_files_metadata: 624*9e94795aSAndroid Build Coastguard Worker soong_module = db.get_soong_module_of_installed_file(installed_file_metadata['installed_file']) 625*9e94795aSAndroid Build Coastguard Worker if soong_module: 626*9e94795aSAndroid Build Coastguard Worker # Merge soong metadata to make metadata 627*9e94795aSAndroid Build Coastguard Worker installed_file_metadata.update(soong_module) 628*9e94795aSAndroid Build Coastguard Worker else: 629*9e94795aSAndroid Build Coastguard Worker # For make modules soong_module_type should be empty 630*9e94795aSAndroid Build Coastguard Worker installed_file_metadata['soong_module_type'] = '' 631*9e94795aSAndroid Build Coastguard Worker installed_file_metadata['static_dep_files'] = '' 632*9e94795aSAndroid Build Coastguard Worker installed_file_metadata['whole_static_dep_files'] = '' 633*9e94795aSAndroid Build Coastguard Worker 634*9e94795aSAndroid Build Coastguard Worker # Scan the metadata and create the corresponding package and file records in SPDX 635*9e94795aSAndroid Build Coastguard Worker for installed_file_metadata in installed_files_metadata: 636*9e94795aSAndroid Build Coastguard Worker installed_file = installed_file_metadata['installed_file'] 637*9e94795aSAndroid Build Coastguard Worker module_path = installed_file_metadata['module_path'] 638*9e94795aSAndroid Build Coastguard Worker product_copy_files = installed_file_metadata['product_copy_files'] 639*9e94795aSAndroid Build Coastguard Worker kernel_module_copy_files = installed_file_metadata['kernel_module_copy_files'] 640*9e94795aSAndroid Build Coastguard Worker build_output_path = installed_file 641*9e94795aSAndroid Build Coastguard Worker installed_file = installed_file.removeprefix(args.product_out) 642*9e94795aSAndroid Build Coastguard Worker 643*9e94795aSAndroid Build Coastguard Worker if not installed_file_has_metadata(installed_file_metadata, report): 644*9e94795aSAndroid Build Coastguard Worker continue 645*9e94795aSAndroid Build Coastguard Worker if not (os.path.islink(build_output_path) or os.path.isfile(build_output_path)): 646*9e94795aSAndroid Build Coastguard Worker report[ISSUE_INSTALLED_FILE_NOT_EXIST].append(installed_file) 647*9e94795aSAndroid Build Coastguard Worker continue 648*9e94795aSAndroid Build Coastguard Worker 649*9e94795aSAndroid Build Coastguard Worker file_id = new_file_id(installed_file) 650*9e94795aSAndroid Build Coastguard Worker sha1 = checksum(build_output_path) 651*9e94795aSAndroid Build Coastguard Worker f = sbom_data.File(id=file_id, name=installed_file, checksum=sha1) 652*9e94795aSAndroid Build Coastguard Worker doc.files.append(f) 653*9e94795aSAndroid Build Coastguard Worker product_package.file_ids.append(file_id) 654*9e94795aSAndroid Build Coastguard Worker 655*9e94795aSAndroid Build Coastguard Worker if is_source_package(installed_file_metadata) or is_prebuilt_package(installed_file_metadata): 656*9e94795aSAndroid Build Coastguard Worker add_package_of_file(file_id, installed_file_metadata, doc, report) 657*9e94795aSAndroid Build Coastguard Worker 658*9e94795aSAndroid Build Coastguard Worker elif module_path or installed_file_metadata['is_platform_generated']: 659*9e94795aSAndroid Build Coastguard Worker # File from PLATFORM package 660*9e94795aSAndroid Build Coastguard Worker doc.add_relationship(sbom_data.Relationship(id1=file_id, 661*9e94795aSAndroid Build Coastguard Worker relationship=sbom_data.RelationshipType.GENERATED_FROM, 662*9e94795aSAndroid Build Coastguard Worker id2=sbom_data.SPDXID_PLATFORM)) 663*9e94795aSAndroid Build Coastguard Worker if installed_file_metadata['is_platform_generated']: 664*9e94795aSAndroid Build Coastguard Worker f.concluded_license_ids = [sbom_data.SPDXID_LICENSE_APACHE] 665*9e94795aSAndroid Build Coastguard Worker 666*9e94795aSAndroid Build Coastguard Worker elif product_copy_files: 667*9e94795aSAndroid Build Coastguard Worker # Format of product_copy_files: <source path>:<dest path> 668*9e94795aSAndroid Build Coastguard Worker src_path = product_copy_files.split(':')[0] 669*9e94795aSAndroid Build Coastguard Worker # So far product_copy_files are copied from directory system, kernel, hardware, frameworks and device, 670*9e94795aSAndroid Build Coastguard Worker # so process them as files from PLATFORM package 671*9e94795aSAndroid Build Coastguard Worker doc.add_relationship(sbom_data.Relationship(id1=file_id, 672*9e94795aSAndroid Build Coastguard Worker relationship=sbom_data.RelationshipType.GENERATED_FROM, 673*9e94795aSAndroid Build Coastguard Worker id2=sbom_data.SPDXID_PLATFORM)) 674*9e94795aSAndroid Build Coastguard Worker if installed_file_metadata['license_text']: 675*9e94795aSAndroid Build Coastguard Worker if installed_file_metadata['license_text'] == 'build/soong/licenses/LICENSE': 676*9e94795aSAndroid Build Coastguard Worker f.concluded_license_ids = [sbom_data.SPDXID_LICENSE_APACHE] 677*9e94795aSAndroid Build Coastguard Worker 678*9e94795aSAndroid Build Coastguard Worker elif installed_file.endswith('.fsv_meta'): 679*9e94795aSAndroid Build Coastguard Worker doc.add_relationship(sbom_data.Relationship(id1=file_id, 680*9e94795aSAndroid Build Coastguard Worker relationship=sbom_data.RelationshipType.GENERATED_FROM, 681*9e94795aSAndroid Build Coastguard Worker id2=sbom_data.SPDXID_PLATFORM)) 682*9e94795aSAndroid Build Coastguard Worker f.concluded_license_ids = [sbom_data.SPDXID_LICENSE_APACHE] 683*9e94795aSAndroid Build Coastguard Worker 684*9e94795aSAndroid Build Coastguard Worker elif kernel_module_copy_files.startswith('ANDROID-GEN'): 685*9e94795aSAndroid Build Coastguard Worker # For the four files generated for _dlkm, _ramdisk partitions 686*9e94795aSAndroid Build Coastguard Worker doc.add_relationship(sbom_data.Relationship(id1=file_id, 687*9e94795aSAndroid Build Coastguard Worker relationship=sbom_data.RelationshipType.GENERATED_FROM, 688*9e94795aSAndroid Build Coastguard Worker id2=sbom_data.SPDXID_PLATFORM)) 689*9e94795aSAndroid Build Coastguard Worker 690*9e94795aSAndroid Build Coastguard Worker # Process static dependencies of the installed file 691*9e94795aSAndroid Build Coastguard Worker add_static_deps_of_file(file_id, installed_file_metadata, doc) 692*9e94795aSAndroid Build Coastguard Worker 693*9e94795aSAndroid Build Coastguard Worker # Add licenses of the installed file 694*9e94795aSAndroid Build Coastguard Worker add_licenses_of_file(file_id, installed_file_metadata, doc) 695*9e94795aSAndroid Build Coastguard Worker 696*9e94795aSAndroid Build Coastguard Worker # Add all static library files to SBOM 697*9e94795aSAndroid Build Coastguard Worker for dep_file in get_all_transitive_static_dep_files_of_installed_files(installed_files_metadata, db, report): 698*9e94795aSAndroid Build Coastguard Worker filepath = dep_file.removeprefix(args.soong_out + '/.intermediates/') 699*9e94795aSAndroid Build Coastguard Worker file_id = new_file_id(filepath) 700*9e94795aSAndroid Build Coastguard Worker # SHA1 of empty string. Sometimes .a files might not be built. 701*9e94795aSAndroid Build Coastguard Worker sha1 = 'SHA1: da39a3ee5e6b4b0d3255bfef95601890afd80709' 702*9e94795aSAndroid Build Coastguard Worker if os.path.islink(dep_file) or os.path.isfile(dep_file): 703*9e94795aSAndroid Build Coastguard Worker sha1 = checksum(dep_file) 704*9e94795aSAndroid Build Coastguard Worker doc.files.append(sbom_data.File(id=file_id, 705*9e94795aSAndroid Build Coastguard Worker name=filepath, 706*9e94795aSAndroid Build Coastguard Worker checksum=sha1)) 707*9e94795aSAndroid Build Coastguard Worker file_metadata = { 708*9e94795aSAndroid Build Coastguard Worker 'installed_file': dep_file, 709*9e94795aSAndroid Build Coastguard Worker 'is_prebuilt_make_module': False 710*9e94795aSAndroid Build Coastguard Worker } 711*9e94795aSAndroid Build Coastguard Worker file_metadata.update(db.get_soong_module_of_built_file(dep_file)) 712*9e94795aSAndroid Build Coastguard Worker add_package_of_file(file_id, file_metadata, doc, report) 713*9e94795aSAndroid Build Coastguard Worker 714*9e94795aSAndroid Build Coastguard Worker # Add relationships for static deps of static libraries 715*9e94795aSAndroid Build Coastguard Worker add_static_deps_of_file(file_id, file_metadata, doc) 716*9e94795aSAndroid Build Coastguard Worker 717*9e94795aSAndroid Build Coastguard Worker # Add licenses of the static lib 718*9e94795aSAndroid Build Coastguard Worker add_licenses_of_file(file_id, file_metadata, doc) 719*9e94795aSAndroid Build Coastguard Worker 720*9e94795aSAndroid Build Coastguard Worker # Save SBOM records to output file 721*9e94795aSAndroid Build Coastguard Worker doc.generate_packages_verification_code() 722*9e94795aSAndroid Build Coastguard Worker doc.created = datetime.datetime.now(tz=datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ') 723*9e94795aSAndroid Build Coastguard Worker prefix = args.output_file 724*9e94795aSAndroid Build Coastguard Worker if prefix.endswith('.spdx'): 725*9e94795aSAndroid Build Coastguard Worker prefix = prefix.removesuffix('.spdx') 726*9e94795aSAndroid Build Coastguard Worker elif prefix.endswith('.spdx.json'): 727*9e94795aSAndroid Build Coastguard Worker prefix = prefix.removesuffix('.spdx.json') 728*9e94795aSAndroid Build Coastguard Worker 729*9e94795aSAndroid Build Coastguard Worker output_file = prefix + '.spdx' 730*9e94795aSAndroid Build Coastguard Worker with open(output_file, 'w', encoding="utf-8") as file: 731*9e94795aSAndroid Build Coastguard Worker sbom_writers.TagValueWriter.write(doc, file) 732*9e94795aSAndroid Build Coastguard Worker if args.json: 733*9e94795aSAndroid Build Coastguard Worker with open(prefix + '.spdx.json', 'w', encoding="utf-8") as file: 734*9e94795aSAndroid Build Coastguard Worker sbom_writers.JSONWriter.write(doc, file) 735*9e94795aSAndroid Build Coastguard Worker 736*9e94795aSAndroid Build Coastguard Worker save_report(prefix + '-gen-report.txt', report) 737*9e94795aSAndroid Build Coastguard Worker 738*9e94795aSAndroid Build Coastguard Worker 739*9e94795aSAndroid Build Coastguard Workerif __name__ == '__main__': 740*9e94795aSAndroid Build Coastguard Worker main() 741