Various fixes (#63)
* Fix digest resolution and attestation style * Add a bunch more tests * Rename fields for consistency * Remove copy-pasta * Value -> pointer
This commit is contained in:
@@ -18,7 +18,12 @@ func Verify(ctx context.Context, src *oci.ImageSpec, opts *policy.PolicyOptions)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create image details resolver: %w", err)
|
return nil, fmt.Errorf("failed to create image details resolver: %w", err)
|
||||||
}
|
}
|
||||||
|
if opts.AttestationStyle == "" {
|
||||||
|
opts.AttestationStyle = config.AttestationStyleReferrers
|
||||||
|
}
|
||||||
|
if opts.ReferrersRepo != "" && opts.AttestationStyle != config.AttestationStyleReferrers {
|
||||||
|
return nil, fmt.Errorf("referrers repo specified but attestation source not set to referrers")
|
||||||
|
}
|
||||||
pctx, err := policy.ResolvePolicy(ctx, detailsResolver, opts)
|
pctx, err := policy.ResolvePolicy(ctx, detailsResolver, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to resolve policy: %w", err)
|
return nil, fmt.Errorf("failed to resolve policy: %w", err)
|
||||||
@@ -33,7 +38,12 @@ func Verify(ctx context.Context, src *oci.ImageSpec, opts *policy.PolicyOptions)
|
|||||||
if opts.ReferrersRepo != "" {
|
if opts.ReferrersRepo != "" {
|
||||||
pctx.Mapping.Attestations = &config.ReferrersConfig{
|
pctx.Mapping.Attestations = &config.ReferrersConfig{
|
||||||
Repo: opts.ReferrersRepo,
|
Repo: opts.ReferrersRepo,
|
||||||
Style: config.AttestationSourceReferrers,
|
Style: config.AttestationStyleReferrers,
|
||||||
|
}
|
||||||
|
} else if opts.AttestationStyle == config.AttestationStyleAttached {
|
||||||
|
pctx.Mapping.Attestations = &config.ReferrersConfig{
|
||||||
|
Repo: opts.ReferrersRepo,
|
||||||
|
Style: config.AttestationStyleAttached,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// because we have a mapping now, we can select a resolver based on its contents (ie. referrers or attached)
|
// because we have a mapping now, we can select a resolver based on its contents (ie. referrers or attached)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/docker/attest/internal/test"
|
"github.com/docker/attest/internal/test"
|
||||||
"github.com/docker/attest/pkg/attest"
|
"github.com/docker/attest/pkg/attest"
|
||||||
"github.com/docker/attest/pkg/attestation"
|
"github.com/docker/attest/pkg/attestation"
|
||||||
|
"github.com/docker/attest/pkg/config"
|
||||||
"github.com/docker/attest/pkg/mirror"
|
"github.com/docker/attest/pkg/mirror"
|
||||||
"github.com/docker/attest/pkg/oci"
|
"github.com/docker/attest/pkg/oci"
|
||||||
"github.com/docker/attest/pkg/policy"
|
"github.com/docker/attest/pkg/policy"
|
||||||
@@ -21,21 +22,29 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
UnsignedTestImage = filepath.Join("..", "..", "test", "testdata", "unsigned-test-image")
|
UnsignedTestImage = filepath.Join("..", "..", "test", "testdata", "unsigned-test-image")
|
||||||
NoProvenanceImage = filepath.Join("..", "..", "test", "testdata", "no-provenance-image")
|
NoProvenanceImage = filepath.Join("..", "..", "test", "testdata", "no-provenance-image")
|
||||||
PassPolicyDir = filepath.Join("..", "..", "test", "testdata", "local-policy-pass")
|
PassPolicyDir = filepath.Join("..", "..", "test", "testdata", "local-policy-pass")
|
||||||
PassNoTLPolicyDir = filepath.Join("..", "..", "test", "testdata", "local-policy-no-tl")
|
LocalPolicy = filepath.Join("..", "..", "test", "testdata", "local-policy")
|
||||||
FailPolicyDir = filepath.Join("..", "..", "test", "testdata", "local-policy-fail")
|
LocalPolicyAttached = filepath.Join("..", "..", "test", "testdata", "local-policy-attached")
|
||||||
TestTempDir = "attest-sign-test"
|
PassNoTLPolicyDir = filepath.Join("..", "..", "test", "testdata", "local-policy-no-tl")
|
||||||
|
FailPolicyDir = filepath.Join("..", "..", "test", "testdata", "local-policy-fail")
|
||||||
|
TestTempDir = "attest-sign-test"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAttestationReferenceTypes(t *testing.T) {
|
func TestAttestationReferenceTypes(t *testing.T) {
|
||||||
ctx, signer := test.Setup(t)
|
ctx, signer := test.Setup(t)
|
||||||
|
ctx = policy.WithPolicyEvaluator(ctx, policy.NewRegoEvaluator(true))
|
||||||
platforms := []string{"linux/amd64", "linux/arm64"}
|
platforms := []string{"linux/amd64", "linux/arm64"}
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
server *httptest.Server
|
server *httptest.Server
|
||||||
skipSubject bool
|
referrersServer *httptest.Server
|
||||||
useDigest bool
|
skipSubject bool
|
||||||
|
useDigest bool
|
||||||
|
referrersRepo string
|
||||||
|
attestationSource config.AttestationStyle
|
||||||
|
expectFailure bool
|
||||||
|
policyDir string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
server: httptest.NewServer(registry.New(registry.WithReferrersSupport(true))),
|
server: httptest.NewServer(registry.New(registry.WithReferrersSupport(true))),
|
||||||
@@ -44,76 +53,135 @@ func TestAttestationReferenceTypes(t *testing.T) {
|
|||||||
server: httptest.NewServer(registry.New()),
|
server: httptest.NewServer(registry.New()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
server: httptest.NewServer(registry.New(registry.WithReferrersSupport(true))),
|
server: httptest.NewServer(registry.New(registry.WithReferrersSupport(true))),
|
||||||
skipSubject: true,
|
skipSubject: true,
|
||||||
|
attestationSource: config.AttestationStyleAttached,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
server: httptest.NewServer(registry.New(registry.WithReferrersSupport(true))),
|
server: httptest.NewServer(registry.New(registry.WithReferrersSupport(true))),
|
||||||
useDigest: true,
|
useDigest: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
server: httptest.NewServer(registry.New(registry.WithReferrersSupport(true))),
|
||||||
|
expectFailure: true, //mismatched args
|
||||||
|
attestationSource: config.AttestationStyleAttached,
|
||||||
|
referrersRepo: "referrers",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
server: httptest.NewServer(registry.New(registry.WithReferrersSupport(true))),
|
||||||
|
expectFailure: true, // no policy
|
||||||
|
attestationSource: config.AttestationStyleReferrers,
|
||||||
|
referrersRepo: "referrers",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
server: httptest.NewServer(registry.New(registry.WithReferrersSupport(true))),
|
||||||
|
attestationSource: config.AttestationStyleReferrers,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
server: httptest.NewServer(registry.New(registry.WithReferrersSupport(false))),
|
||||||
|
attestationSource: config.AttestationStyleReferrers,
|
||||||
|
referrersServer: httptest.NewServer(registry.New(registry.WithReferrersSupport(true))),
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
s := tc.server
|
t.Run(fmt.Sprint(tc), func(t *testing.T) {
|
||||||
defer s.Close()
|
s := tc.server
|
||||||
u, err := url.Parse(s.URL)
|
defer s.Close()
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
opts := &attestation.SigningOptions{
|
if tc.referrersServer != nil {
|
||||||
Replace: true,
|
defer tc.referrersServer.Close()
|
||||||
SkipSubject: tc.skipSubject,
|
|
||||||
}
|
|
||||||
attIdx, err := oci.SubjectIndexFromPath(UnsignedTestImage)
|
|
||||||
require.NoError(t, err)
|
|
||||||
signedIndex, err := attest.Sign(ctx, attIdx.Index, signer, opts)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
indexName := fmt.Sprintf("%s/repo:root", u.Host)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = mirror.PushIndexToRegistry(signedIndex, indexName)
|
|
||||||
|
|
||||||
for _, platform := range platforms {
|
|
||||||
// can eval policy in the normal way
|
|
||||||
ref := indexName
|
|
||||||
if tc.useDigest {
|
|
||||||
options := oci.WithOptions(ctx, nil)
|
|
||||||
subjectRef, err := name.ParseReference(indexName)
|
|
||||||
require.NoError(t, err)
|
|
||||||
desc, err := remote.Index(subjectRef, options...)
|
|
||||||
require.NoError(t, err)
|
|
||||||
idxDigest, err := desc.Digest()
|
|
||||||
require.NoError(t, err)
|
|
||||||
ref = fmt.Sprintf("%s/repo@%s", u.Host, idxDigest.String())
|
|
||||||
}
|
}
|
||||||
|
u, err := url.Parse(s.URL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
policyOpts := &policy.PolicyOptions{
|
opts := &attestation.SigningOptions{
|
||||||
LocalPolicyDir: PassPolicyDir,
|
Replace: true,
|
||||||
|
SkipSubject: tc.skipSubject,
|
||||||
}
|
}
|
||||||
src, err := oci.ParseImageSpec(ref, oci.WithPlatform(platform))
|
attIdx, err := oci.SubjectIndexFromPath(UnsignedTestImage)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
results, err := attest.Verify(ctx, src, policyOpts)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, attest.OutcomeSuccess, results.Outcome)
|
|
||||||
|
|
||||||
if !tc.skipSubject {
|
indexName := fmt.Sprintf("%s/repo:root", u.Host)
|
||||||
// can evaluate policy using referrers
|
require.NoError(t, err)
|
||||||
if tc.useDigest {
|
|
||||||
p, err := oci.ParsePlatform(platform)
|
if tc.referrersServer != nil {
|
||||||
|
ru, err := url.Parse(s.URL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
repo := fmt.Sprintf("%s/referrers", ru.Host)
|
||||||
|
tc.referrersRepo = repo
|
||||||
|
images, err := attest.SignedAttestationImages(ctx, attIdx.Index, signer, opts)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = mirror.PushIndexToRegistry(attIdx.Index, indexName)
|
||||||
|
for _, img := range images {
|
||||||
|
err = mirror.PushImageToRegistry(img.Image, fmt.Sprintf("%s:tag-does-not-matter", repo))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
options := oci.WithOptions(ctx, p)
|
}
|
||||||
|
} else {
|
||||||
|
signedIndex, err := attest.Sign(ctx, attIdx.Index, signer, opts)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = mirror.PushIndexToRegistry(signedIndex, indexName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, platform := range platforms {
|
||||||
|
// can eval policy in the normal way
|
||||||
|
ref := indexName
|
||||||
|
if tc.useDigest {
|
||||||
|
options := oci.WithOptions(ctx, nil)
|
||||||
subjectRef, err := name.ParseReference(indexName)
|
subjectRef, err := name.ParseReference(indexName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
desc, err := remote.Image(subjectRef, options...)
|
desc, err := remote.Index(subjectRef, options...)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
subjectDigest, err := desc.Digest()
|
idxDigest, err := desc.Digest()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ref = fmt.Sprintf("%s/repo@%s", u.Host, subjectDigest.String())
|
ref = fmt.Sprintf("%s/repo@%s", u.Host, idxDigest.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
policyOpts := &policy.PolicyOptions{
|
||||||
|
LocalPolicyDir: LocalPolicy,
|
||||||
|
}
|
||||||
|
if tc.policyDir != "" {
|
||||||
|
policyOpts.LocalPolicyDir = tc.policyDir
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.referrersRepo != "" {
|
||||||
|
policyOpts.ReferrersRepo = tc.referrersRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.attestationSource != "" {
|
||||||
|
policyOpts.AttestationStyle = tc.attestationSource
|
||||||
}
|
}
|
||||||
src, err := oci.ParseImageSpec(ref, oci.WithPlatform(platform))
|
src, err := oci.ParseImageSpec(ref, oci.WithPlatform(platform))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
results, err = attest.Verify(ctx, src, policyOpts)
|
results, err := attest.Verify(ctx, src, policyOpts)
|
||||||
|
if tc.expectFailure {
|
||||||
|
require.Error(t, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, attest.OutcomeSuccess, results.Outcome)
|
assert.Equal(t, attest.OutcomeSuccess, results.Outcome)
|
||||||
|
|
||||||
|
if !tc.skipSubject {
|
||||||
|
// can evaluate policy using referrers
|
||||||
|
if tc.useDigest {
|
||||||
|
p, err := oci.ParsePlatform(platform)
|
||||||
|
require.NoError(t, err)
|
||||||
|
options := oci.WithOptions(ctx, p)
|
||||||
|
subjectRef, err := name.ParseReference(indexName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
desc, err := remote.Image(subjectRef, options...)
|
||||||
|
require.NoError(t, err)
|
||||||
|
subjectDigest, err := desc.Digest()
|
||||||
|
require.NoError(t, err)
|
||||||
|
ref = fmt.Sprintf("%s/repo@%s", u.Host, subjectDigest.String())
|
||||||
|
}
|
||||||
|
src, err := oci.ParseImageSpec(ref, oci.WithPlatform(platform))
|
||||||
|
require.NoError(t, err)
|
||||||
|
results, err = attest.Verify(ctx, src, policyOpts)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, attest.OutcomeSuccess, results.Outcome)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
type PolicyMappings struct {
|
type PolicyMappings struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Kind string `json:"kind"`
|
Kind string `json:"kind"`
|
||||||
Policies []PolicyMapping `json:"policies"`
|
Policies []*PolicyMapping `json:"policies"`
|
||||||
Mirrors []PolicyMirror `json:"mirrors"`
|
Mirrors []*PolicyMirror `json:"mirrors"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AttestationSource string
|
type AttestationStyle string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AttestationSourceAttached AttestationSource = "attached"
|
AttestationStyleAttached AttestationStyle = "attached"
|
||||||
AttestationSourceReferrers AttestationSource = "referrers"
|
AttestationStyleReferrers AttestationStyle = "referrers"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PolicyMapping struct {
|
type PolicyMapping struct {
|
||||||
@@ -23,8 +23,8 @@ type PolicyMapping struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ReferrersConfig struct {
|
type ReferrersConfig struct {
|
||||||
Style AttestationSource `json:"style"`
|
Style AttestationStyle `json:"style"`
|
||||||
Repo string `json:"repo"`
|
Repo string `json:"repo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PolicyMappingFile struct {
|
type PolicyMappingFile struct {
|
||||||
|
|||||||
@@ -47,24 +47,16 @@ func (r *RegistryImageDetailsResolver) ImageDigest(ctx context.Context) (string,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to parse reference: %w", err)
|
return "", fmt.Errorf("failed to parse reference: %w", err)
|
||||||
}
|
}
|
||||||
switch t := subjectRef.(type) {
|
options := WithOptions(ctx, r.Platform)
|
||||||
case name.Digest:
|
desc, err := remote.Image(subjectRef, options...)
|
||||||
// TODO should check if this is an index or an image
|
if err != nil {
|
||||||
r.digest = t.DigestStr()
|
return "", fmt.Errorf("failed to get image manifest: %w", err)
|
||||||
case name.Tag:
|
|
||||||
options := WithOptions(ctx, r.Platform)
|
|
||||||
desc, err := remote.Image(t, options...)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to get image manifest: %w", err)
|
|
||||||
}
|
|
||||||
subjectDigest, err := desc.Digest()
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to get image digest: %w", err)
|
|
||||||
}
|
|
||||||
r.digest = subjectDigest.String()
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("unsupported reference type: %T", t)
|
|
||||||
}
|
}
|
||||||
|
subjectDigest, err := desc.Digest()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get image digest: %w", err)
|
||||||
|
}
|
||||||
|
r.digest = subjectDigest.String()
|
||||||
}
|
}
|
||||||
return r.digest, nil
|
return r.digest, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func findPolicyMatch(named reference.Named, mappings *config.PolicyMappings) (*c
|
|||||||
for _, mapping := range mappings.Policies {
|
for _, mapping := range mappings.Policies {
|
||||||
if mapping.Origin.Domain == reference.Domain(named) &&
|
if mapping.Origin.Domain == reference.Domain(named) &&
|
||||||
strings.HasPrefix(reference.Path(named), mapping.Origin.Prefix) {
|
strings.HasPrefix(reference.Path(named), mapping.Origin.Prefix) {
|
||||||
return &mapping, nil
|
return mapping, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// now search mirrors
|
// now search mirrors
|
||||||
@@ -73,10 +73,10 @@ func findPolicyMatch(named reference.Named, mappings *config.PolicyMappings) (*c
|
|||||||
strings.HasPrefix(reference.Path(named), mirror.Mirror.Prefix) {
|
strings.HasPrefix(reference.Path(named), mirror.Mirror.Prefix) {
|
||||||
for _, mapping := range mappings.Policies {
|
for _, mapping := range mappings.Policies {
|
||||||
if mapping.Id == mirror.PolicyId {
|
if mapping.Id == mirror.PolicyId {
|
||||||
return &mapping, nil
|
return mapping, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, &mirror
|
return nil, mirror
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ func resolvePolicyById(opts *PolicyOptions) (*Policy, error) {
|
|||||||
if localMappings != nil {
|
if localMappings != nil {
|
||||||
for _, mapping := range localMappings.Policies {
|
for _, mapping := range localMappings.Policies {
|
||||||
if mapping.Id == opts.PolicyId {
|
if mapping.Id == opts.PolicyId {
|
||||||
return resolveLocalPolicy(opts, &mapping)
|
return resolveLocalPolicy(opts, mapping)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ func resolvePolicyById(opts *PolicyOptions) (*Policy, error) {
|
|||||||
}
|
}
|
||||||
for _, mapping := range tufMappings.Policies {
|
for _, mapping := range tufMappings.Policies {
|
||||||
if mapping.Id == opts.PolicyId {
|
if mapping.Id == opts.PolicyId {
|
||||||
return resolveTufPolicy(opts, &mapping)
|
return resolveTufPolicy(opts, mapping)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("policy with id %s not found", opts.PolicyId)
|
return nil, fmt.Errorf("policy with id %s not found", opts.PolicyId)
|
||||||
@@ -146,7 +146,7 @@ func ResolvePolicy(ctx context.Context, detailsResolver oci.ImageDetailsResolver
|
|||||||
if mirror != nil {
|
if mirror != nil {
|
||||||
for _, mapping := range tufMappings.Policies {
|
for _, mapping := range tufMappings.Policies {
|
||||||
if mapping.Id == mirror.PolicyId {
|
if mapping.Id == mirror.PolicyId {
|
||||||
return resolveTufPolicy(opts, &mapping)
|
return resolveTufPolicy(opts, mapping)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,7 +172,7 @@ func CreateImageDetailsResolver(imageSource *oci.ImageSpec) (oci.ImageDetailsRes
|
|||||||
func CreateAttestationResolver(resolver oci.ImageDetailsResolver, mapping *config.PolicyMapping) (oci.AttestationResolver, error) {
|
func CreateAttestationResolver(resolver oci.ImageDetailsResolver, mapping *config.PolicyMapping) (oci.AttestationResolver, error) {
|
||||||
switch resolver := resolver.(type) {
|
switch resolver := resolver.(type) {
|
||||||
case *oci.RegistryImageDetailsResolver:
|
case *oci.RegistryImageDetailsResolver:
|
||||||
if mapping.Attestations != nil && mapping.Attestations.Style == config.AttestationSourceAttached {
|
if mapping.Attestations != nil && mapping.Attestations.Style == config.AttestationStyleAttached {
|
||||||
return oci.NewRegistryAttestationResolver(resolver)
|
return oci.NewRegistryAttestationResolver(resolver)
|
||||||
} else {
|
} else {
|
||||||
if mapping.Attestations != nil && mapping.Attestations.Repo != "" {
|
if mapping.Attestations != nil && mapping.Attestations.Repo != "" {
|
||||||
|
|||||||
@@ -27,11 +27,12 @@ type Result struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PolicyOptions struct {
|
type PolicyOptions struct {
|
||||||
TufClient tuf.TUFClient
|
TufClient tuf.TUFClient
|
||||||
LocalTargetsDir string
|
LocalTargetsDir string
|
||||||
LocalPolicyDir string
|
LocalPolicyDir string
|
||||||
PolicyId string
|
PolicyId string
|
||||||
ReferrersRepo string
|
ReferrersRepo string
|
||||||
|
AttestationStyle config.AttestationStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
type Policy struct {
|
type Policy struct {
|
||||||
|
|||||||
49
test/testdata/local-policy/doi/policy.rego
vendored
Normal file
49
test/testdata/local-policy/doi/policy.rego
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package attest
|
||||||
|
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
keys := [{
|
||||||
|
"id": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4",
|
||||||
|
"key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgH23D1i2+ZIOtVjmfB7iFvX8AhVN\n9CPJ4ie9axw+WRHozGnRy99U2dRge3zueBBg2MweF0zrToXGig2v3YOrdw==\n-----END PUBLIC KEY-----",
|
||||||
|
"from": "2023-12-15T14:00:00Z",
|
||||||
|
"to": null,
|
||||||
|
"status": "active",
|
||||||
|
"signing-format": "dssev1",
|
||||||
|
}]
|
||||||
|
|
||||||
|
provs(pred) := p if {
|
||||||
|
res := attest.fetch(pred)
|
||||||
|
not res.error
|
||||||
|
p := res.value
|
||||||
|
}
|
||||||
|
|
||||||
|
atts := union({
|
||||||
|
provs("https://slsa.dev/provenance/v0.2"),
|
||||||
|
provs("https://spdx.dev/Document"),
|
||||||
|
})
|
||||||
|
|
||||||
|
opts := {"keys": keys}
|
||||||
|
|
||||||
|
statements contains s if {
|
||||||
|
some att in atts
|
||||||
|
res := attest.verify(att, opts)
|
||||||
|
not res.error
|
||||||
|
s := res.value
|
||||||
|
}
|
||||||
|
|
||||||
|
subjects contains subject if {
|
||||||
|
some statement in statements
|
||||||
|
some subject in statement.subject
|
||||||
|
}
|
||||||
|
|
||||||
|
result := {
|
||||||
|
"success": count(atts) > 0,
|
||||||
|
"violations": set(),
|
||||||
|
"attestations": statements,
|
||||||
|
"summary": {
|
||||||
|
"subjects": subjects,
|
||||||
|
"slsa_level": "SLSA_BUILD_LEVEL_3",
|
||||||
|
"verifier": "docker-official-images",
|
||||||
|
"policy_uri": "https://docker.com/official/policy/v0.1",
|
||||||
|
},
|
||||||
|
}
|
||||||
29
test/testdata/local-policy/mapping.yaml
vendored
Normal file
29
test/testdata/local-policy/mapping.yaml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# map repos to policies
|
||||||
|
version: v1
|
||||||
|
kind: policy-mapping
|
||||||
|
policies:
|
||||||
|
- origin:
|
||||||
|
domain: docker.io
|
||||||
|
prefix: library/
|
||||||
|
id: test-images
|
||||||
|
description: Local test images
|
||||||
|
files:
|
||||||
|
- path: "doi/policy.rego"
|
||||||
|
|
||||||
|
mirrors:
|
||||||
|
- policy-id: test-images
|
||||||
|
mirror:
|
||||||
|
domains: ["*"]
|
||||||
|
prefix: "repo"
|
||||||
|
- policy-id: test-images
|
||||||
|
mirror:
|
||||||
|
domains: ["*"]
|
||||||
|
prefix: "library/"
|
||||||
|
- policy-id: test-images
|
||||||
|
mirror:
|
||||||
|
domains: ["*"]
|
||||||
|
prefix: "test-image"
|
||||||
|
- policy-id: test-images
|
||||||
|
mirror:
|
||||||
|
domains: ["*"]
|
||||||
|
prefix: "image-signer-verifier-test"
|
||||||
Reference in New Issue
Block a user