From b16511d6e4c27b33ab772b208e2e9c7f5b505127 Mon Sep 17 00:00:00 2001 From: mrjoelkamp Date: Mon, 29 Apr 2024 12:52:39 -0500 Subject: [PATCH] feat: add attest sign/verify --- pkg/attest/sign.go | 2 ++ pkg/attest/verify.go | 40 ++++++++++++++++++++++++++ pkg/attest/verify_test.go | 60 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 pkg/attest/sign.go create mode 100644 pkg/attest/verify.go create mode 100644 pkg/attest/verify_test.go diff --git a/pkg/attest/sign.go b/pkg/attest/sign.go new file mode 100644 index 0000000..1c255bd --- /dev/null +++ b/pkg/attest/sign.go @@ -0,0 +1,2 @@ +package attest + diff --git a/pkg/attest/verify.go b/pkg/attest/verify.go new file mode 100644 index 0000000..b23c1ca --- /dev/null +++ b/pkg/attest/verify.go @@ -0,0 +1,40 @@ +package attest + +import ( + "context" + "fmt" + + "github.com/docker/attest/pkg/oci" + "github.com/docker/attest/pkg/policy" +) + +func VerifyAttestations(ctx context.Context, resolver oci.AttestationResolver, files []*policy.PolicyFile) error { + digest, err := resolver.ImageDigest(ctx) + if err != nil { + return fmt.Errorf("failed to get image digest: %w", err) + } + name, err := resolver.ImageName(ctx) + if err != nil { + return fmt.Errorf("failed to get image name: %w", err) + } + purl, canonical, err := oci.RefToPURL(name, resolver.ImagePlatformStr()) + if err != nil { + return fmt.Errorf("failed to convert ref to purl: %w", err) + } + input := &policy.PolicyInput{ + Digest: digest, + Purl: purl, + IsCanonical: canonical, + } + + evaluator, err := policy.GetPolicyEvaluator(ctx) + if err != nil { + return err + } + err = evaluator.Evaluate(ctx, resolver, files, input) + if err != nil { + return fmt.Errorf("policy evaluation failed: %w", err) + } + + return nil +} diff --git a/pkg/attest/verify_test.go b/pkg/attest/verify_test.go new file mode 100644 index 0000000..42ea7b7 --- /dev/null +++ b/pkg/attest/verify_test.go @@ -0,0 +1,60 @@ +package attest + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/docker/attest/pkg/attestation" + "github.com/docker/attest/pkg/oci" + "github.com/docker/attest/pkg/policy" + "github.com/stretchr/testify/assert" +) + +var ( + ExampleAttestation = filepath.Join("..", "..", "test", "testdata", "example_attestation.json") +) + +func TestVerifyAttestations(t *testing.T) { + ex, err := os.ReadFile(ExampleAttestation) + assert.NoError(t, err) + + var env = new(attestation.Envelope) + err = json.Unmarshal(ex, env) + assert.NoError(t, err) + resolver := &oci.MockResolver{ + Envs: []*attestation.Envelope{env}, + } + + testCases := []struct { + name string + policyEvaluationError error + expectedError error + }{ + {"policy ok", nil, nil}, + {"policy error", fmt.Errorf("policy error"), fmt.Errorf("policy evaluation failed: policy error")}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + mockPE := policy.MockPolicyEvaluator{ + EvaluateFunc: func(ctx context.Context, resolver oci.AttestationResolver, policy []*policy.PolicyFile, input *policy.PolicyInput) error { + return tc.policyEvaluationError + }, + } + + ctx := policy.WithPolicyEvaluator(context.Background(), &mockPE) + err = VerifyAttestations(ctx, resolver, nil) + if tc.expectedError != nil { + assert.Error(t, err) + assert.Equal(t, tc.expectedError.Error(), err.Error()) + } else { + assert.NoError(t, err) + } + }) + } +}