Skip to content

Commit f51b71f

Browse files
committed
updated per code review
Signed-off-by: Patrick Zheng <[email protected]>
1 parent fdd182e commit f51b71f

File tree

5 files changed

+93
-72
lines changed

5 files changed

+93
-72
lines changed

internal/slice/slice.go

-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
package slice
22

3-
const (
4-
Wildcard = "*"
5-
X509Subject = "x509.subject"
6-
)
7-
83
// ContainsString is a utility function to check if a string exists in an array
94
func ContainsString(val string, values []string) bool {
105
for _, v := range values {

internal/trustpolicy/trustpolicy.go

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package trustpolicy
2+
3+
const (
4+
Wildcard = "*"
5+
X509Subject = "x509.subject"
6+
)

verification/trustpolicy/trustpolicy.go

+55-54
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/notaryproject/notation-go/internal/pkix"
1010
"github.com/notaryproject/notation-go/internal/slice"
11+
"github.com/notaryproject/notation-go/internal/trustpolicy"
1112
"github.com/notaryproject/notation-go/verification/truststore"
1213
)
1314

@@ -168,7 +169,7 @@ func (policyDoc *Document) Validate() error {
168169
policyStatementNameCount[statement.Name]++
169170

170171
// Verify signature verification level is valid
171-
verificationLevel, err := GetVerificationLevel(statement.SignatureVerification)
172+
verificationLevel, err := statement.SignatureVerification.GetVerificationLevel()
172173
if err != nil {
173174
return fmt.Errorf("trust policy statement %q uses invalid signatureVerification value %q", statement.Name, statement.SignatureVerification.VerificationLevel)
174175
}
@@ -213,9 +214,43 @@ func (policyDoc *Document) Validate() error {
213214
return nil
214215
}
215216

217+
// GetApplicableTrustPolicy returns a pointer to the deep copied TrustPolicy
218+
// statement that applies to the given registry URI. If no applicable trust
219+
// policy is found, returns an error
220+
// see https://github.com/notaryproject/notaryproject/blob/main/trust-store-trust-policy-specification.md#selecting-a-trust-policy-based-on-artifact-uri
221+
func (trustPolicyDoc *Document) GetApplicableTrustPolicy(artifactReference string) (*TrustPolicy, error) {
222+
223+
artifactPath, err := getArtifactPathFromReference(artifactReference)
224+
if err != nil {
225+
return nil, err
226+
}
227+
228+
var wildcardPolicy *TrustPolicy
229+
var applicablePolicy *TrustPolicy
230+
for _, policyStatement := range trustPolicyDoc.TrustPolicies {
231+
if slice.ContainsString(trustpolicy.Wildcard, policyStatement.RegistryScopes) {
232+
// we need to deep copy because we can't use the loop variable
233+
// address. see https://stackoverflow.com/a/45967429
234+
wildcardPolicy = (&policyStatement).clone()
235+
} else if slice.ContainsString(artifactPath, policyStatement.RegistryScopes) {
236+
applicablePolicy = (&policyStatement).clone()
237+
}
238+
}
239+
240+
if applicablePolicy != nil {
241+
// a policy with exact match for registry URI takes precedence over
242+
// a wildcard (*) policy.
243+
return applicablePolicy, nil
244+
} else if wildcardPolicy != nil {
245+
return wildcardPolicy, nil
246+
} else {
247+
return nil, fmt.Errorf("artifact %q has no applicable trust policy", artifactReference)
248+
}
249+
}
250+
216251
// GetVerificationLevel returns VerificationLevel struct for the given
217252
// SignatureVerification struct throws error if SignatureVerification is invalid
218-
func GetVerificationLevel(signatureVerification SignatureVerification) (*VerificationLevel, error) {
253+
func (signatureVerification *SignatureVerification) GetVerificationLevel() (*VerificationLevel, error) {
219254
var baseLevel *VerificationLevel
220255
for _, l := range VerificationLevels {
221256
if l.Name == signatureVerification.VerificationLevel {
@@ -281,6 +316,17 @@ func GetVerificationLevel(signatureVerification SignatureVerification) (*Verific
281316
return customVerificationLevel, nil
282317
}
283318

319+
// clone returns a pointer to the deeply copied TrustPolicy
320+
func (t *TrustPolicy) clone() *TrustPolicy {
321+
return &TrustPolicy{
322+
Name: t.Name,
323+
SignatureVerification: t.SignatureVerification,
324+
RegistryScopes: append([]string(nil), t.RegistryScopes...),
325+
TrustedIdentities: append([]string(nil), t.TrustedIdentities...),
326+
TrustStores: append([]string(nil), t.TrustStores...),
327+
}
328+
}
329+
284330
// validateTrustStore validates if the policy statement is following the
285331
// Notary V2 spec rules for truststores
286332
func validateTrustStore(statement TrustPolicy) error {
@@ -300,7 +346,7 @@ func validateTrustedIdentities(statement TrustPolicy) error {
300346

301347
// If there is a wildcard in trusted identies, there shouldn't be any other
302348
//identities
303-
if len(statement.TrustedIdentities) > 1 && slice.ContainsString(slice.Wildcard, statement.TrustedIdentities) {
349+
if len(statement.TrustedIdentities) > 1 && slice.ContainsString(trustpolicy.Wildcard, statement.TrustedIdentities) {
304350
return fmt.Errorf("trust policy statement %q uses a wildcard trusted identity '*', a wildcard identity cannot be used in conjunction with other values", statement.Name)
305351
}
306352

@@ -311,7 +357,7 @@ func validateTrustedIdentities(statement TrustPolicy) error {
311357
return fmt.Errorf("trust policy statement %q has an empty trusted identity", statement.Name)
312358
}
313359

314-
if identity != slice.Wildcard {
360+
if identity != trustpolicy.Wildcard {
315361
i := strings.Index(identity, ":")
316362
if i < 0 {
317363
return fmt.Errorf("trust policy statement %q has trusted identity %q without an identity prefix", statement.Name, identity)
@@ -321,7 +367,7 @@ func validateTrustedIdentities(statement TrustPolicy) error {
321367
identityValue := identity[i+1:]
322368

323369
// notation natively supports x509.subject identities only
324-
if identityPrefix == slice.X509Subject {
370+
if identityPrefix == trustpolicy.X509Subject {
325371
dn, err := pkix.ParseDistinguishedName(identityValue)
326372
if err != nil {
327373
return err
@@ -332,7 +378,7 @@ func validateTrustedIdentities(statement TrustPolicy) error {
332378
}
333379

334380
// Verify there are no overlapping DNs
335-
if err := hasOverlappingDNs(statement.Name, parsedDNs); err != nil {
381+
if err := validateOverlappingDNs(statement.Name, parsedDNs); err != nil {
336382
return err
337383
}
338384

@@ -350,11 +396,11 @@ func validateRegistryScopes(policyDoc *Document) error {
350396
if len(statement.RegistryScopes) == 0 {
351397
return fmt.Errorf("trust policy statement %q has zero registry scopes, it must specify registry scopes with at least one value", statement.Name)
352398
}
353-
if len(statement.RegistryScopes) > 1 && slice.ContainsString(slice.Wildcard, statement.RegistryScopes) {
399+
if len(statement.RegistryScopes) > 1 && slice.ContainsString(trustpolicy.Wildcard, statement.RegistryScopes) {
354400
return fmt.Errorf("trust policy statement %q uses wildcard registry scope '*', a wildcard scope cannot be used in conjunction with other scope values", statement.Name)
355401
}
356402
for _, scope := range statement.RegistryScopes {
357-
if scope != slice.Wildcard {
403+
if scope != trustpolicy.Wildcard {
358404
if err := validateRegistryScopeFormat(scope); err != nil {
359405
return err
360406
}
@@ -374,7 +420,7 @@ func validateRegistryScopes(policyDoc *Document) error {
374420
return nil
375421
}
376422

377-
func hasOverlappingDNs(policyName string, parsedDNs []parsedDN) error {
423+
func validateOverlappingDNs(policyName string, parsedDNs []parsedDN) error {
378424
for i, dn1 := range parsedDNs {
379425
for j, dn2 := range parsedDNs {
380426
if i != j && pkix.IsSubsetDN(dn1.ParsedMap, dn2.ParsedMap) {
@@ -397,51 +443,6 @@ func isValidTrustStoreType(s string) bool {
397443
return false
398444
}
399445

400-
// GetApplicableTrustPolicy returns a pointer to the deep copied TrustPolicy
401-
// statement that applies to the given registry URI. If no applicable trust
402-
// policy is found, returns an error
403-
// see https://github.com/notaryproject/notaryproject/blob/main/trust-store-trust-policy-specification.md#selecting-a-trust-policy-based-on-artifact-uri
404-
func GetApplicableTrustPolicy(trustPolicyDoc *Document, artifactReference string) (*TrustPolicy, error) {
405-
406-
artifactPath, err := getArtifactPathFromReference(artifactReference)
407-
if err != nil {
408-
return nil, err
409-
}
410-
411-
var wildcardPolicy *TrustPolicy
412-
var applicablePolicy *TrustPolicy
413-
for _, policyStatement := range trustPolicyDoc.TrustPolicies {
414-
if slice.ContainsString(slice.Wildcard, policyStatement.RegistryScopes) {
415-
// we need to deep copy because we can't use the loop variable
416-
// address. see https://stackoverflow.com/a/45967429
417-
wildcardPolicy = (&policyStatement).clone()
418-
} else if slice.ContainsString(artifactPath, policyStatement.RegistryScopes) {
419-
applicablePolicy = (&policyStatement).clone()
420-
}
421-
}
422-
423-
if applicablePolicy != nil {
424-
// a policy with exact match for registry URI takes precedence over
425-
// a wildcard (*) policy.
426-
return applicablePolicy, nil
427-
} else if wildcardPolicy != nil {
428-
return wildcardPolicy, nil
429-
} else {
430-
return nil, fmt.Errorf("artifact %q has no applicable trust policy", artifactReference)
431-
}
432-
}
433-
434-
// clone returns a pointer to the deeply copied TrustPolicy
435-
func (t *TrustPolicy) clone() *TrustPolicy {
436-
return &TrustPolicy{
437-
Name: t.Name,
438-
SignatureVerification: t.SignatureVerification,
439-
RegistryScopes: append([]string(nil), t.RegistryScopes...),
440-
TrustedIdentities: append([]string(nil), t.TrustedIdentities...),
441-
TrustStores: append([]string(nil), t.TrustStores...),
442-
}
443-
}
444-
445446
func getArtifactPathFromReference(artifactReference string) (string, error) {
446447
// TODO support more types of URI like "domain.com/repository",
447448
// "domain.com/repository:tag"

verification/trustpolicy/trustpolicy_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ func TestGetVerificationLevel(t *testing.T) {
381381
for i, tt := range tests {
382382
t.Run(strconv.Itoa(i), func(t *testing.T) {
383383

384-
level, err := GetVerificationLevel(tt.verificationLevel)
384+
level, err := tt.verificationLevel.GetVerificationLevel()
385385

386386
if tt.wantErr != (err != nil) {
387387
t.Fatalf("TestFindVerificationLevel Error: %q WantErr: %v", err, tt.wantErr)
@@ -428,7 +428,7 @@ func TestCustomVerificationLevel(t *testing.T) {
428428
}
429429
for i, tt := range tests {
430430
t.Run(strconv.Itoa(i), func(t *testing.T) {
431-
level, err := GetVerificationLevel(tt.customVerification)
431+
level, err := tt.customVerification.GetVerificationLevel()
432432

433433
if tt.wantErr != (err != nil) {
434434
t.Fatalf("TestCustomVerificationLevel Error: %q WantErr: %v", err, tt.wantErr)
@@ -461,13 +461,13 @@ func TestApplicableTrustPolicy(t *testing.T) {
461461
policyStatement,
462462
}
463463
// existing Registry Scope
464-
policy, err := GetApplicableTrustPolicy(&policyDoc, registryUri)
464+
policy, err := (&policyDoc).GetApplicableTrustPolicy(registryUri)
465465
if policy.Name != policyStatement.Name || err != nil {
466466
t.Fatalf("getApplicableTrustPolicy should return %q for registry scope %q", policyStatement.Name, registryScope)
467467
}
468468

469469
// non-existing Registry Scope
470-
policy, err = GetApplicableTrustPolicy(&policyDoc, "non.existing.scope/repo@sha256:hash")
470+
policy, err = (&policyDoc).GetApplicableTrustPolicy("non.existing.scope/repo@sha256:hash")
471471
if policy != nil || err == nil || err.Error() != "artifact \"non.existing.scope/repo@sha256:hash\" has no applicable trust policy" {
472472
t.Fatalf("getApplicableTrustPolicy should return nil for non existing registry scope")
473473
}
@@ -484,7 +484,7 @@ func TestApplicableTrustPolicy(t *testing.T) {
484484
policyStatement,
485485
wildcardStatement,
486486
}
487-
policy, err = GetApplicableTrustPolicy(&policyDoc, "some.registry.that/has.no.policy@sha256:hash")
487+
policy, err = (&policyDoc).GetApplicableTrustPolicy("some.registry.that/has.no.policy@sha256:hash")
488488
if policy.Name != wildcardStatement.Name || err != nil {
489489
t.Fatalf("getApplicableTrustPolicy should return wildcard policy for registry scope \"some.registry.that/has.no.policy\"")
490490
}

verification/truststore/truststore.go

+27-8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"io/fs"
1010
"os"
1111
"path/filepath"
12+
"regexp"
1213

1314
corex509 "github.com/notaryproject/notation-core-go/x509"
1415
"github.com/notaryproject/notation-go/dir"
@@ -36,20 +37,23 @@ type X509TrustStore interface {
3637
GetCertificates(ctx context.Context, storeType Type, namedStore string) ([]*x509.Certificate, error)
3738
}
3839

40+
// NewX509TrustStore generates a new X509TrustStore
41+
func NewX509TrustStore(trustStorefs dir.SysFS) X509TrustStore {
42+
return &x509TrustStore{trustStorefs}
43+
}
44+
3945
// x509TrustStore implements X509TrustStore
4046
type x509TrustStore struct {
4147
trustStorefs dir.SysFS
4248
}
4349

44-
// NewX509TrustStore generates a new X509TrustStore
45-
func NewX509TrustStore(trustStorefs dir.SysFS) X509TrustStore {
46-
return x509TrustStore{trustStorefs}
47-
}
48-
4950
// GetCertificates returns certificates under storeType/namedStore
50-
func (trustStore x509TrustStore) GetCertificates(ctx context.Context, storeType Type, namedStore string) ([]*x509.Certificate, error) {
51-
if storeType == "" || namedStore == "" {
52-
return nil, errors.New("storeType and namedStore cannot be empty")
51+
func (trustStore *x509TrustStore) GetCertificates(ctx context.Context, storeType Type, namedStore string) ([]*x509.Certificate, error) {
52+
if !isValidStoreType(storeType) {
53+
return nil, fmt.Errorf("unsupported store type: %s", storeType)
54+
}
55+
if !isValidNamedStore(namedStore) {
56+
return nil, errors.New("named store name needs to follow [a-zA-Z0-9_.-]+ format")
5357
}
5458
path, err := trustStore.trustStorefs.SysPath(dir.X509TrustStoreDir(string(storeType), namedStore))
5559
if err != nil {
@@ -123,3 +127,18 @@ func validateCerts(certs []*x509.Certificate, path string) error {
123127

124128
return nil
125129
}
130+
131+
// isValidStoreType checks if storeType is supported
132+
func isValidStoreType(storeType Type) bool {
133+
for _, t := range Types {
134+
if storeType == t {
135+
return true
136+
}
137+
}
138+
return false
139+
}
140+
141+
// isValidFileName checks if a file name is cross-platform compatible
142+
func isValidNamedStore(namedStore string) bool {
143+
return regexp.MustCompile(`^[a-zA-Z0-9_.-]+$`).MatchString(namedStore)
144+
}

0 commit comments

Comments
 (0)