Skip to content

Commit 12066e3

Browse files
committed
refactor: improve extraction of subjects being build artifacts from witness provenances
Signed-off-by: Nathan Nguyen <nathan.nguyen@oracle.com>
1 parent f2ed7f7 commit 12066e3

File tree

3 files changed

+91
-61
lines changed

3 files changed

+91
-61
lines changed

src/macaron/slsa_analyzer/checks/provenance_witness_l1_check.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
from macaron.slsa_analyzer.checks.check_result import CheckResultData, CheckResultType, Confidence, JustificationType
1515
from macaron.slsa_analyzer.package_registry import JFrogMavenRegistry
1616
from macaron.slsa_analyzer.package_registry.jfrog_maven_registry import JFrogMavenAsset
17+
from macaron.slsa_analyzer.provenance.intoto.v01 import InTotoV01Subject
1718
from macaron.slsa_analyzer.provenance.witness import (
18-
WitnessProvenanceSubject,
19-
extract_witness_provenance_subjects,
19+
extract_build_artifacts_from_witness_subjects,
2020
is_witness_provenance_payload,
2121
load_witness_verifier_config,
2222
)
@@ -51,15 +51,15 @@ class WitnessProvenanceAvailableFacts(CheckFacts):
5151

5252
def verify_artifact_assets(
5353
artifact_assets: list[JFrogMavenAsset],
54-
subjects: set[WitnessProvenanceSubject],
54+
subjects: list[InTotoV01Subject],
5555
) -> bool:
5656
"""Verify artifact assets against subjects in the witness provenance payload.
5757
5858
Parameters
5959
----------
6060
artifact_assets : list[JFrogMavenAsset]
6161
List of artifact assets to verify.
62-
subjects : list[WitnessProvenanceSubject]
62+
subjects : list[InTotoV01Subject]
6363
List of subjects extracted from the in the witness provenance.
6464
6565
Returns
@@ -70,12 +70,12 @@ def verify_artifact_assets(
7070
# A look-up table to verify:
7171
# 1. if the name of the artifact appears in any subject of the witness provenance, then
7272
# 2. if the digest of the artifact could be found
73-
look_up: dict[str, dict[str, WitnessProvenanceSubject]] = {}
73+
look_up: dict[str, dict[str, InTotoV01Subject]] = {}
7474

7575
for subject in subjects:
76-
if subject.artifact_name not in look_up:
77-
look_up[subject.artifact_name] = {}
78-
look_up[subject.artifact_name][subject.sha256_digest] = subject
76+
if subject["name"] not in look_up:
77+
look_up[subject["name"]] = {}
78+
look_up[subject["name"]][subject["digest"]["sha256"]] = subject
7979

8080
for asset in artifact_assets:
8181
if asset.name not in look_up:
@@ -93,7 +93,7 @@ def verify_artifact_assets(
9393
logger.info(
9494
"Successfully verified asset '%s' against the subject '%s' in the provenance.",
9595
asset.name,
96-
subject.subject_name,
96+
subject["name"],
9797
)
9898

9999
return True
@@ -167,7 +167,7 @@ def run_check(self, ctx: AnalyzeContext) -> CheckResultData:
167167
version=provenance.asset.version,
168168
extensions=witness_verifier_config.artifact_extensions,
169169
)
170-
subjects = extract_witness_provenance_subjects(provenance.payload)
170+
subjects = extract_build_artifacts_from_witness_subjects(provenance.payload)
171171

172172
if not verify_artifact_assets(artifact_assets, subjects):
173173
return CheckResultData(

src/macaron/slsa_analyzer/provenance/witness/__init__.py

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2023 - 2023, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2023 - 2024, Oracle and/or its affiliates. All rights reserved.
22
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.
33

44
"""Witness provenance (https://github.com/testifysec/witness)."""
@@ -9,6 +9,7 @@
99
from macaron.config.defaults import defaults
1010
from macaron.slsa_analyzer.asset import AssetLocator
1111
from macaron.slsa_analyzer.provenance.intoto import InTotoPayload, InTotoV01Payload
12+
from macaron.slsa_analyzer.provenance.intoto.v01 import InTotoV01Subject
1213
from macaron.slsa_analyzer.provenance.witness.attestor import GitLabWitnessAttestor, RepoAttestor
1314

1415
logger: logging.Logger = logging.getLogger(__name__)
@@ -120,7 +121,7 @@ def extract_repo_url(witness_payload: InTotoPayload) -> str | None:
120121
return None
121122

122123

123-
def extract_witness_provenance_subjects(witness_payload: InTotoPayload) -> set[WitnessProvenanceSubject]:
124+
def extract_build_artifacts_from_witness_subjects(witness_payload: InTotoPayload) -> list[InTotoV01Subject]:
124125
"""Read the ``"subjects"`` field of the provenance to obtain the hash digests of each subject.
125126
126127
Parameters
@@ -133,28 +134,25 @@ def extract_witness_provenance_subjects(witness_payload: InTotoPayload) -> set[W
133134
134135
Returns
135136
-------
136-
dict[str, str]
137+
list[InTotoV01Subject]
137138
A dictionary in which each key is a subject name and each value is the corresponding SHA256 digest.
138139
"""
139-
if isinstance(witness_payload, InTotoV01Payload):
140-
subjects = witness_payload.statement["subject"]
141-
subject_digests = set()
142-
143-
for subject in subjects:
144-
name = subject["name"]
145-
digest = subject["digest"]
146-
147-
sha256 = digest.get("sha256")
148-
if not sha256 or not isinstance(sha256, str):
149-
continue
150-
151-
subject_digests.add(
152-
WitnessProvenanceSubject(
153-
subject_name=name,
154-
sha256_digest=sha256,
155-
)
156-
)
157-
158-
return subject_digests
159-
160-
return set()
140+
if not isinstance(witness_payload, InTotoV01Payload):
141+
return []
142+
143+
subjects = witness_payload.statement["subject"]
144+
artifact_subjects = []
145+
for subject in subjects:
146+
# Filter all subjects attested by the product attestor, which records all changed and
147+
# created files in the build process.
148+
# Documentation: https://github.com/in-toto/witness/blob/main/docs/attestors/product.md
149+
if not subject["name"].startswith("https://witness.dev/attestations/product/v0.1/file:"):
150+
continue
151+
152+
digest = subject["digest"]
153+
sha256 = digest.get("sha256")
154+
if not sha256 or not isinstance(sha256, str):
155+
continue
156+
artifact_subjects.append(subject)
157+
158+
return artifact_subjects

tests/slsa_analyzer/provenance/test_witness_provenance.py

Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2023 - 2023, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2023 - 2024, Oracle and/or its affiliates. All rights reserved.
22
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.
33

44
"""Tests for witness provenance."""
@@ -12,9 +12,8 @@
1212
from macaron.config.defaults import load_defaults
1313
from macaron.slsa_analyzer.provenance.intoto import InTotoV01Payload, v01
1414
from macaron.slsa_analyzer.provenance.witness import (
15-
WitnessProvenanceSubject,
1615
WitnessVerifierConfig,
17-
extract_witness_provenance_subjects,
16+
extract_build_artifacts_from_witness_subjects,
1817
is_witness_provenance_payload,
1918
load_witness_verifier_config,
2019
)
@@ -124,18 +123,20 @@ def test_is_witness_provenance_payload(
124123
}
125124
"""
126125
),
127-
{
128-
WitnessProvenanceSubject(
129-
subject_name=(
130-
"https://witness.dev/attestations/product/v0.1/file:target/jackson-annotations-2.9.9.jar"
131-
),
132-
sha256_digest="6f97fe2094bd50435d6fbb7a2f6c2638fe44e6af17cfff98ce111d0abfffe17e",
133-
),
134-
WitnessProvenanceSubject(
135-
subject_name="https://witness.dev/attestations/product/v0.1/file:foo/bar/baz.txt",
136-
sha256_digest="cbc8f554dbfa17e5c5873c425a09cb1488c2f784ac52340747a92b7ec0aaefba",
137-
),
138-
},
126+
[
127+
{
128+
"name": "https://witness.dev/attestations/product/v0.1/file:target/jackson-annotations-2.9.9.jar",
129+
"digest": {
130+
"sha256": "6f97fe2094bd50435d6fbb7a2f6c2638fe44e6af17cfff98ce111d0abfffe17e",
131+
},
132+
},
133+
{
134+
"name": "https://witness.dev/attestations/product/v0.1/file:foo/bar/baz.txt",
135+
"digest": {
136+
"sha256": "cbc8f554dbfa17e5c5873c425a09cb1488c2f784ac52340747a92b7ec0aaefba",
137+
},
138+
},
139+
],
139140
id="Valid payload",
140141
),
141142
pytest.param(
@@ -159,22 +160,53 @@ def test_is_witness_provenance_payload(
159160
}
160161
"""
161162
),
162-
{
163-
WitnessProvenanceSubject(
164-
subject_name=(
165-
"https://witness.dev/attestations/product/v0.1/file:target/jackson-annotations-2.9.9.jar"
166-
),
167-
sha256_digest="6f97fe2094bd50435d6fbb7a2f6c2638fe44e6af17cfff98ce111d0abfffe17e",
168-
),
169-
},
163+
[
164+
{
165+
"name": "https://witness.dev/attestations/product/v0.1/file:target/jackson-annotations-2.9.9.jar",
166+
"digest": {
167+
"sha256": "6f97fe2094bd50435d6fbb7a2f6c2638fe44e6af17cfff98ce111d0abfffe17e",
168+
},
169+
}
170+
],
170171
id="Missing sha256",
171172
),
173+
pytest.param(
174+
json.loads(
175+
"""
176+
{
177+
"subject": [
178+
{
179+
"name": "https://witness.dev/attestations/git/v0.1/authoremail:foo.bar@oracle.com",
180+
"digest": {
181+
"sha256": "923e32b55b983525acfd0df3ad18bbb016623bdf33ba7706c7ab8318ff1284a1"
182+
}
183+
},
184+
{
185+
"name": "https://witness.dev/attestations/product/v0.1/file:target/jackson-annotations-2.9.9.jar",
186+
"digest": {
187+
"sha256": "6f97fe2094bd50435d6fbb7a2f6c2638fe44e6af17cfff98ce111d0abfffe17e"
188+
}
189+
}
190+
]
191+
}
192+
"""
193+
),
194+
[
195+
{
196+
"name": "https://witness.dev/attestations/product/v0.1/file:target/jackson-annotations-2.9.9.jar",
197+
"digest": {
198+
"sha256": "6f97fe2094bd50435d6fbb7a2f6c2638fe44e6af17cfff98ce111d0abfffe17e",
199+
},
200+
}
201+
],
202+
id="Not a subject attested by the product attestor",
203+
),
172204
],
173205
)
174-
def test_extract_witness_provenances_subjects(
206+
def test_extract_build_artifacts_from_witness_subjects(
175207
payload_json: v01.InTotoV01Statement,
176-
expected_subjects: set[WitnessProvenanceSubject],
208+
expected_subjects: list[v01.InTotoV01Subject],
177209
) -> None:
178210
"""Test the ``extract_witness_provenance_subjects`` function."""
179211
payload = InTotoV01Payload(statement=payload_json)
180-
assert extract_witness_provenance_subjects(payload) == expected_subjects
212+
assert extract_build_artifacts_from_witness_subjects(payload) == expected_subjects

0 commit comments

Comments
 (0)