summaryrefslogtreecommitdiffstats
path: root/meta/lib/oe/spdx30_tasks.py
diff options
context:
space:
mode:
authorJoshua Watt <JPEWhacker@gmail.com>2024-12-10 10:33:07 -0700
committerRichard Purdie <richard.purdie@linuxfoundation.org>2024-12-12 12:55:56 +0000
commit102743c4dfe2ceedd4c0663ee6f6b14bb1f9f745 (patch)
treea21be43869aafb1f243ce3d8434705df43c16aa3 /meta/lib/oe/spdx30_tasks.py
parent9b80c039add2bdd7ed70774a4c379030df6538a5 (diff)
downloadpoky-102743c4dfe2ceedd4c0663ee6f6b14bb1f9f745.tar.gz
spdx 3.0: Rework how SPDX aliases are linked
The SPDX code needs to be able to look up an Element by its SPDX ID, locating the file that (should) contain the SPDX ID and opening it for parsing. Previously, the code would do this be hashing each Element SPDX ID and Alias, and the creating a symbolic link to the file that contains the element with a name of the hash. This worked well as it was possible to look up any arbitrary SPDX ID or alias by simply hashing it and following the symbolic link to get the file. However, the down side of this approach is that it creates a lot of symbolic links, since it will make one or two per Element in the document. This can be a problem when using SPDX_INCLUDE_SOURCES, for example. This change reworks this strategy so that the only Element that gets a symbolic link based on the hash is the singular SpdxDocument that is create for each file. All other Elements are assigned an alias with a special prefix that encodes the hash of SpdxDocument alias. Thus, when attempting to look up an arbitrary alias, the code sees the special prefix, extract the hash, opens the file based on the symlink with that hash name, then finds the matching Element in the file. This drastically reduces the number of symbolic links by making only one per file. This also means that the custom link extension can be removed since it is now superfluous. (From OE-Core rev: 838d64c09657ac53175737fc4e7fd6f01f3dcf47) Signed-off-by: Joshua Watt <JPEWhacker@gmail.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib/oe/spdx30_tasks.py')
-rw-r--r--meta/lib/oe/spdx30_tasks.py60
1 files changed, 38 insertions, 22 deletions
diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
index 3d7035909f..036c58bf4b 100644
--- a/meta/lib/oe/spdx30_tasks.py
+++ b/meta/lib/oe/spdx30_tasks.py
@@ -56,6 +56,7 @@ def add_license_expression(d, objset, license_expression, license_data):
56 name=name, 56 name=name,
57 ) 57 )
58 ) 58 )
59 objset.set_element_alias(lic)
59 simple_license_text[name] = lic 60 simple_license_text[name] = lic
60 61
61 if name == "PD": 62 if name == "PD":
@@ -106,7 +107,9 @@ def add_license_expression(d, objset, license_expression, license_data):
106 107
107 spdx_license = "LicenseRef-" + l 108 spdx_license = "LicenseRef-" + l
108 if spdx_license not in license_text_map: 109 if spdx_license not in license_text_map:
109 license_text_map[spdx_license] = add_license_text(l)._id 110 license_text_map[spdx_license] = oe.sbom30.get_element_link_id(
111 add_license_text(l)
112 )
110 113
111 return spdx_license 114 return spdx_license
112 115
@@ -277,7 +280,7 @@ def collect_dep_objsets(d, build):
277 for dep in deps: 280 for dep in deps:
278 bb.debug(1, "Fetching SPDX for dependency %s" % (dep.pn)) 281 bb.debug(1, "Fetching SPDX for dependency %s" % (dep.pn))
279 dep_build, dep_objset = oe.sbom30.find_root_obj_in_jsonld( 282 dep_build, dep_objset = oe.sbom30.find_root_obj_in_jsonld(
280 d, "recipes", dep.pn, oe.spdx30.build_Build 283 d, "recipes", "recipe-" + dep.pn, oe.spdx30.build_Build
281 ) 284 )
282 # If the dependency is part of the taskhash, return it to be linked 285 # If the dependency is part of the taskhash, return it to be linked
283 # against. Otherwise, it cannot be linked against because this recipe 286 # against. Otherwise, it cannot be linked against because this recipe
@@ -461,7 +464,7 @@ def create_spdx(d):
461 if not include_vex in ("none", "current", "all"): 464 if not include_vex in ("none", "current", "all"):
462 bb.fatal("SPDX_INCLUDE_VEX must be one of 'none', 'current', 'all'") 465 bb.fatal("SPDX_INCLUDE_VEX must be one of 'none', 'current', 'all'")
463 466
464 build_objset = oe.sbom30.ObjectSet.new_objset(d, d.getVar("PN")) 467 build_objset = oe.sbom30.ObjectSet.new_objset(d, "recipe-" + d.getVar("PN"))
465 468
466 build = build_objset.new_task_build("recipe", "recipe") 469 build = build_objset.new_task_build("recipe", "recipe")
467 build_objset.set_element_alias(build) 470 build_objset.set_element_alias(build)
@@ -501,8 +504,11 @@ def create_spdx(d):
501 bb.debug(1, "Skipping %s since it is already fixed upstream" % cve) 504 bb.debug(1, "Skipping %s since it is already fixed upstream" % cve)
502 continue 505 continue
503 506
507 spdx_cve = build_objset.new_cve_vuln(cve)
508 build_objset.set_element_alias(spdx_cve)
509
504 cve_by_status.setdefault(decoded_status["mapping"], {})[cve] = ( 510 cve_by_status.setdefault(decoded_status["mapping"], {})[cve] = (
505 build_objset.new_cve_vuln(cve), 511 spdx_cve,
506 decoded_status["detail"], 512 decoded_status["detail"],
507 decoded_status["description"], 513 decoded_status["description"],
508 ) 514 )
@@ -574,7 +580,7 @@ def create_spdx(d):
574 580
575 bb.debug(1, "Creating SPDX for package %s" % pkg_name) 581 bb.debug(1, "Creating SPDX for package %s" % pkg_name)
576 582
577 pkg_objset = oe.sbom30.ObjectSet.new_objset(d, pkg_name) 583 pkg_objset = oe.sbom30.ObjectSet.new_objset(d, "package-" + pkg_name)
578 584
579 spdx_package = pkg_objset.add_root( 585 spdx_package = pkg_objset.add_root(
580 oe.spdx30.software_Package( 586 oe.spdx30.software_Package(
@@ -662,20 +668,21 @@ def create_spdx(d):
662 for status, cves in cve_by_status.items(): 668 for status, cves in cve_by_status.items():
663 for cve, items in cves.items(): 669 for cve, items in cves.items():
664 spdx_cve, detail, description = items 670 spdx_cve, detail, description = items
671 spdx_cve_id = oe.sbom30.get_element_link_id(spdx_cve)
665 672
666 all_cves.add(spdx_cve._id) 673 all_cves.add(spdx_cve_id)
667 674
668 if status == "Patched": 675 if status == "Patched":
669 pkg_objset.new_vex_patched_relationship( 676 pkg_objset.new_vex_patched_relationship(
670 [spdx_cve._id], [spdx_package] 677 [spdx_cve_id], [spdx_package]
671 ) 678 )
672 elif status == "Unpatched": 679 elif status == "Unpatched":
673 pkg_objset.new_vex_unpatched_relationship( 680 pkg_objset.new_vex_unpatched_relationship(
674 [spdx_cve._id], [spdx_package] 681 [spdx_cve_id], [spdx_package]
675 ) 682 )
676 elif status == "Ignored": 683 elif status == "Ignored":
677 spdx_vex = pkg_objset.new_vex_ignored_relationship( 684 spdx_vex = pkg_objset.new_vex_ignored_relationship(
678 [spdx_cve._id], 685 [spdx_cve_id],
679 [spdx_package], 686 [spdx_package],
680 impact_statement=description, 687 impact_statement=description,
681 ) 688 )
@@ -810,7 +817,7 @@ def create_package_spdx(d):
810 d, 817 d,
811 pkg_arch, 818 pkg_arch,
812 "packages-staging", 819 "packages-staging",
813 pkg_name, 820 "package-" + pkg_name,
814 oe.spdx30.software_Package, 821 oe.spdx30.software_Package,
815 software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.install, 822 software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.install,
816 ) 823 )
@@ -849,7 +856,7 @@ def create_package_spdx(d):
849 dep_spdx_package, _ = oe.sbom30.find_root_obj_in_jsonld( 856 dep_spdx_package, _ = oe.sbom30.find_root_obj_in_jsonld(
850 d, 857 d,
851 "packages-staging", 858 "packages-staging",
852 dep_pkg, 859 "package-" + dep_pkg,
853 oe.spdx30.software_Package, 860 oe.spdx30.software_Package,
854 software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.install, 861 software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.install,
855 ) 862 )
@@ -949,13 +956,14 @@ def write_bitbake_spdx(d):
949 ) 956 )
950 957
951 for obj in objset.foreach_type(oe.spdx30.Element): 958 for obj in objset.foreach_type(oe.spdx30.Element):
952 obj.extension.append(oe.sbom30.OELinkExtension(link_spdx_id=False))
953 obj.extension.append(oe.sbom30.OEIdAliasExtension()) 959 obj.extension.append(oe.sbom30.OEIdAliasExtension())
954 960
955 oe.sbom30.write_jsonld_doc(d, objset, deploy_dir_spdx / "bitbake.spdx.json") 961 oe.sbom30.write_jsonld_doc(d, objset, deploy_dir_spdx / "bitbake.spdx.json")
956 962
957 963
958def collect_build_package_inputs(d, objset, build, packages): 964def collect_build_package_inputs(d, objset, build, packages):
965 import oe.sbom30
966
959 providers = oe.spdx_common.collect_package_providers(d) 967 providers = oe.spdx_common.collect_package_providers(d)
960 968
961 build_deps = set() 969 build_deps = set()
@@ -972,11 +980,11 @@ def collect_build_package_inputs(d, objset, build, packages):
972 pkg_spdx, _ = oe.sbom30.find_root_obj_in_jsonld( 980 pkg_spdx, _ = oe.sbom30.find_root_obj_in_jsonld(
973 d, 981 d,
974 "packages", 982 "packages",
975 pkg_name, 983 "package-" + pkg_name,
976 oe.spdx30.software_Package, 984 oe.spdx30.software_Package,
977 software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.install, 985 software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.install,
978 ) 986 )
979 build_deps.add(pkg_spdx._id) 987 build_deps.add(oe.sbom30.get_element_link_id(pkg_spdx))
980 988
981 if missing_providers: 989 if missing_providers:
982 bb.fatal( 990 bb.fatal(
@@ -1002,7 +1010,9 @@ def create_rootfs_spdx(d):
1002 with root_packages_file.open("r") as f: 1010 with root_packages_file.open("r") as f:
1003 packages = json.load(f) 1011 packages = json.load(f)
1004 1012
1005 objset = oe.sbom30.ObjectSet.new_objset(d, "%s-%s" % (image_basename, machine)) 1013 objset = oe.sbom30.ObjectSet.new_objset(
1014 d, "%s-%s-rootfs" % (image_basename, machine)
1015 )
1006 1016
1007 rootfs = objset.add_root( 1017 rootfs = objset.add_root(
1008 oe.spdx30.software_Package( 1018 oe.spdx30.software_Package(
@@ -1030,6 +1040,8 @@ def create_rootfs_spdx(d):
1030 1040
1031 1041
1032def create_image_spdx(d): 1042def create_image_spdx(d):
1043 import oe.sbom30
1044
1033 image_deploy_dir = Path(d.getVar("IMGDEPLOYDIR")) 1045 image_deploy_dir = Path(d.getVar("IMGDEPLOYDIR"))
1034 manifest_path = Path(d.getVar("IMAGE_OUTPUT_MANIFEST")) 1046 manifest_path = Path(d.getVar("IMAGE_OUTPUT_MANIFEST"))
1035 spdx_work_dir = Path(d.getVar("SPDXIMAGEWORK")) 1047 spdx_work_dir = Path(d.getVar("SPDXIMAGEWORK"))
@@ -1037,7 +1049,9 @@ def create_image_spdx(d):
1037 image_basename = d.getVar("IMAGE_BASENAME") 1049 image_basename = d.getVar("IMAGE_BASENAME")
1038 machine = d.getVar("MACHINE") 1050 machine = d.getVar("MACHINE")
1039 1051
1040 objset = oe.sbom30.ObjectSet.new_objset(d, "%s-%s" % (image_basename, machine)) 1052 objset = oe.sbom30.ObjectSet.new_objset(
1053 d, "%s-%s-image" % (image_basename, machine)
1054 )
1041 1055
1042 with manifest_path.open("r") as f: 1056 with manifest_path.open("r") as f:
1043 manifest = json.load(f) 1057 manifest = json.load(f)
@@ -1090,7 +1104,7 @@ def create_image_spdx(d):
1090 rootfs_image, _ = oe.sbom30.find_root_obj_in_jsonld( 1104 rootfs_image, _ = oe.sbom30.find_root_obj_in_jsonld(
1091 d, 1105 d,
1092 "rootfs", 1106 "rootfs",
1093 "%s-%s" % (image_basename, machine), 1107 "%s-%s-rootfs" % (image_basename, machine),
1094 oe.spdx30.software_Package, 1108 oe.spdx30.software_Package,
1095 # TODO: Should use a purpose to filter here? 1109 # TODO: Should use a purpose to filter here?
1096 ) 1110 )
@@ -1098,7 +1112,7 @@ def create_image_spdx(d):
1098 builds, 1112 builds,
1099 oe.spdx30.RelationshipType.hasInput, 1113 oe.spdx30.RelationshipType.hasInput,
1100 oe.spdx30.LifecycleScopeType.build, 1114 oe.spdx30.LifecycleScopeType.build,
1101 [rootfs_image._id], 1115 [oe.sbom30.get_element_link_id(rootfs_image)],
1102 ) 1116 )
1103 1117
1104 objset.add_aliases() 1118 objset.add_aliases()
@@ -1107,6 +1121,8 @@ def create_image_spdx(d):
1107 1121
1108 1122
1109def create_image_sbom_spdx(d): 1123def create_image_sbom_spdx(d):
1124 import oe.sbom30
1125
1110 image_name = d.getVar("IMAGE_NAME") 1126 image_name = d.getVar("IMAGE_NAME")
1111 image_basename = d.getVar("IMAGE_BASENAME") 1127 image_basename = d.getVar("IMAGE_BASENAME")
1112 image_link_name = d.getVar("IMAGE_LINK_NAME") 1128 image_link_name = d.getVar("IMAGE_LINK_NAME")
@@ -1121,17 +1137,17 @@ def create_image_sbom_spdx(d):
1121 rootfs_image, _ = oe.sbom30.find_root_obj_in_jsonld( 1137 rootfs_image, _ = oe.sbom30.find_root_obj_in_jsonld(
1122 d, 1138 d,
1123 "rootfs", 1139 "rootfs",
1124 "%s-%s" % (image_basename, machine), 1140 "%s-%s-rootfs" % (image_basename, machine),
1125 oe.spdx30.software_Package, 1141 oe.spdx30.software_Package,
1126 # TODO: Should use a purpose here? 1142 # TODO: Should use a purpose here?
1127 ) 1143 )
1128 root_elements.append(rootfs_image._id) 1144 root_elements.append(oe.sbom30.get_element_link_id(rootfs_image))
1129 1145
1130 image_objset, _ = oe.sbom30.find_jsonld( 1146 image_objset, _ = oe.sbom30.find_jsonld(
1131 d, "image", "%s-%s" % (image_basename, machine), required=True 1147 d, "image", "%s-%s-image" % (image_basename, machine), required=True
1132 ) 1148 )
1133 for o in image_objset.foreach_root(oe.spdx30.software_File): 1149 for o in image_objset.foreach_root(oe.spdx30.software_File):
1134 root_elements.append(o._id) 1150 root_elements.append(oe.sbom30.get_element_link_id(o))
1135 1151
1136 objset, sbom = oe.sbom30.create_sbom(d, image_name, root_elements) 1152 objset, sbom = oe.sbom30.create_sbom(d, image_name, root_elements)
1137 1153