@@ -4,11 +4,13 @@ import (
4
4
"crypto/x509"
5
5
"encoding/asn1"
6
6
"fmt"
7
+ "net/url"
7
8
"strings"
8
9
9
10
fulcio "github.com/sigstore/fulcio/pkg/certificate"
10
11
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
11
12
"github.com/slsa-framework/slsa-verifier/v2/options"
13
+ "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
12
14
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
13
15
)
14
16
@@ -24,23 +26,18 @@ var (
24
26
)
25
27
26
28
var defaultArtifactTrustedReusableWorkflows = map [string ]bool {
27
- trustedBuilderRepository + "/.github/workflows/generator_generic_slsa3.yml" : true ,
28
- trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml" : true ,
29
- trustedBuilderRepository + "/.github/workflows/builder_container-based_slsa3.yml" : true ,
29
+ common . GenericGeneratorBuilderID : true ,
30
+ common . GoBuilderID : true ,
31
+ common . ContainerBasedBuilderID : true ,
30
32
}
31
33
32
34
var defaultContainerTrustedReusableWorkflows = map [string ]bool {
33
- trustedBuilderRepository + "/.github/workflows/generator_container_slsa3.yml" : true ,
35
+ common . ContainerGeneratorBuilderID : true ,
34
36
}
35
37
36
- var (
37
- delegatorGenericReusableWorkflow = trustedBuilderRepository + "/.github/workflows/delegator_generic_slsa3.yml"
38
- delegatorLowPermsGenericReusableWorkflow = trustedBuilderRepository + "/.github/workflows/delegator_lowperms-generic_slsa3.yml"
39
- )
40
-
41
38
var defaultBYOBReusableWorkflows = map [string ]bool {
42
- delegatorGenericReusableWorkflow : true ,
43
- delegatorLowPermsGenericReusableWorkflow : true ,
39
+ common . GenericDelegatorBuilderID : true ,
40
+ common . GenericLowPermsDelegatorBuilderID : true ,
44
41
}
45
42
46
43
// VerifyCertficateSourceRepository verifies the source repository.
@@ -69,26 +66,27 @@ func VerifyBuilderIdentity(id *WorkflowIdentity,
69
66
// Issuer verification.
70
67
// NOTE: this is necessary before we do any further verification.
71
68
if id .Issuer != certOidcIssuer {
72
- return nil , false , fmt .Errorf ("%w: %s " , serrors .ErrorInvalidOIDCIssuer , id .Issuer )
69
+ return nil , false , fmt .Errorf ("%w: %q " , serrors .ErrorInvalidOIDCIssuer , id .Issuer )
73
70
}
74
71
75
- // cert URI path is /org/repo/path/to/workflow@ref
76
- workflowPath := strings .SplitN (id .SubjectWorkflowRef , "@" , 2 )
77
- if len (workflowPath ) < 2 {
78
- return nil , false , fmt .Errorf ("%w: workflow uri: %s" , serrors .ErrorMalformedURI , id .SubjectWorkflowRef )
72
+ // cert URI is https://github.com/org/repo/path/to/workflow@ref
73
+ // Remove '@' from Path
74
+ workflowID := id .SubjectWorkflowName ()
75
+ workflowTag := id .SubjectWorkflowRef ()
76
+
77
+ if workflowID == "" || workflowTag == "" {
78
+ return nil , false , fmt .Errorf ("%w: workflow uri: %q" , serrors .ErrorMalformedURI , id .SubjectWorkflow .String ())
79
79
}
80
80
81
81
// Verify trusted workflow.
82
- reusableWorkflowPath := strings .Trim (workflowPath [0 ], "/" )
83
- reusableWorkflowTag := strings .Trim (workflowPath [1 ], "/" )
84
- builderID , byob , err := verifyTrustedBuilderID (reusableWorkflowPath , reusableWorkflowTag ,
82
+ builderID , byob , err := verifyTrustedBuilderID (workflowID , workflowTag ,
85
83
builderOpts .ExpectedID , defaultBuilders )
86
84
if err != nil {
87
85
return nil , byob , err
88
86
}
89
87
90
88
// Verify the ref is a full semantic version tag.
91
- if err := verifyTrustedBuilderRef (id , reusableWorkflowTag ); err != nil {
89
+ if err := verifyTrustedBuilderRef (id , workflowTag ); err != nil {
92
90
return nil , byob , err
93
91
}
94
92
@@ -97,26 +95,25 @@ func VerifyBuilderIdentity(id *WorkflowIdentity,
97
95
98
96
// Verifies the builder ID at path against an expected builderID.
99
97
// If an expected builderID is not provided, uses the defaultBuilders.
100
- func verifyTrustedBuilderID (certPath , certTag string , expectedBuilderID * string , defaultTrustedBuilders map [string ]bool ) (* utils.TrustedBuilderID , bool , error ) {
98
+ func verifyTrustedBuilderID (certBuilderID , certTag string , expectedBuilderID * string , defaultTrustedBuilders map [string ]bool ) (* utils.TrustedBuilderID , bool , error ) {
101
99
var trustedBuilderID * utils.TrustedBuilderID
102
100
var err error
103
- certBuilderName := httpsGithubCom + certPath
104
101
// WARNING: we don't validate the tag here, because we need to allow
105
102
// refs/heads/main for e2e tests. See verifyTrustedBuilderRef().
106
103
// No builder ID provided by user: use the default trusted workflows.
107
104
if expectedBuilderID == nil || * expectedBuilderID == "" {
108
- if _ , ok := defaultTrustedBuilders [certPath ]; ! ok {
109
- return nil , false , fmt .Errorf ("%w: %s with builderID provided: %t" , serrors .ErrorUntrustedReusableWorkflow , certPath , expectedBuilderID != nil )
105
+ if _ , ok := defaultTrustedBuilders [certBuilderID ]; ! ok {
106
+ return nil , false , fmt .Errorf ("%w: %s with builderID provided: %t" , serrors .ErrorUntrustedReusableWorkflow , certBuilderID , expectedBuilderID != nil )
110
107
}
111
108
// Construct the builderID using the certificate's builder's name and tag.
112
- trustedBuilderID , err = utils .TrustedBuilderIDNew (certBuilderName + "@" + certTag , true )
109
+ trustedBuilderID , err = utils .TrustedBuilderIDNew (certBuilderID + "@" + certTag , true )
113
110
if err != nil {
114
111
return nil , false , err
115
112
}
116
113
} else {
117
114
// Verify the builderID.
118
115
// We only accept IDs on github.com.
119
- trustedBuilderID , err = utils .TrustedBuilderIDNew (certBuilderName + "@" + certTag , true )
116
+ trustedBuilderID , err = utils .TrustedBuilderIDNew (certBuilderID + "@" + certTag , true )
120
117
if err != nil {
121
118
return nil , false , err
122
119
}
@@ -144,7 +141,7 @@ func verifyTrustedBuilderID(certPath, certTag string, expectedBuilderID *string,
144
141
func isTrustedDelegatorBuilder (certBuilder * utils.TrustedBuilderID , trustedBuilders map [string ]bool ) bool {
145
142
for byobBuilder := range defaultBYOBReusableWorkflows {
146
143
// Check that the certificate builder is a BYOB workflow.
147
- if err := certBuilder .MatchesLoose (httpsGithubCom + byobBuilder , true ); err == nil {
144
+ if err := certBuilder .MatchesLoose (byobBuilder , true ); err == nil {
148
145
// We found a delegator workflow that matches the certificate identity.
149
146
// Check that the BYOB builder is trusted by the caller.
150
147
if _ , ok := trustedBuilders [byobBuilder ]; ! ok {
@@ -204,6 +201,7 @@ const (
204
201
HostedGitHub
205
202
)
206
203
204
+ // WorkflowIdentity is a identity captured from a Fulcio certificate.
207
205
// See https://github.com/sigstore/fulcio/blob/main/docs/oid-info.md.
208
206
type WorkflowIdentity struct {
209
207
// The source repository
@@ -218,7 +216,7 @@ type WorkflowIdentity struct {
218
216
SourceOwnerID * string
219
217
220
218
// Workflow path OIDC subject - ref of reuseable workflow or trigger workflow.
221
- SubjectWorkflowRef string
219
+ SubjectWorkflow * url. URL
222
220
// Subject commit sha1.
223
221
SubjectSha1 * string
224
222
// Hosted status of the subject.
@@ -235,6 +233,33 @@ type WorkflowIdentity struct {
235
233
Issuer string
236
234
}
237
235
236
+ // SubjectWorkflowName returns the subject workflow without the git ref.
237
+ func (id * WorkflowIdentity ) SubjectWorkflowName () string {
238
+ // NOTE: You should be able to copy a net.URL struct safely.
239
+ // See: https://github.com/golang/go/issues/38351
240
+ withoutRef := * id .SubjectWorkflow
241
+ withoutRef .Path = id .SubjectWorkflowPath ()
242
+ return withoutRef .String ()
243
+ }
244
+
245
+ // SubjectWorkflowPath returns the subject workflow without the server url.
246
+ func (id * WorkflowIdentity ) SubjectWorkflowPath () string {
247
+ i := strings .LastIndex (id .SubjectWorkflow .Path , "@" )
248
+ if i == - 1 {
249
+ return id .SubjectWorkflow .Path
250
+ }
251
+ return id .SubjectWorkflow .Path [:i ]
252
+ }
253
+
254
+ // SubjectWorkflowRef returns the ref for the subject workflow.
255
+ func (id * WorkflowIdentity ) SubjectWorkflowRef () string {
256
+ i := strings .LastIndex (id .SubjectWorkflow .Path , "@" )
257
+ if i == - 1 {
258
+ return ""
259
+ }
260
+ return id .SubjectWorkflow .Path [i + 1 :]
261
+ }
262
+
238
263
func getHosted (cert * x509.Certificate ) (* Hosted , error ) {
239
264
runnerEnv , err := getExtension (cert , fulcio .OIDRunnerEnvironment , true )
240
265
if err != nil {
@@ -423,9 +448,7 @@ func GetWorkflowInfoFromCertificate(cert *x509.Certificate) (*WorkflowIdentity,
423
448
if ! strings .HasPrefix (cert .URIs [0 ].Path , "/" ) {
424
449
return nil , fmt .Errorf ("%w: %s" , serrors .ErrorInvalidFormat , cert .URIs [0 ].Path )
425
450
}
426
- // Remove the starting '/'.
427
- // NOTE: The Path has the following structure: repo/name/path/to/workflow.yml@ref.
428
- subjectWorkflowRef := cert .URIs [0 ].Path [1 :]
451
+ subjectWorkflow := cert .URIs [0 ]
429
452
430
453
var pSubjectSha1 , pSourceID , pSourceRef , pSourceOwnerID , pBuildConfigPath , pRunID * string
431
454
if subjectSha1 != "" {
@@ -451,9 +474,9 @@ func GetWorkflowInfoFromCertificate(cert *x509.Certificate) (*WorkflowIdentity,
451
474
// Issuer.
452
475
Issuer : issuer ,
453
476
// Subject
454
- SubjectWorkflowRef : subjectWorkflowRef ,
455
- SubjectSha1 : pSubjectSha1 ,
456
- SubjectHosted : subjectHosted ,
477
+ SubjectWorkflow : subjectWorkflow ,
478
+ SubjectSha1 : pSubjectSha1 ,
479
+ SubjectHosted : subjectHosted ,
457
480
// Source.
458
481
SourceRepository : sourceRepository ,
459
482
SourceSha1 : sourceSha1 ,
0 commit comments