From c7d17faf059deb01f5e804d96c3ac36e499b0b14 Mon Sep 17 00:00:00 2001 From: mrjoelkamp Date: Mon, 5 Aug 2024 13:24:58 -0500 Subject: [PATCH] fix: layout attestation resolver --- pkg/attest/verify_test.go | 65 ++++++++++++--------------------------- pkg/attestation/types.go | 2 +- pkg/mirror/mirror.go | 9 +++--- pkg/oci/layout.go | 3 ++ pkg/oci/referrers.go | 11 ++----- pkg/policy/policy.go | 42 +++++++------------------ 6 files changed, 41 insertions(+), 91 deletions(-) diff --git a/pkg/attest/verify_test.go b/pkg/attest/verify_test.go index 9c38687..34c4ecc 100644 --- a/pkg/attest/verify_test.go +++ b/pkg/attest/verify_test.go @@ -10,12 +10,10 @@ import ( "github.com/docker/attest/internal/test" "github.com/docker/attest/pkg/attestation" + "github.com/docker/attest/pkg/config" + "github.com/docker/attest/pkg/mirror" "github.com/docker/attest/pkg/oci" "github.com/docker/attest/pkg/policy" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/empty" - "github.com/google/go-containerregistry/pkg/v1/layout" - "github.com/google/go-containerregistry/pkg/v1/mutate" intoto "github.com/in-toto/in-toto-golang/in_toto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -84,25 +82,17 @@ func TestVSA(t *testing.T) { require.NoError(t, err) // output signed attestations - idx := v1.ImageIndex(empty.Index) - idx = mutate.AppendManifests(idx, mutate.IndexAddendum{ - Add: signedIndex, - Descriptor: v1.Descriptor{ - Annotations: map[string]string{ - oci.OCIReferenceTarget: attIdx.Name, - }, - }, - }) - _, err = layout.Write(outputLayout, idx) + spec, err := oci.ParseImageSpec(oci.LocalPrefix+outputLayout, oci.WithPlatform(LinuxAMD64)) + require.NoError(t, err) + err = mirror.SaveIndex([]*oci.ImageSpec{spec}, signedIndex, attIdx.Name) assert.NoError(t, err) // mocked vsa query should pass policyOpts := &policy.Options{ - LocalPolicyDir: PassPolicyDir, + LocalPolicyDir: PassPolicyDir, + AttestationStyle: config.AttestationStyleAttached, } - src, err := oci.ParseImageSpec(oci.LocalPrefix+outputLayout, oci.WithPlatform(LinuxAMD64)) - require.NoError(t, err) - results, err := Verify(ctx, src, policyOpts) + results, err := Verify(ctx, spec, policyOpts) require.NoError(t, err) assert.Equal(t, OutcomeSuccess, results.Outcome) assert.Empty(t, results.Violations) @@ -142,25 +132,17 @@ func TestVerificationFailure(t *testing.T) { require.NoError(t, err) // output signed attestations - idx := v1.ImageIndex(empty.Index) - idx = mutate.AppendManifests(idx, mutate.IndexAddendum{ - Add: signedIndex, - Descriptor: v1.Descriptor{ - Annotations: map[string]string{ - oci.OCIReferenceTarget: attIdx.Name, - }, - }, - }) - _, err = layout.Write(outputLayout, idx) + spec, err := oci.ParseImageSpec(oci.LocalPrefix+outputLayout, oci.WithPlatform(LinuxAMD64)) + require.NoError(t, err) + err = mirror.SaveIndex([]*oci.ImageSpec{spec}, signedIndex, attIdx.Name) assert.NoError(t, err) // mocked vsa query should fail policyOpts := &policy.Options{ - LocalPolicyDir: FailPolicyDir, + LocalPolicyDir: FailPolicyDir, + AttestationStyle: config.AttestationStyleAttached, } - src, err := oci.ParseImageSpec(oci.LocalPrefix+outputLayout, oci.WithPlatform(LinuxAMD64)) - require.NoError(t, err) - results, err := Verify(ctx, src, policyOpts) + results, err := Verify(ctx, spec, policyOpts) require.NoError(t, err) assert.Equal(t, OutcomeFailure, results.Outcome) assert.Len(t, results.Violations, 1) @@ -224,24 +206,15 @@ func TestSignVerify(t *testing.T) { imageName = attIdx.Name } // output signed attestations - idx := v1.ImageIndex(empty.Index) - idx = mutate.AppendManifests(idx, mutate.IndexAddendum{ - Add: signedIndex, - Descriptor: v1.Descriptor{ - Annotations: map[string]string{ - oci.OCIReferenceTarget: imageName, - }, - }, - }) - _, err = layout.Write(outputLayout, idx) - assert.NoError(t, err) + spec, err := oci.ParseImageSpec(oci.LocalPrefix+outputLayout, oci.WithPlatform(LinuxAMD64)) + require.NoError(t, err) + err = mirror.SaveIndex([]*oci.ImageSpec{spec}, signedIndex, imageName) + require.NoError(t, err) policyOpts := &policy.Options{ LocalPolicyDir: tc.policyDir, } - src, err := oci.ParseImageSpec(oci.LocalPrefix+outputLayout, oci.WithPlatform(LinuxAMD64)) - require.NoError(t, err) - results, err := Verify(ctx, src, policyOpts) + results, err := Verify(ctx, spec, policyOpts) if tc.expectError { require.Error(t, err) return diff --git a/pkg/attestation/types.go b/pkg/attestation/types.go index fd28c83..3305b24 100644 --- a/pkg/attestation/types.go +++ b/pkg/attestation/types.go @@ -36,7 +36,7 @@ type Manifest struct { // accumulated during signing SignedLayers []*Layer - // details of subect image + // details of subject image SubjectName string SubjectDescriptor *v1.Descriptor } diff --git a/pkg/mirror/mirror.go b/pkg/mirror/mirror.go index eb927ac..e9e35de 100644 --- a/pkg/mirror/mirror.go +++ b/pkg/mirror/mirror.go @@ -129,6 +129,7 @@ func SaveImage(output *oci.ImageSpec, image v1.Image, imageName string) error { func SaveReferrers(manifest *attestation.Manifest, outputs []*oci.ImageSpec) error { for _, output := range outputs { + // OCI layout output for referrers not supported if output.Type == oci.OCI { continue } @@ -147,10 +148,10 @@ func SaveReferrers(manifest *attestation.Manifest, outputs []*oci.ImageSpec) err return fmt.Errorf("failed to build image: %w", err) } for _, image := range images { - err = SaveImage(attOut, image, "") - } - if err != nil { - return fmt.Errorf("failed to push image: %w", err) + err := PushImageToRegistry(image, attOut.Identifier) + if err != nil { + return fmt.Errorf("failed to push image: %w", err) + } } } return nil diff --git a/pkg/oci/layout.go b/pkg/oci/layout.go index 132c061..634be9e 100644 --- a/pkg/oci/layout.go +++ b/pkg/oci/layout.go @@ -115,6 +115,9 @@ func attestationManifestFromOCILayout(path string, platform *v1.Platform) (*atte } } } + if subjectDescriptor == nil { + return nil, fmt.Errorf("platform not found in index") + } for i := range mfs2.Manifests { mf := &mfs2.Manifests[i] if mf.Annotations[att.DockerReferenceType] != attestation.AttestationManifestType { diff --git a/pkg/oci/referrers.go b/pkg/oci/referrers.go index 0be2183..b74b4a8 100644 --- a/pkg/oci/referrers.go +++ b/pkg/oci/referrers.go @@ -3,6 +3,7 @@ package oci import ( "context" "fmt" + "strings" "github.com/docker/attest/pkg/attestation" att "github.com/docker/attest/pkg/attestation" @@ -16,14 +17,6 @@ type ReferrersResolver struct { } func NewReferrersAttestationResolver(src ImageDetailsResolver, options ...func(*ReferrersResolver) error) (*ReferrersResolver, error) { - // currently only supports RegistryImageDetailsResolver - switch src.(type) { - case *RegistryImageDetailsResolver: - case *MockRegistryResolver: - default: - return nil, fmt.Errorf("unsupported referrers image details resolver type: %T", src) - } - res := &ReferrersResolver{ ImageDetailsResolver: src, } @@ -66,7 +59,7 @@ func (r *ReferrersResolver) resolveAttestations(ctx context.Context, predicateTy } var referrersSubjectRef name.Digest if r.referrersRepo != "" { - referrersSubjectRef, err = name.NewDigest(fmt.Sprintf("%s@%s", r.referrersRepo, subjectDigest)) + referrersSubjectRef, err = name.NewDigest(fmt.Sprintf("%s@%s", strings.TrimPrefix(r.referrersRepo, RegistryPrefix), subjectDigest)) if err != nil { return nil, fmt.Errorf("failed to create referrers reference: %w", err) } diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index ad6fb85..0582192 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -219,40 +219,20 @@ func CreateImageDetailsResolver(imageSource *oci.ImageSpec) (oci.ImageDetailsRes } func CreateAttestationResolver(resolver oci.ImageDetailsResolver, mapping *config.PolicyMapping) (oci.AttestationResolver, error) { - switch resolver := resolver.(type) { - case *oci.RegistryImageDetailsResolver: - if mapping.Attestations != nil && mapping.Attestations.Style == config.AttestationStyleAttached { - return oci.NewRegistryAttestationResolver(resolver) - } else { - if mapping.Attestations != nil && mapping.Attestations.Repo != "" { - return oci.NewReferrersAttestationResolver(resolver, oci.WithReferrersRepo(mapping.Attestations.Repo)) - } - return oci.NewReferrersAttestationResolver(resolver) - } - case *oci.LayoutResolver: - if mapping.Attestations != nil { - switch mapping.Attestations.Style { - case config.AttestationStyleAttached: + if mapping.Attestations != nil { + if mapping.Attestations.Style == config.AttestationStyleAttached { + switch resolver := resolver.(type) { + case *oci.RegistryImageDetailsResolver: + return oci.NewRegistryAttestationResolver(resolver) + case *oci.LayoutResolver: return resolver, nil - case config.AttestationStyleReferrers: - if mapping.Attestations.Repo != "" { - referrersSpec, err := oci.ParseImageSpec(mapping.Attestations.Repo) - if err != nil { - return nil, fmt.Errorf("failed to parse referrers image spec: %w", err) - } - referrersResolver, err := CreateImageDetailsResolver(referrersSpec) - if err != nil { - return nil, fmt.Errorf("failed to create referrers resolver: %w", err) - } - return oci.NewReferrersAttestationResolver(referrersResolver, oci.WithReferrersRepo(mapping.Attestations.Repo)) - } - return oci.NewReferrersAttestationResolver(resolver) default: - return nil, fmt.Errorf("unsupported attestation style: %s", mapping.Attestations.Style) + return nil, fmt.Errorf("unsupported image details resolver type: %T", resolver) } } - return resolver, nil - default: - return nil, fmt.Errorf("unsupported image details resolver type: %T", resolver) + if mapping.Attestations.Repo != "" { + return oci.NewReferrersAttestationResolver(resolver, oci.WithReferrersRepo(mapping.Attestations.Repo)) + } } + return oci.NewReferrersAttestationResolver(resolver) }