@@ -43,8 +43,11 @@ def extract_repo_and_commit_from_provenance(payload: InTotoPayload) -> tuple[str
43
43
If the extraction process fails for any reason.
44
44
"""
45
45
predicate_type = payload .statement .get ("predicateType" )
46
- if isinstance (payload , InTotoV1Payload ) and predicate_type == "https://slsa.dev/provenance/v1" :
47
- return _extract_from_slsa_v1 (payload )
46
+ if isinstance (payload , InTotoV1Payload ):
47
+ if predicate_type == "https://slsa.dev/provenance/v1" :
48
+ return _extract_from_slsa_v1 (payload )
49
+ if predicate_type == "https://docs.pypi.org/attestations/publish/v1" :
50
+ return _extract_from_pypi_v1 (payload )
48
51
49
52
if isinstance (payload , InTotoV01Payload ):
50
53
if predicate_type == "https://slsa.dev/provenance/v0.2" :
@@ -195,6 +198,32 @@ def _extract_from_slsa_v1(payload: InTotoV1Payload) -> tuple[str | None, str | N
195
198
return repo , commit or None
196
199
197
200
201
+ def _extract_from_pypi_v1 (payload : InTotoV1Payload ) -> tuple [str | None , str | None ]:
202
+ """Extract the repository and commit metadata from the pypi provenance file found at the passed path.
203
+
204
+ This payload represents a custom predicate created from the certificate of a PyPI v1 attestation file.
205
+ By design, these attestations come without a predicate.
206
+
207
+ Parameters
208
+ ----------
209
+ payload: InTotoPayload
210
+ The payload to extract from.
211
+
212
+ Returns
213
+ -------
214
+ tuple[str, str]
215
+ The repository URL and commit hash if found, a pair of empty strings otherwise.
216
+ """
217
+ predicate : dict [str , JsonType ] | None = payload .statement .get ("predicate" )
218
+ if not predicate :
219
+ logger .debug ("No predicate in payload statement." )
220
+ return None , None
221
+
222
+ repo = json_extract (predicate , ["sourceUri" ], str )
223
+ digest = json_extract (predicate , ["sourceDigest" ], str )
224
+ return repo , digest
225
+
226
+
198
227
def _extract_from_witness_provenance (payload : InTotoV01Payload ) -> tuple [str | None , str | None ]:
199
228
"""Extract the repository and commit metadata from the witness provenance file found at the passed path.
200
229
@@ -300,7 +329,7 @@ def check_if_input_purl_provenance_conflict(
300
329
provenance_repo_url : str | None ,
301
330
purl : PackageURL ,
302
331
) -> bool :
303
- """Test if the input repository type PURL's repo and commit match the contents of the provenance.
332
+ """Test if the input repository type PURL's repo matches the contents of the provenance.
304
333
305
334
Parameters
306
335
----------
@@ -620,6 +649,41 @@ def get_build_invocation(self, statement: InTotoV01Statement | InTotoV1Statement
620
649
return gl_workflow , gl_job_url
621
650
622
651
652
+ class PyPICertificateDefinition (ProvenanceBuildDefinition ):
653
+ """Class representing the derived PyPI certificate build definition.
654
+
655
+ This class implements the abstract methods from the `ProvenanceBuildDefinition`
656
+ to extract build invocation details specific to the GitHub Actions build type.
657
+ """
658
+
659
+ #: Determines the expected ``buildType`` field in the provenance predicate.
660
+ expected_build_type = "pypi_certificate"
661
+
662
+ def get_build_invocation (self , statement : InTotoV01Statement | InTotoV1Statement ) -> tuple [str | None , str | None ]:
663
+ """Retrieve the build invocation information from the given statement.
664
+
665
+ Parameters
666
+ ----------
667
+ statement : InTotoV1Statement | InTotoV01Statement
668
+ The provenance statement from which to extract the build invocation
669
+ details. This statement contains the metadata about the build process
670
+ and its associated artifacts.
671
+
672
+ Returns
673
+ -------
674
+ tuple[str | None, str | None]
675
+ A tuple containing two elements:
676
+ - The first element is the build invocation entry point (e.g., workflow name), or None if not found.
677
+ - The second element is the invocation URL or identifier (e.g., job URL), or None if not found.
678
+ """
679
+ if statement ["predicate" ] is None :
680
+ return None , None
681
+
682
+ gha_workflow = json_extract (statement ["predicate" ], ["workflow" ], str )
683
+ invocation_url = json_extract (statement ["predicate" ], ["invocationUrl" ], str )
684
+ return gha_workflow , invocation_url
685
+
686
+
623
687
class ProvenancePredicate :
624
688
"""Class providing utility methods for handling provenance predicates.
625
689
@@ -685,6 +749,7 @@ def find_build_def(statement: InTotoV01Statement | InTotoV1Statement) -> Provena
685
749
SLSAGCBBuildDefinitionV1 (),
686
750
SLSAOCIBuildDefinitionV1 (),
687
751
WitnessGitLabBuildDefinitionV01 (),
752
+ PyPICertificateDefinition (),
688
753
]
689
754
690
755
for build_def in build_defs :
0 commit comments