diff --git a/pkg/attest/example_verify_test.go b/pkg/attest/example_verify_test.go index a50fabd..1cf52c2 100644 --- a/pkg/attest/example_verify_test.go +++ b/pkg/attest/example_verify_test.go @@ -56,6 +56,7 @@ func ExampleVerify_remote() { TufClient: tufClient, LocalTargetsDir: filepath.Join(home, ".docker", "policy"), // location to store policy files downloaded from TUF LocalPolicyDir: "", // overrides TUF policy for local policy files if set + PolicyId: "", // set to ignore policy mapping and select a policy by id } // verify attestations diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index 4fd5f3f..3c52306 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -79,6 +79,7 @@ type PolicyOptions struct { TufClient tuf.TUFClient LocalTargetsDir string LocalPolicyDir string + PolicyId string } type Policy struct { @@ -195,7 +196,44 @@ func findPolicyMatch(named reference.Named, mappings *PolicyMappings) (*PolicyMa return nil, nil } +func resolvePolicyById(opts *PolicyOptions) (*Policy, error) { + if opts.PolicyId != "" { + localMappings, err := LoadLocalMappings(opts) + if err != nil { + return nil, fmt.Errorf("failed to load local policy mappings: %w", err) + } + if localMappings != nil { + for _, mapping := range localMappings.Policies { + if mapping.Id == opts.PolicyId { + return resolveLocalPolicy(opts, &mapping) + } + } + } + + // must check tuf + tufMappings, err := loadTufMappings(opts.TufClient, opts.LocalTargetsDir) + if err != nil { + return nil, fmt.Errorf("failed to load tuf policy mappings: %w", err) + } + for _, mapping := range tufMappings.Policies { + if mapping.Id == opts.PolicyId { + return resolveTufPolicy(opts, &mapping) + } + } + return nil, fmt.Errorf("policy with id %s not found", opts.PolicyId) + } + return nil, nil +} + func ResolvePolicy(ctx context.Context, resolver oci.AttestationResolver, opts *PolicyOptions) (*Policy, error) { + p, err := resolvePolicyById(opts) + if err != nil { + return nil, fmt.Errorf("failed to resolve policy by id: %w", err) + } + if p != nil { + return p, nil + } + imageName, err := resolver.ImageName(ctx) if err != nil { return nil, fmt.Errorf("failed to get image name: %w", err) diff --git a/pkg/policy/policy_test.go b/pkg/policy/policy_test.go index 552b990..b4211d1 100644 --- a/pkg/policy/policy_test.go +++ b/pkg/policy/policy_test.go @@ -31,7 +31,7 @@ func loadAttestation(t *testing.T, path string) *attestation.Envelope { func TestRegoEvaluator_Evaluate(t *testing.T) { ctx, _ := test.Setup(t) - + errorStr := "failed to resolve policy by id: policy with id non-existent-policy-id not found" TestDataPath := filepath.Join("..", "..", "test", "testdata") ExampleAttestation := filepath.Join(TestDataPath, "example_attestation.json") @@ -47,8 +47,12 @@ func TestRegoEvaluator_Evaluate(t *testing.T) { isCanonical bool resolver oci.AttestationResolver policy *policy.PolicyOptions + policyId string + errorStr string }{ {repo: "testdata/mock-tuf-allow", expectSuccess: true, isCanonical: false, resolver: defaultResolver}, + {repo: "testdata/mock-tuf-allow", expectSuccess: true, isCanonical: false, resolver: defaultResolver, policyId: "docker-official-images"}, + {repo: "testdata/mock-tuf-allow", expectSuccess: false, isCanonical: false, resolver: defaultResolver, policyId: "non-existent-policy-id", errorStr: errorStr}, {repo: "testdata/mock-tuf-deny", expectSuccess: false, isCanonical: false, resolver: defaultResolver}, {repo: "testdata/mock-tuf-verify-sig", expectSuccess: true, isCanonical: false, resolver: defaultResolver}, {repo: "testdata/mock-tuf-wrong-key", expectSuccess: false, isCanonical: false, resolver: defaultResolver}, @@ -69,11 +73,17 @@ func TestRegoEvaluator_Evaluate(t *testing.T) { tc.policy = &policy.PolicyOptions{ TufClient: tufClient, LocalTargetsDir: test.CreateTempDir(t, "", "tuf-targets"), + PolicyId: tc.policyId, } } policy, err := policy.ResolvePolicy(ctx, tc.resolver, tc.policy) - assert.NoErrorf(t, err, "failed to resolve policy") + if tc.errorStr != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tc.errorStr) + return + } + require.NoErrorf(t, err, "failed to resolve policy") result, err := re.Evaluate(ctx, tc.resolver, policy, input) require.NoErrorf(t, err, "Evaluate failed")