From d58ce0c6000ca6c799d872c163fd81c5a54f231a Mon Sep 17 00:00:00 2001 From: mrjoelkamp Date: Mon, 7 Oct 2024 13:34:04 -0500 Subject: [PATCH] feat: add reference wrapper for envelope --- attestation/attestation.go | 15 ++++++++++++--- attestation/layout.go | 21 +++++++++++++++------ attestation/mock.go | 4 ++-- attestation/referrers.go | 4 ++-- attestation/registry.go | 2 +- attestation/resolver.go | 2 +- attestation/types.go | 11 +++++++++++ policy/policy_test.go | 8 ++++---- policy/rego_test.go | 2 +- verify_test.go | 4 ++-- 10 files changed, 51 insertions(+), 22 deletions(-) diff --git a/attestation/attestation.go b/attestation/attestation.go index 952ebd2..7f040cf 100644 --- a/attestation/attestation.go +++ b/attestation/attestation.go @@ -316,8 +316,8 @@ func buildImageFromLayers(layers []*Layer, manifest *v1.Descriptor, subject *v1. return newImg, nil } -func ExtractEnvelopes(manifest *Manifest, predicateType string) ([]*Envelope, error) { - var envs []*Envelope +func ExtractEnvelopes(manifest *Manifest, predicateType string) ([]*EnvelopeReference, error) { + var envs []*EnvelopeReference dsseMediaType, err := DSSEMediaType(predicateType) if err != nil { return nil, fmt.Errorf("failed to get DSSE media type for predicate '%s': %w", predicateType, err) @@ -333,11 +333,20 @@ func ExtractEnvelopes(manifest *Manifest, predicateType string) ([]*Envelope, er return nil, fmt.Errorf("failed to get layer contents: %w", err) } defer reader.Close() - env := new(Envelope) + env := new(EnvelopeReference) err = json.NewDecoder(reader).Decode(&env) if err != nil { return nil, fmt.Errorf("failed to decode envelope: %w", err) } + var uri string + if len(manifest.OriginalDescriptor.URLs) > 0 { + uri = manifest.OriginalDescriptor.URLs[0] + } + env.ResourceDescriptor = &ResourceDescriptor{ + MediaType: string(mt), + Digest: map[string]string{manifest.OriginalDescriptor.Digest.Algorithm: manifest.OriginalDescriptor.Digest.Hex}, + URI: uri, + } envs = append(envs, env) } } diff --git a/attestation/layout.go b/attestation/layout.go index 24aaa7b..94a72f3 100644 --- a/attestation/layout.go +++ b/attestation/layout.go @@ -45,8 +45,8 @@ func (r *LayoutResolver) fetchManifest() (*Manifest, error) { return r.Manifest, nil } -func (r *LayoutResolver) Attestations(_ context.Context, predicateType string) ([]*Envelope, error) { - var envs []*Envelope +func (r *LayoutResolver) Attestations(_ context.Context, predicateType string) ([]*EnvelopeReference, error) { + var envs []*EnvelopeReference dsseMediaType, err := DSSEMediaType(predicateType) if err != nil { return nil, fmt.Errorf("failed to get DSSE media type for predicate '%s': %w", predicateType, err) @@ -60,17 +60,26 @@ func (r *LayoutResolver) Attestations(_ context.Context, predicateType string) ( if mts != dsseMediaType { continue } - env := new(Envelope) + env := new(EnvelopeReference) // parse layer blob as json - r, err := attestationLayer.Layer.Uncompressed() + layer, err := attestationLayer.Layer.Uncompressed() if err != nil { return nil, fmt.Errorf("failed to get layer contents: %w", err) } - defer r.Close() - err = json.NewDecoder(r).Decode(env) + defer layer.Close() + err = json.NewDecoder(layer).Decode(env) if err != nil { return nil, fmt.Errorf("failed to decode envelope: %w", err) } + var uri string + if len(r.Manifest.OriginalDescriptor.URLs) > 0 { + uri = r.Manifest.OriginalDescriptor.URLs[0] + } + env.ResourceDescriptor = &ResourceDescriptor{ + MediaType: string(mt), + Digest: map[string]string{r.Manifest.OriginalDescriptor.Digest.Algorithm: r.Manifest.OriginalDescriptor.Digest.Hex}, + URI: uri, + } envs = append(envs, env) } return envs, nil diff --git a/attestation/mock.go b/attestation/mock.go index 977e087..98dacc9 100644 --- a/attestation/mock.go +++ b/attestation/mock.go @@ -12,14 +12,14 @@ import ( var _ oci.ImageDetailsResolver = MockResolver{} type MockResolver struct { - Envs []*Envelope + Envs []*EnvelopeReference Image string PlatformFn func() (*v1.Platform, error) DescriptorFn func() (*v1.Descriptor, error) ImangeNameFn func() (string, error) } -func (r MockResolver) Attestations(_ context.Context, _ string) ([]*Envelope, error) { +func (r MockResolver) Attestations(_ context.Context, _ string) ([]*EnvelopeReference, error) { return r.Envs, nil } diff --git a/attestation/referrers.go b/attestation/referrers.go index a804dfe..4c109ed 100644 --- a/attestation/referrers.go +++ b/attestation/referrers.go @@ -109,12 +109,12 @@ func (r *ReferrersResolver) resolveAttestations(ctx context.Context, predicateTy return aManifests, nil } -func (r *ReferrersResolver) Attestations(ctx context.Context, predicateType string) ([]*Envelope, error) { +func (r *ReferrersResolver) Attestations(ctx context.Context, predicateType string) ([]*EnvelopeReference, error) { manifests, err := r.resolveAttestations(ctx, predicateType) if err != nil { return nil, fmt.Errorf("failed to resolve attestations: %w", err) } - var envs []*Envelope + var envs []*EnvelopeReference for _, attest := range manifests { es, err := ExtractEnvelopes(attest, predicateType) if err != nil { diff --git a/attestation/registry.go b/attestation/registry.go index c65b94e..73639d3 100644 --- a/attestation/registry.go +++ b/attestation/registry.go @@ -24,7 +24,7 @@ func NewRegistryResolver(src *oci.RegistryImageDetailsResolver) (*RegistryResolv }, nil } -func (r *RegistryResolver) Attestations(ctx context.Context, predicateType string) ([]*Envelope, error) { +func (r *RegistryResolver) Attestations(ctx context.Context, predicateType string) ([]*EnvelopeReference, error) { if r.Manifest == nil { attest, err := FetchManifest(ctx, r.Identifier, r.ImageSpec.Platform) if err != nil { diff --git a/attestation/resolver.go b/attestation/resolver.go index 0cc73b0..7eae516 100644 --- a/attestation/resolver.go +++ b/attestation/resolver.go @@ -8,5 +8,5 @@ import ( type Resolver interface { oci.ImageDetailsResolver - Attestations(ctx context.Context, mediaType string) ([]*Envelope, error) + Attestations(ctx context.Context, mediaType string) ([]*EnvelopeReference, error) } diff --git a/attestation/types.go b/attestation/types.go index a912ca5..2da43db 100644 --- a/attestation/types.go +++ b/attestation/types.go @@ -67,6 +67,17 @@ type Extension struct { Ext *DockerDSSEExtension `json:"ext"` } +type EnvelopeReference struct { + *Envelope + ResourceDescriptor *ResourceDescriptor +} + +type ResourceDescriptor struct { + MediaType string `json:"mediaType"` + Digest map[string]string `json:"digest"` + URI string `json:"uri"` +} + type AnnotatedStatement struct { OCIDescriptor *v1.Descriptor InTotoStatement *intoto.Statement diff --git a/policy/policy_test.go b/policy/policy_test.go index 455e447..c2d43c6 100644 --- a/policy/policy_test.go +++ b/policy/policy_test.go @@ -20,13 +20,13 @@ import ( "github.com/stretchr/testify/require" ) -func loadAttestation(t *testing.T, path string) *attestation.Envelope { +func loadAttestation(t *testing.T, path string) *attestation.EnvelopeReference { ex, err := os.ReadFile(path) if err != nil { t.Fatal(err) } - env := new(attestation.Envelope) + env := new(attestation.EnvelopeReference) err = json.Unmarshal(ex, env) if err != nil { t.Fatal(err) @@ -44,7 +44,7 @@ func TestRegoEvaluator_Evaluate(t *testing.T) { require.NoError(t, err) re := policy.NewRegoEvaluator(true, verifier) defaultResolver := attestation.MockResolver{ - Envs: []*attestation.Envelope{loadAttestation(t, ExampleAttestation)}, + Envs: []*attestation.EnvelopeReference{loadAttestation(t, ExampleAttestation)}, } defaultPlatform, err := v1.ParsePlatform("linux/amd64") require.NoError(t, err) @@ -122,7 +122,7 @@ func TestLoadingMappings(t *testing.T) { func TestCreateAttestationResolver(t *testing.T) { mockResolver := attestation.MockResolver{ - Envs: []*attestation.Envelope{}, + Envs: []*attestation.EnvelopeReference{}, } layoutResolver := &attestation.LayoutResolver{} registryResolver := &oci.RegistryImageDetailsResolver{} diff --git a/policy/rego_test.go b/policy/rego_test.go index 47cf304..5f44083 100644 --- a/policy/rego_test.go +++ b/policy/rego_test.go @@ -83,7 +83,7 @@ func (r *NullAttestationResolver) ImageDescriptor(_ context.Context) (*v1.Descri return nil, nil } -func (r *NullAttestationResolver) Attestations(_ context.Context, _ string) ([]*attestation.Envelope, error) { +func (r *NullAttestationResolver) Attestations(_ context.Context, _ string) ([]*attestation.EnvelopeReference, error) { r.called = true return nil, nil } diff --git a/verify_test.go b/verify_test.go index af241e9..d7e0ff6 100644 --- a/verify_test.go +++ b/verify_test.go @@ -37,11 +37,11 @@ func TestVerifyAttestations(t *testing.T) { ex, err := os.ReadFile(ExampleAttestation) assert.NoError(t, err) - env := new(attestation.Envelope) + env := new(attestation.EnvelopeReference) err = json.Unmarshal(ex, env) assert.NoError(t, err) resolver := &attestation.MockResolver{ - Envs: []*attestation.Envelope{env}, + Envs: []*attestation.EnvelopeReference{env}, } testCases := []struct {