8000 Sort arrays in SBOM JSON data for reproducibility · python/release-tools@e2c2bb1 · GitHub
[go: up one dir, main page]

Skip to content

Commit e2c2bb1

Browse files
authored
Sort arrays in SBOM JSON data for reproducibility
1 parent 5b7ef45 commit e2c2bb1

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

sbom.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,29 @@ def get_release_tools_commit_sha() -> str:
109109
return stdout
110110

111111

112+
def normalize_sbom_data(sbom_data):
113+
"""
114+
Normalize SBOM data in-place by recursion
115+
and sorting lists by some repeatable key.
116+
"""
117+
def recursive_sort_in_place(value):
118+
if isinstance(value, list):
119+
# We need to recurse first so bottom-most elements are sorted first.
120+
for item in value:
121+
recursive_sort_in_place(item)
122+
123+
# Otherwise this key might change depending on the unsorted order of items.
124+
value.sort(key=lambda item: json.dumps(item, sort_keys=True))
125+
126+
# Dictionaries are the only other containers and keys
127+
# are already handled by json.dumps(sort_keys=True).
128+
elif isinstance(value, dict):
129+
for dict_val in value.values():
130+
recursive_sort_in_place(dict_val)
131+
132+
recursive_sort_in_place(sbom_data)
133+
134+
112135
def create_sbom_for_source_tarball(tarball_path: str):
113136
"""Stitches together an SBOM for a source tarball"""
114137
tarball_name = os.path.basename(tarball_path)
@@ -294,6 +317,9 @@ def create_sbom_for_source_tarball(tarball_path: str):
294317
# Calculate the 'packageVerificationCode' values for files in packages.
295318
calculate_package_verification_codes(sbom)
296319

320+
# Normalize SBOM structures for reproducibility.
321+
normalize_sbom_data(sbom)
322+
297323
return sbom
298324

299325

tests/test_sbom.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,19 @@ def test_calculate_package_verification_code(package_sha1s, package_verification
4242
assert input_sbom["packages"][0]["packageVerificationCode"] == {
4343
"packageVerificationCodeValue": package_verification_code
4444
}
45+
46+
47+
def test_normalization():
48+
# Test that arbitrary JSON data can be normalized.
49+
# Normalization doesn't have to make too much sense,
50+
# only needs to be reproducible.
51+
data = {
52+
"a": [1, 2, 3, {"b": [4, "c", [7, True, "2", {}]]}],
53+
# This line tests that inner structures are sorted first.
54+
"b": [[1, 2, "b"], [2, 1, "a"]]
55+
}
56+
sbom.normalize_sbom_data(data)
57+
assert data == {
58+
"a": [1, 2, 3, {"b": ["c", 4, ["2", 7, True, {}]]}],
59+
"b": [["a", 1, 2], ["b", 1, 2]]
60+
}

0 commit comments

Comments
 (0)
0