Skip to content

Commit 9b1fdfa

Browse files
committed
Addresses external reviewer feedback
* Adds dependency relationships between top-level CPython package and vendored packages. * Removes directory prefix in file names to make diffs more consistent across different releases. * Gets release-tool commit SHA for tool version
1 parent 12982a1 commit 9b1fdfa

1 file changed

Lines changed: 31 additions & 19 deletions

File tree

sbom.py

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import json
99
import os
1010
import re
11+
import subprocess
1112
import sys
1213
import tarfile
1314

@@ -19,7 +20,7 @@ def spdx_id(value: str) -> str:
1920

2021
def calculate_package_verification_codes(sbom) -> None:
2122
"""
22-
Calculate SPDX 'PackageVerificationCode' values for
23+
Calculate SPDX 'packageVerificationCode' values for
2324
each package with 'filesAnalyzed' set to 'true'.
2425
Mutates the values within the passed structure.
2526
@@ -90,6 +91,14 @@ def calculate_package_verification_codes(sbom) -> None:
9091
}
9192

9293

94+
def get_release_tools_commit_sha() -> str:
95+
"""Gets the git commit SHA of the release-tools repository"""
96+
git_prefix = os.path.abspath(os.path.dirname(__file__))
97+
stdout = subprocess.check_output(["git", "rev-parse", "--prefix", git_prefix, "HEAD"]).decode("ascii")
98+
assert re.match(r"^[a-f0-9]{40,}$", stdout)
99+
return stdout
100+
101+
93102
def create_sbom_for_source_tarball(tarball_path: str):
94103
"""Stitches together an SBOM for a source tarball"""
95104
tarball_name = os.path.basename(tarball_path)
@@ -114,7 +123,8 @@ def create_sbom_for_source_tarball(tarball_path: str):
114123

115124
# There should be an SBOM included in the tarball.
116125
# If there's not we can't create an SBOM.
117-
sbom_bytes = tarball.extractfile(tarball.getmember("Misc/sbom.spdx.json")).read()
126+
sbom_tarball_member = tarball.getmember(f"Python-{cpython_version}/Misc/sbom.spdx.json")
127+
sbom_bytes = tarball.extractfile(sbom_tarball_member).read()
118128

119129
sbom = json.loads(sbom_bytes)
120130
sbom.update({
@@ -132,7 +142,7 @@ def create_sbom_for_source_tarball(tarball_path: str):
132142
),
133143
"creators": [
134144
"Person: Python Release Managers",
135-
"Tool: python/release-tools@f58cfa6611dd13f2fb4e4790a8c54f06dddab6bc",
145+
f"Tool: ReleaseTools-{get_release_tools_commit_sha()}",
136146
],
137147
# Version of the SPDX License ID list.
138148
# This shouldn't need to be updated often, if ever.
@@ -161,26 +171,26 @@ def create_sbom_for_source_tarball(tarball_path: str):
161171
"downloadLocation": tarball_download_location,
162172
"checksums": [{"algorithm": "SHA256", "checksumValue": tarball_checksum_sha256}],
163173
}
174+
175+
# The top-level CPython package depends on every vendored sub-package.
176+
for sbom_package in sbom["packages"]:
177+
sbom["relationships"].append({
178+
"spdxElementId": sbom_cpython_package["SPDXID"],
179+
"relatedSpdxElement": sbom_package["SPDXID"],
180+
"relationshipType": "DEPENDS_ON",
181+
})
182+
164183
sbom["packages"].append(sbom_cpython_package)
165184

166185
# Extract all currently known files from the SBOM with their checksums.
167186
known_sbom_files = {}
168187
for sbom_file in sbom["files"]:
169188
sbom_filename = sbom_file["fileName"]
170189

171-
# We use the name we're expecting in the tarball here
172-
# which is to prefix the name with 'Python-{version}/...'.
173-
expected_tar_filename = f"Python-{cpython_version}/{sbom_filename}"
174-
175-
# We also want to update our SBOM to use the same filenames
176-
# as the ones in the tarball. We maintain the SPDXIDs though
177-
# to not need to rewrite SBOM relationships.
178-
sbom_file["fileName"] = expected_tar_filename
179-
180190
# Look for the expected SHA256 checksum.
181191
for sbom_file_checksum in sbom_file["checksums"]:
182192
if sbom_file_checksum["algorithm"] == "SHA256":
183-
known_sbom_files[expected_tar_filename] = (
193+
known_sbom_files[sbom_filename] = (
184194
sbom_file_checksum["checksumValue"]
185195
)
186196
break
@@ -206,21 +216,23 @@ def create_sbom_for_source_tarball(tarball_path: str):
206216
actual_file_checksum_sha1 = hashlib.sha1(file_bytes).hexdigest()
207217
actual_file_checksum_sha256 = hashlib.sha256(file_bytes).hexdigest()
208218

219+
# Remove the 'Python-{version}/...' prefix for the SPDXID and fileName.
220+
member_name_no_prefix = member.name.split('/', 1)[1]
221+
209222
# We've already seen this file, so we check it hasn't been modified and continue on.
210-
if member.name in known_sbom_files:
223+
if member_name_no_prefix in known_sbom_files:
211224
# If there's a hash mismatch we raise an error, something isn't right!
212-
expected_file_checksum_sha256 = known_sbom_files.pop(member.name)
225+
expected_file_checksum_sha256 = known_sbom_files.pop(member_name_no_prefix)
213226
if expected_file_checksum_sha256 != actual_file_checksum_sha256:
214-
raise ValueError(f"Mismatched checksum for file '{member.name}'")
227+
raise ValueError(f"Mismatched checksum for file '{member_name_no_prefix}'")
215228

216229
# If this is a new file, then it's a part of the 'CPython' SBOM package.
217230
else:
218-
# Remove the 'Python-{version}/...' prefix for the SPDXID.
219-
sbom_file_spdx_id = spdx_id(f"SPDXRef-FILE-{member.name.split('/', 1)[1]}")
231+
sbom_file_spdx_id = spdx_id(f"SPDXRef-FILE-{member_name_no_prefix}")
220232
sbom["files"].append(
221233
{
222234
"SPDXID": sbom_file_spdx_id,
223-
"fileName": member.name,
235+
"fileName": member_name_no_prefix,
224236
"checksums": [
225237
{
226238
"algorithm": "SHA1",

0 commit comments

Comments
 (0)