diff options
| author | Joshua Watt <JPEWhacker@gmail.com> | 2024-12-10 10:33:07 -0700 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2024-12-12 12:55:56 +0000 |
| commit | 102743c4dfe2ceedd4c0663ee6f6b14bb1f9f745 (patch) | |
| tree | a21be43869aafb1f243ce3d8434705df43c16aa3 /meta/lib/oe | |
| parent | 9b80c039add2bdd7ed70774a4c379030df6538a5 (diff) | |
| download | poky-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')
| -rw-r--r-- | meta/lib/oe/sbom30.py | 159 | ||||
| -rw-r--r-- | meta/lib/oe/spdx30_tasks.py | 60 |
2 files changed, 97 insertions, 122 deletions
diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py index 9a3b188dbb..29cb9e45ad 100644 --- a/meta/lib/oe/sbom30.py +++ b/meta/lib/oe/sbom30.py | |||
| @@ -21,45 +21,8 @@ VEX_VERSION = "1.0.0" | |||
| 21 | 21 | ||
| 22 | SPDX_BUILD_TYPE = "http://openembedded.org/bitbake" | 22 | SPDX_BUILD_TYPE = "http://openembedded.org/bitbake" |
| 23 | 23 | ||
| 24 | 24 | OE_ALIAS_PREFIX = "http://spdxdocs.org/openembedded-alias/by-doc-hash/" | |
| 25 | @oe.spdx30.register(OE_SPDX_BASE + "link-extension") | 25 | OE_DOC_ALIAS_PREFIX = "http://spdxdocs.org/openembedded-alias/doc/" |
| 26 | class OELinkExtension(oe.spdx30.extension_Extension): | ||
| 27 | """ | ||
| 28 | This custom extension controls if an Element creates a symlink based on | ||
| 29 | its SPDX ID in the deploy directory. Some elements may not be able to be | ||
| 30 | linked because they are duplicated in multiple documents (e.g. the bitbake | ||
| 31 | Build Element). Those elements can add this extension and set link_spdx_id | ||
| 32 | to False | ||
| 33 | |||
| 34 | It is in internal extension that should be removed when writing out a final | ||
| 35 | SBoM | ||
| 36 | """ | ||
| 37 | |||
| 38 | CLOSED = True | ||
| 39 | INTERNAL = True | ||
| 40 | |||
| 41 | @classmethod | ||
| 42 | def _register_props(cls): | ||
| 43 | super()._register_props() | ||
| 44 | cls._add_property( | ||
| 45 | "link_spdx_id", | ||
| 46 | oe.spdx30.BooleanProp(), | ||
| 47 | OE_SPDX_BASE + "link-spdx-id", | ||
| 48 | min_count=1, | ||
| 49 | max_count=1, | ||
| 50 | ) | ||
| 51 | |||
| 52 | # The symlinks written to the deploy directory are based on the hash of | ||
| 53 | # the SPDX ID. While this makes it easy to look them up, it can be | ||
| 54 | # difficult to trace a Element to the hashed symlink name. As a | ||
| 55 | # debugging aid, this property is set to the basename of the symlink | ||
| 56 | # when the symlink is created to make it easier to trace | ||
| 57 | cls._add_property( | ||
| 58 | "link_name", | ||
| 59 | oe.spdx30.StringProp(), | ||
| 60 | OE_SPDX_BASE + "link-name", | ||
| 61 | max_count=1, | ||
| 62 | ) | ||
| 63 | 26 | ||
| 64 | 27 | ||
| 65 | @oe.spdx30.register(OE_SPDX_BASE + "id-alias") | 28 | @oe.spdx30.register(OE_SPDX_BASE + "id-alias") |
| @@ -185,18 +148,6 @@ def get_element_link_id(e): | |||
| 185 | return e._id | 148 | return e._id |
| 186 | 149 | ||
| 187 | 150 | ||
| 188 | def set_alias(obj, alias): | ||
| 189 | for ext in obj.extension: | ||
| 190 | if not isinstance(ext, OEIdAliasExtension): | ||
| 191 | continue | ||
| 192 | ext.alias = alias | ||
| 193 | return ext | ||
| 194 | |||
| 195 | ext = OEIdAliasExtension(alias=alias) | ||
| 196 | obj.extension.append(ext) | ||
| 197 | return ext | ||
| 198 | |||
| 199 | |||
| 200 | def get_alias(obj): | 151 | def get_alias(obj): |
| 201 | for ext in obj.extension: | 152 | for ext in obj.extension: |
| 202 | if not isinstance(ext, OEIdAliasExtension): | 153 | if not isinstance(ext, OEIdAliasExtension): |
| @@ -206,6 +157,10 @@ def get_alias(obj): | |||
| 206 | return None | 157 | return None |
| 207 | 158 | ||
| 208 | 159 | ||
| 160 | def hash_id(_id): | ||
| 161 | return hashlib.sha256(_id.encode("utf-8")).hexdigest() | ||
| 162 | |||
| 163 | |||
| 209 | def to_list(l): | 164 | def to_list(l): |
| 210 | if isinstance(l, set): | 165 | if isinstance(l, set): |
| 211 | l = sorted(list(l)) | 166 | l = sorted(list(l)) |
| @@ -220,6 +175,7 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): | |||
| 220 | def __init__(self, d): | 175 | def __init__(self, d): |
| 221 | super().__init__() | 176 | super().__init__() |
| 222 | self.d = d | 177 | self.d = d |
| 178 | self.alias_prefix = None | ||
| 223 | 179 | ||
| 224 | def create_index(self): | 180 | def create_index(self): |
| 225 | self.by_sha256_hash = {} | 181 | self.by_sha256_hash = {} |
| @@ -230,11 +186,10 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): | |||
| 230 | if isinstance(obj, oe.spdx30.Element): | 186 | if isinstance(obj, oe.spdx30.Element): |
| 231 | if not obj._id: | 187 | if not obj._id: |
| 232 | raise ValueError("Element missing ID") | 188 | raise ValueError("Element missing ID") |
| 233 | for ext in obj.extension: | 189 | |
| 234 | if not isinstance(ext, OEIdAliasExtension): | 190 | alias_ext = get_alias(obj) |
| 235 | continue | 191 | if alias_ext is not None and alias_ext.alias: |
| 236 | if ext.alias: | 192 | self.obj_by_id[alias_ext.alias] = obj |
| 237 | self.obj_by_id[ext.alias] = obj | ||
| 238 | 193 | ||
| 239 | for v in obj.verifiedUsing: | 194 | for v in obj.verifiedUsing: |
| 240 | if not isinstance(v, oe.spdx30.Hash): | 195 | if not isinstance(v, oe.spdx30.Hash): |
| @@ -248,6 +203,9 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): | |||
| 248 | super().add_index(obj) | 203 | super().add_index(obj) |
| 249 | if isinstance(obj, oe.spdx30.SpdxDocument): | 204 | if isinstance(obj, oe.spdx30.SpdxDocument): |
| 250 | self.doc = obj | 205 | self.doc = obj |
| 206 | alias_ext = get_alias(obj) | ||
| 207 | if alias_ext is not None and alias_ext.alias: | ||
| 208 | self.alias_prefix = OE_ALIAS_PREFIX + hash_id(alias_ext.alias) + "/" | ||
| 251 | 209 | ||
| 252 | def __filter_obj(self, obj, attr_filter): | 210 | def __filter_obj(self, obj, attr_filter): |
| 253 | return all(getattr(obj, k) == v for k, v in attr_filter.items()) | 211 | return all(getattr(obj, k) == v for k, v in attr_filter.items()) |
| @@ -307,6 +265,21 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): | |||
| 307 | for o in self.foreach_type(oe.spdx30.Element): | 265 | for o in self.foreach_type(oe.spdx30.Element): |
| 308 | self.set_element_alias(o) | 266 | self.set_element_alias(o) |
| 309 | 267 | ||
| 268 | def new_alias_id(self, obj, replace): | ||
| 269 | unihash = self.d.getVar("BB_UNIHASH") | ||
| 270 | namespace = self.get_namespace() + "/" | ||
| 271 | if unihash not in obj._id: | ||
| 272 | bb.warn(f"Unihash {unihash} not found in {obj._id}") | ||
| 273 | return None | ||
| 274 | |||
| 275 | if namespace not in obj._id: | ||
| 276 | bb.warn(f"Namespace {namespace} not found in {obj._id}") | ||
| 277 | return None | ||
| 278 | |||
| 279 | return obj._id.replace(unihash, "UNIHASH").replace( | ||
| 280 | namespace, replace + self.d.getVar("PN") | ||
| 281 | ) | ||
| 282 | |||
| 310 | def remove_internal_extensions(self): | 283 | def remove_internal_extensions(self): |
| 311 | def remove(o): | 284 | def remove(o): |
| 312 | o.extension = [e for e in o.extension if not getattr(e, "INTERNAL", False)] | 285 | o.extension = [e for e in o.extension if not getattr(e, "INTERNAL", False)] |
| @@ -334,21 +307,17 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): | |||
| 334 | 307 | ||
| 335 | alias_ext = get_alias(e) | 308 | alias_ext = get_alias(e) |
| 336 | if alias_ext is None: | 309 | if alias_ext is None: |
| 337 | unihash = self.d.getVar("BB_UNIHASH") | 310 | alias_id = self.new_alias_id(e, self.alias_prefix) |
| 338 | namespace = self.get_namespace() | 311 | if alias_id is not None: |
| 339 | if unihash not in e._id: | 312 | e.extension.append(OEIdAliasExtension(alias=alias_id)) |
| 340 | bb.warn(f"Unihash {unihash} not found in {e._id}") | 313 | elif ( |
| 341 | elif namespace not in e._id: | 314 | alias_ext.alias |
| 342 | bb.warn(f"Namespace {namespace} not found in {e._id}") | 315 | and not isinstance(e, oe.spdx30.SpdxDocument) |
| 343 | else: | 316 | and not alias_ext.alias.startswith(self.alias_prefix) |
| 344 | alias_ext = set_alias( | 317 | ): |
| 345 | e, | 318 | bb.warn( |
| 346 | e._id.replace(unihash, "UNIHASH").replace( | 319 | f"Element {e._id} has alias {alias_ext.alias}, but it should have prefix {self.alias_prefix}" |
| 347 | namespace, | 320 | ) |
| 348 | "http://spdx.org/spdxdocs/openembedded-alias/" | ||
| 349 | + self.d.getVar("PN"), | ||
| 350 | ), | ||
| 351 | ) | ||
| 352 | 321 | ||
| 353 | def new_spdxid(self, *suffix, include_unihash=True): | 322 | def new_spdxid(self, *suffix, include_unihash=True): |
| 354 | items = [self.get_namespace()] | 323 | items = [self.get_namespace()] |
| @@ -812,9 +781,17 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): | |||
| 812 | _id=objset.new_spdxid("document", name), | 781 | _id=objset.new_spdxid("document", name), |
| 813 | name=name, | 782 | name=name, |
| 814 | ) | 783 | ) |
| 815 | document.extension.append(OEIdAliasExtension()) | 784 | |
| 816 | document.extension.append(OELinkExtension(link_spdx_id=False)) | 785 | document.extension.append( |
| 786 | OEIdAliasExtension( | ||
| 787 | alias=objset.new_alias_id( | ||
| 788 | document, | ||
| 789 | OE_DOC_ALIAS_PREFIX + d.getVar("PN") + "/" + name + "/", | ||
| 790 | ), | ||
| 791 | ) | ||
| 792 | ) | ||
| 817 | objset.doc = document | 793 | objset.doc = document |
| 794 | objset.add_index(document) | ||
| 818 | 795 | ||
| 819 | if copy_from_bitbake_doc: | 796 | if copy_from_bitbake_doc: |
| 820 | bb_objset = objset.import_bitbake_build_objset() | 797 | bb_objset = objset.import_bitbake_build_objset() |
| @@ -907,9 +884,7 @@ def jsonld_arch_path(d, arch, subdir, name, deploydir=None): | |||
| 907 | return deploydir / arch / subdir / (name + ".spdx.json") | 884 | return deploydir / arch / subdir / (name + ".spdx.json") |
| 908 | 885 | ||
| 909 | 886 | ||
| 910 | def jsonld_hash_path(_id): | 887 | def jsonld_hash_path(h): |
| 911 | h = hashlib.sha256(_id.encode("utf-8")).hexdigest() | ||
| 912 | |||
| 913 | return Path("by-spdxid-hash") / h[:2], h | 888 | return Path("by-spdxid-hash") / h[:2], h |
| 914 | 889 | ||
| 915 | 890 | ||
| @@ -981,7 +956,7 @@ def write_recipe_jsonld_doc( | |||
| 981 | dest = jsonld_arch_path(d, pkg_arch, subdir, objset.doc.name, deploydir=deploydir) | 956 | dest = jsonld_arch_path(d, pkg_arch, subdir, objset.doc.name, deploydir=deploydir) |
| 982 | 957 | ||
| 983 | def link_id(_id): | 958 | def link_id(_id): |
| 984 | hash_path = jsonld_hash_path(_id) | 959 | hash_path = jsonld_hash_path(hash_id(_id)) |
| 985 | 960 | ||
| 986 | link_name = jsonld_arch_path( | 961 | link_name = jsonld_arch_path( |
| 987 | d, | 962 | d, |
| @@ -1005,28 +980,9 @@ def write_recipe_jsonld_doc( | |||
| 1005 | 980 | ||
| 1006 | try: | 981 | try: |
| 1007 | if create_spdx_id_links: | 982 | if create_spdx_id_links: |
| 1008 | for o in objset.foreach_type(oe.spdx30.Element): | 983 | alias_ext = get_alias(objset.doc) |
| 1009 | if not o._id or o._id.startswith("_:"): | 984 | if alias_ext is not None and alias_ext.alias: |
| 1010 | continue | 985 | alias_ext.link_name = link_id(alias_ext.alias) |
| 1011 | |||
| 1012 | ext = None | ||
| 1013 | for e in o.extension: | ||
| 1014 | if not isinstance(e, OELinkExtension): | ||
| 1015 | continue | ||
| 1016 | |||
| 1017 | ext = e | ||
| 1018 | break | ||
| 1019 | |||
| 1020 | if ext is None: | ||
| 1021 | ext = OELinkExtension(link_spdx_id=True) | ||
| 1022 | o.extension.append(ext) | ||
| 1023 | |||
| 1024 | if ext.link_spdx_id: | ||
| 1025 | ext.link_name = link_id(o._id) | ||
| 1026 | |||
| 1027 | alias_ext = get_alias(o) | ||
| 1028 | if alias_ext is not None and alias_ext.alias: | ||
| 1029 | alias_ext.link_name = link_id(alias_ext.alias) | ||
| 1030 | 986 | ||
| 1031 | finally: | 987 | finally: |
| 1032 | # It is really helpful for debugging if the JSON document is written | 988 | # It is really helpful for debugging if the JSON document is written |
| @@ -1055,7 +1011,10 @@ def load_obj_in_jsonld(d, arch, subdir, fn_name, obj_type, **attr_filter): | |||
| 1055 | 1011 | ||
| 1056 | 1012 | ||
| 1057 | def find_by_spdxid(d, spdxid, *, required=False): | 1013 | def find_by_spdxid(d, spdxid, *, required=False): |
| 1058 | return find_jsonld(d, *jsonld_hash_path(spdxid), required=required) | 1014 | if spdxid.startswith(OE_ALIAS_PREFIX): |
| 1015 | h = spdxid[len(OE_ALIAS_PREFIX) :].split("/", 1)[0] | ||
| 1016 | return find_jsonld(d, *jsonld_hash_path(h), required=required) | ||
| 1017 | return find_jsonld(d, *jsonld_hash_path(hash_id(spdxid)), required=required) | ||
| 1059 | 1018 | ||
| 1060 | 1019 | ||
| 1061 | def create_sbom(d, name, root_elements, add_objectsets=[]): | 1020 | def create_sbom(d, name, root_elements, add_objectsets=[]): |
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 | ||
| 958 | def collect_build_package_inputs(d, objset, build, packages): | 964 | def 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 | ||
| 1032 | def create_image_spdx(d): | 1042 | def 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 | ||
| 1109 | def create_image_sbom_spdx(d): | 1123 | def 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 | ||
