Skip to content

Commit 9d37a9c

Browse files
authored
feat: add support for SLSA v1 provenance with OCI build type (#778)
This PR adds support for SLSA v1 provenance files with the OCI build type. Signed-off-by: Ben Selwyn-Smith <[email protected]>
1 parent 707cf11 commit 9d37a9c

File tree

2 files changed

+88
-24
lines changed

2 files changed

+88
-24
lines changed

src/macaron/repo_finder/provenance_extractor.py

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -132,36 +132,44 @@ def _extract_from_slsa_v1(payload: InTotoV1Payload) -> tuple[str | None, str | N
132132
return None, None
133133

134134
# Extract the repository URL.
135-
repo = None
136-
if build_type == "https://slsa-framework.github.io/gcb-buildtypes/triggered-build/v1":
137-
repo = json_extract(build_def, ["externalParameters", "sourceToBuild", "repository"], str)
138-
if not repo:
139-
repo = json_extract(build_def, ["externalParameters", "configSource", "repository"], str)
140-
if build_type == "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1":
141-
repo = json_extract(build_def, ["externalParameters", "workflow", "repository"], str)
135+
match build_type:
136+
case "https://slsa-framework.github.io/gcb-buildtypes/triggered-build/v1":
137+
repo = json_extract(build_def, ["externalParameters", "sourceToBuild", "repository"], str)
138+
if not repo:
139+
repo = json_extract(build_def, ["externalParameters", "configSource", "repository"], str)
140+
case "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1":
141+
repo = json_extract(build_def, ["externalParameters", "workflow", "repository"], str)
142+
case "https://github.com/oracle/macaron/tree/main/src/macaron/resources/provenance-buildtypes/oci/v1":
143+
repo = json_extract(build_def, ["externalParameters", "source"], str)
144+
case _:
145+
logger.debug("Unsupported build type for SLSA v1: %s", build_type)
146+
return None, None
142147

143148
if not repo:
144-
logger.debug("Repo required to extract commit from SLSA v1.")
149+
logger.debug("Repo URL not found in SLSA v1 payload.")
145150
return None, None
146151

147152
# Extract the commit hash.
148153
commit = None
149-
deps = json_extract(build_def, ["resolvedDependencies"], list)
150-
if not deps:
151-
return repo, None
152-
for dep in deps:
153-
if not isinstance(dep, dict):
154-
continue
155-
uri = json_extract(dep, ["uri"], str)
156-
if not uri:
157-
continue
158-
url = _clean_spdx(uri)
159-
if url != repo:
160-
continue
161-
digest_set = json_extract(dep, ["digest"], dict)
162-
if not digest_set:
163-
continue
164-
commit = _extract_commit_from_digest_set(digest_set, SLSA_V1_DIGEST_SET_GIT_ALGORITHMS)
154+
if build_type == "https://github.com/oracle/macaron/tree/main/src/macaron/resources/provenance-buildtypes/oci/v1":
155+
commit = json_extract(build_def, ["internalParameters", "buildEnvVar", "BLD_COMMIT_HASH"], str)
156+
else:
157+
deps = json_extract(build_def, ["resolvedDependencies"], list)
158+
if not deps:
159+
return repo, None
160+
for dep in deps:
161+
if not isinstance(dep, dict):
162+
continue
163+
uri = json_extract(dep, ["uri"], str)
164+
if not uri:
165+
continue
166+
url = _clean_spdx(uri)
167+
if url != repo:
168+
continue
169+
digest_set = json_extract(dep, ["digest"], dict)
170+
if not digest_set:
171+
continue
172+
commit = _extract_commit_from_digest_set(digest_set, SLSA_V1_DIGEST_SET_GIT_ALGORITHMS)
165173

166174
return repo, commit or None
167175

tests/repo_finder/test_provenance_extractor.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,40 @@ def slsa_v1_github_provenance_() -> dict[str, JsonType]:
113113
)
114114

115115

116+
@pytest.fixture(name="slsa_v1_oci_provenance")
117+
def slsa_v1_oci_provenance_() -> dict[str, JsonType]:
118+
"""Return a valid SLSA v1 provenance using the OCI build type."""
119+
payload = _load_and_validate_json(
120+
"""
121+
{
122+
"_type": "https://in-toto.io/Statement/v1",
123+
"predicateType": "https://slsa.dev/provenance/v1",
124+
"subject": [],
125+
"predicate": {
126+
"buildDefinition": {
127+
"buildType": "",
128+
"externalParameters": {
129+
"source": "https://github.com/oracle/macaron"
130+
},
131+
"internalParameters": {
132+
"buildEnvVar": {
133+
"BLD_COMMIT_HASH": "51aa22a42ec1bffa71518041a6a6d42d40bf50f0"
134+
}
135+
}
136+
}
137+
}
138+
}
139+
"""
140+
)
141+
# The build type is modified here to avoid issues with excessive line length.
142+
_json_modify(
143+
payload,
144+
["predicate", "buildDefinition", "buildType"],
145+
"https://github.com/oracle/macaron/tree/main/src/macaron/resources/provenance-buildtypes/oci/v1",
146+
)
147+
return payload
148+
149+
116150
@pytest.fixture(name="slsa_v02_provenance")
117151
def slsa_v02_provenance_() -> dict[str, JsonType]:
118152
"""Return a valid SLSA v02 provenance."""
@@ -322,6 +356,28 @@ def test_slsa_v1_github_is_invalid(
322356
_test_extract_repo_and_commit_from_provenance(slsa_v1_github_provenance)
323357

324358

359+
def test_slsa_v1_oci_is_valid(
360+
slsa_v1_oci_provenance: dict[str, JsonType], target_repository: str, target_commit: str
361+
) -> None:
362+
"""Test SLSA v1 oci provenance."""
363+
_test_extract_repo_and_commit_from_provenance(slsa_v1_oci_provenance, target_repository, target_commit)
364+
365+
366+
@pytest.mark.parametrize(
367+
("keys", "new_value"),
368+
[
369+
(["predicate", "buildDefinition", "externalParameters", "source"], ""),
370+
(["predicate", "buildDefinition", "externalParameters", "source"], None),
371+
],
372+
)
373+
def test_slsa_v1_oci_is_invalid(
374+
slsa_v1_oci_provenance: dict[str, JsonType], keys: list[str], new_value: JsonType
375+
) -> None:
376+
"""Test invalidly modified SLSA v1 oci provenance."""
377+
_json_modify(slsa_v1_oci_provenance, keys, new_value)
378+
_test_extract_repo_and_commit_from_provenance(slsa_v1_oci_provenance)
379+
380+
325381
def test_slsa_v02_is_valid(
326382
slsa_v02_provenance: dict[str, JsonType], target_repository: str, target_commit: str
327383
) -> None:

0 commit comments

Comments
 (0)