feat: add policy, oci, attestation
This commit is contained in:
41
pkg/policy/evaluator.go
Normal file
41
pkg/policy/evaluator.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/attest/internal/oci"
|
||||
)
|
||||
|
||||
type policyEvaluatorCtxKeyType struct{}
|
||||
|
||||
var PolicyEvaluatorCtxKey policyEvaluatorCtxKeyType
|
||||
|
||||
// sets PolicyEvaluator in context
|
||||
func WithPolicyEvaluator(ctx context.Context, pe PolicyEvaluator) context.Context {
|
||||
return context.WithValue(ctx, PolicyEvaluatorCtxKey, pe)
|
||||
}
|
||||
|
||||
// gets PolicyEvaluator from context, defaults to Rego PolicyEvaluator if not set
|
||||
func GetPolicyEvaluator(ctx context.Context) (PolicyEvaluator, error) {
|
||||
t, ok := ctx.Value(PolicyEvaluatorCtxKey).(PolicyEvaluator)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no policy evaluator client set on context (set one with policy.WithPolicyEvaluator)")
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
type PolicyEvaluator interface {
|
||||
Evaluate(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) error
|
||||
}
|
||||
|
||||
type MockPolicyEvaluator struct {
|
||||
EvaluateFunc func(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) error
|
||||
}
|
||||
|
||||
func (pe *MockPolicyEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) error {
|
||||
if pe.EvaluateFunc != nil {
|
||||
return pe.EvaluateFunc(ctx, resolver, policy, input)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
203
pkg/policy/policy.go
Normal file
203
pkg/policy/policy.go
Normal file
@@ -0,0 +1,203 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/attest/internal/oci"
|
||||
"github.com/docker/attest/internal/util"
|
||||
"github.com/docker/attest/pkg/tuf"
|
||||
|
||||
goyaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
PolicyMappingFileName = "mapping.yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
PolicyFileNames = []string{"data.yaml", "policy.rego"}
|
||||
)
|
||||
|
||||
type PolicyMappings struct {
|
||||
Version string `json:"version"`
|
||||
Kind string `json:"kind"`
|
||||
Policies []PolicyMapping `json:"policies"`
|
||||
Mirrors []PolicyMirror `json:"mirrors"`
|
||||
}
|
||||
|
||||
type PolicyMapping struct {
|
||||
Name string `json:"namespace"`
|
||||
Location string `json:"location"`
|
||||
Description string `json:"description"`
|
||||
Origin PolicyOrigin `json:"origin"`
|
||||
}
|
||||
|
||||
type PolicyMirror struct {
|
||||
Name string `json:"name"`
|
||||
Mirror MirrorSpec `json:"mirror"`
|
||||
}
|
||||
|
||||
type MirrorSpec struct {
|
||||
Domains []string `json:"domains"`
|
||||
Prefix string `json:"prefix"`
|
||||
}
|
||||
|
||||
type PolicyOrigin struct {
|
||||
Name string `json:"name"`
|
||||
Prefix string `json:"prefix"`
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
type PolicyOptions struct {
|
||||
TufClient tuf.TUFClient
|
||||
LocalTargetsDir string
|
||||
LocalPolicyDir string
|
||||
}
|
||||
|
||||
type PolicyInput struct {
|
||||
Digest string `json:"digest"`
|
||||
Purl string `json:"purl"`
|
||||
IsCanonical bool `json:"isCanonical"`
|
||||
}
|
||||
|
||||
type PolicyFile struct {
|
||||
Path string
|
||||
Content []byte
|
||||
}
|
||||
|
||||
func resolveLocalPolicy(opts *PolicyOptions, mapping *PolicyMapping) ([]*PolicyFile, error) {
|
||||
if opts.LocalPolicyDir == "" {
|
||||
return nil, fmt.Errorf("local policy dir not set")
|
||||
}
|
||||
files := make([]*PolicyFile, 0, len(PolicyFileNames))
|
||||
for _, filename := range PolicyFileNames {
|
||||
filePath := path.Join(opts.LocalPolicyDir, mapping.Location, filename)
|
||||
fileContents, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read policy file %s: %w", filename, err)
|
||||
}
|
||||
files = append(files, &PolicyFile{
|
||||
Path: filename,
|
||||
Content: fileContents,
|
||||
})
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func loadLocalMappings(opts *PolicyOptions) (*PolicyMappings, error) {
|
||||
if opts.LocalPolicyDir == "" {
|
||||
return nil, nil
|
||||
}
|
||||
mappings := &PolicyMappings{}
|
||||
path := path.Join(opts.LocalPolicyDir, PolicyMappingFileName)
|
||||
mappingFile, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read policy mapping file %s: %w", path, err)
|
||||
}
|
||||
err = goyaml.Unmarshal(mappingFile, mappings)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal policy mapping file %s: %w", path, err)
|
||||
}
|
||||
return mappings, nil
|
||||
}
|
||||
|
||||
func resolveTufPolicy(opts *PolicyOptions, mapping *PolicyMapping) ([]*PolicyFile, error) {
|
||||
files := make([]*PolicyFile, 0, len(PolicyFileNames))
|
||||
for _, filename := range PolicyFileNames {
|
||||
filePath := path.Join(mapping.Location, filename)
|
||||
_, fileContents, err := opts.TufClient.DownloadTarget(filePath, filepath.Join(opts.LocalTargetsDir, filePath))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to download policy file %s: %w", filename, err)
|
||||
}
|
||||
files = append(files, &PolicyFile{
|
||||
Path: filename,
|
||||
Content: fileContents,
|
||||
})
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func loadTufMappings(tufClient tuf.TUFClient, localTargetsDir string) (*PolicyMappings, error) {
|
||||
filename := PolicyMappingFileName
|
||||
_, fileContents, err := tufClient.DownloadTarget(filename, filepath.Join(localTargetsDir, filename))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to download policy file %s: %w", filename, err)
|
||||
}
|
||||
mappings := &PolicyMappings{}
|
||||
|
||||
err = goyaml.Unmarshal(fileContents, mappings)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal policy mapping file %s: %w", filename, err)
|
||||
}
|
||||
return mappings, nil
|
||||
}
|
||||
|
||||
func findPolicyMatch(named reference.Named, mappings *PolicyMappings) (*PolicyMapping, *PolicyMirror) {
|
||||
if mappings != nil {
|
||||
for _, mapping := range mappings.Policies {
|
||||
if mapping.Origin.Domain == reference.Domain(named) &&
|
||||
strings.HasPrefix(reference.Path(named), mapping.Origin.Prefix) {
|
||||
return &mapping, nil
|
||||
}
|
||||
}
|
||||
// now search mirrors
|
||||
for _, mirror := range mappings.Mirrors {
|
||||
if util.StringInSlice(reference.Domain(named), mirror.Mirror.Domains) &&
|
||||
strings.HasPrefix(reference.Path(named), mirror.Mirror.Prefix) {
|
||||
for _, mapping := range mappings.Policies {
|
||||
if mapping.Name == mirror.Name {
|
||||
return &mapping, nil
|
||||
}
|
||||
}
|
||||
return nil, &mirror
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func ResolvePolicy(ctx context.Context, resolver oci.AttestationResolver, opts *PolicyOptions) ([]*PolicyFile, error) {
|
||||
imageName, err := resolver.ImageName(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get image name: %w", err)
|
||||
}
|
||||
named, err := reference.ParseNormalizedNamed(imageName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse image name: %w", err)
|
||||
}
|
||||
localMappings, err := loadLocalMappings(opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load local policy mappings: %w", err)
|
||||
}
|
||||
mapping, mirror := findPolicyMatch(named, localMappings)
|
||||
if mapping != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
// it's a mirror of a tuf policy
|
||||
if mirror != nil {
|
||||
for _, mapping := range tufMappings.Policies {
|
||||
if mapping.Name == mirror.Name {
|
||||
return resolveTufPolicy(opts, &mapping)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try to resolve a tuf policy directly
|
||||
mapping, _ = findPolicyMatch(named, tufMappings)
|
||||
if mapping == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return resolveTufPolicy(opts, mapping)
|
||||
}
|
||||
110
pkg/policy/policy_test.go
Normal file
110
pkg/policy/policy_test.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package policy_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/attest/internal/oci"
|
||||
"github.com/docker/attest/internal/test"
|
||||
"github.com/docker/attest/pkg/attestation"
|
||||
"github.com/docker/attest/pkg/policy"
|
||||
"github.com/docker/attest/pkg/tuf"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func loadAttestation(t *testing.T, path string) *attestation.Envelope {
|
||||
ex, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var env = new(attestation.Envelope)
|
||||
err = json.Unmarshal(ex, env)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
func TestRegoEvaluator_Evaluate(t *testing.T) {
|
||||
ctx, _ := test.Setup(t)
|
||||
|
||||
TestDataPath := filepath.Join("..", "..", "test", "testdata")
|
||||
MockTufRepo := filepath.Join(TestDataPath, "local-policy")
|
||||
ExampleAttestation := filepath.Join(TestDataPath, "example_attestation.json")
|
||||
VSA := filepath.Join(TestDataPath, "vsa.json")
|
||||
|
||||
re := policy.NewRegoEvaluator(true)
|
||||
|
||||
defaultInput := &policy.PolicyInput{
|
||||
Digest: "sha256:test-digest",
|
||||
Purl: "test-purl",
|
||||
IsCanonical: true,
|
||||
}
|
||||
|
||||
defaultResolver := oci.MockResolver{
|
||||
Envs: []*attestation.Envelope{loadAttestation(t, ExampleAttestation)},
|
||||
}
|
||||
|
||||
vsaResolver := oci.MockResolver{
|
||||
Envs: []*attestation.Envelope{loadAttestation(t, ExampleAttestation), loadAttestation(t, VSA)},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
repo string
|
||||
expectSuccess bool
|
||||
input *policy.PolicyInput
|
||||
resolver oci.AttestationResolver
|
||||
policy *policy.PolicyOptions
|
||||
}{
|
||||
{repo: "testdata/mock-tuf-allow", expectSuccess: true, input: defaultInput, resolver: defaultResolver},
|
||||
{repo: "testdata/mock-tuf-deny", expectSuccess: false, input: defaultInput, resolver: defaultResolver},
|
||||
{repo: "testdata/mock-tuf-verify-sig", expectSuccess: true, input: defaultInput, resolver: defaultResolver},
|
||||
{repo: "testdata/mock-tuf-wrong-key", expectSuccess: false, input: defaultInput, resolver: defaultResolver},
|
||||
{repo: MockTufRepo, expectSuccess: true, input: &policy.PolicyInput{
|
||||
Digest: "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
Purl: "pkg:docker/test-image@test?platform=linux%2Famd64",
|
||||
IsCanonical: true,
|
||||
}, resolver: vsaResolver},
|
||||
{repo: MockTufRepo, expectSuccess: true, input: &policy.PolicyInput{
|
||||
Digest: "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
Purl: "pkg:docker/test-image@test?platform=linux%2Famd64",
|
||||
IsCanonical: false,
|
||||
}, resolver: vsaResolver},
|
||||
// not a doi
|
||||
{repo: MockTufRepo, expectSuccess: false, input: defaultInput, resolver: vsaResolver, policy: &policy.PolicyOptions{
|
||||
LocalPolicyDir: "testdata/mock-tuf-deny",
|
||||
}},
|
||||
// digest mismatch
|
||||
{repo: MockTufRepo, expectSuccess: false, input: &policy.PolicyInput{
|
||||
Digest: "sha256:test-digest-wrong",
|
||||
Purl: "test-purl",
|
||||
IsCanonical: false,
|
||||
}, resolver: vsaResolver},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.repo, func(t *testing.T) {
|
||||
tufClient := tuf.NewMockTufClient(tc.repo, test.CreateTempDir(t, "", "tuf-dest"))
|
||||
if tc.policy == nil {
|
||||
tc.policy = &policy.PolicyOptions{
|
||||
TufClient: tufClient,
|
||||
LocalTargetsDir: test.CreateTempDir(t, "", "tuf-targets"),
|
||||
}
|
||||
}
|
||||
|
||||
policyFiles, err := policy.ResolvePolicy(ctx, tc.resolver, tc.policy)
|
||||
assert.NoErrorf(t, err, "failed to resolve policy")
|
||||
err = re.Evaluate(ctx, tc.resolver, policyFiles, tc.input)
|
||||
|
||||
if tc.expectSuccess {
|
||||
assert.NoErrorf(t, err, "Evaluate failed")
|
||||
} else {
|
||||
assert.Errorf(t, err, "Evaluate should have failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
225
pkg/policy/rego.go
Normal file
225
pkg/policy/rego.go
Normal file
@@ -0,0 +1,225 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/attest/internal/oci"
|
||||
att "github.com/docker/attest/pkg/attestation"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/rego"
|
||||
"github.com/open-policy-agent/opa/storage"
|
||||
"github.com/open-policy-agent/opa/storage/inmem"
|
||||
"github.com/open-policy-agent/opa/tester"
|
||||
"github.com/open-policy-agent/opa/topdown"
|
||||
"github.com/open-policy-agent/opa/types"
|
||||
opa "github.com/open-policy-agent/opa/util"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type regoEvaluator struct {
|
||||
debug bool
|
||||
}
|
||||
|
||||
func NewRegoEvaluator(debug bool) PolicyEvaluator {
|
||||
return ®oEvaluator{
|
||||
debug: debug,
|
||||
}
|
||||
}
|
||||
|
||||
func (re *regoEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, files []*PolicyFile, input *PolicyInput) error {
|
||||
var regoOpts []func(*rego.Rego)
|
||||
|
||||
// Create a new in-memory store
|
||||
store := inmem.New()
|
||||
params := storage.TransactionParams{}
|
||||
params.Write = true
|
||||
txn, err := store.NewTransaction(ctx, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, target := range files {
|
||||
// load yaml as data (no rego opt for this!?)
|
||||
if filepath.Ext(target.Path) == ".yaml" {
|
||||
yamlData, err := loadYAML(target.Path, target.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = store.Write(ctx, txn, storage.AddOp, storage.Path{}, yamlData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
regoOpts = append(regoOpts, rego.Module(target.Path, string(target.Content)))
|
||||
}
|
||||
}
|
||||
|
||||
err = store.Commit(ctx, txn)
|
||||
if err != nil {
|
||||
store.Abort(ctx, txn)
|
||||
return err
|
||||
}
|
||||
|
||||
if re.debug {
|
||||
regoOpts = append(regoOpts,
|
||||
rego.EnablePrintStatements(true),
|
||||
rego.PrintHook(topdown.NewPrintHook(os.Stderr)),
|
||||
rego.Dump(os.Stderr),
|
||||
)
|
||||
}
|
||||
|
||||
regoOpts = append(regoOpts,
|
||||
rego.Query("data.docker.allow"),
|
||||
rego.StrictBuiltinErrors(true),
|
||||
rego.Input(input),
|
||||
rego.Store(store),
|
||||
)
|
||||
for _, custom := range RegoFunctions(resolver) {
|
||||
regoOpts = append(regoOpts, custom.Func)
|
||||
}
|
||||
|
||||
r := rego.New(regoOpts...)
|
||||
rs, err := r.Eval(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error from Eval: %w", err)
|
||||
}
|
||||
|
||||
if !rs.Allowed() {
|
||||
return fmt.Errorf("policy evaluation failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var dynamicObj = types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))
|
||||
var arrayObj = types.NewArray(nil, dynamicObj)
|
||||
var verifyDecl = &ast.Builtin{
|
||||
Name: "attestations.verify_envelope",
|
||||
Decl: types.NewFunction(types.Args(dynamicObj, arrayObj), dynamicObj),
|
||||
Nondeterministic: true,
|
||||
}
|
||||
var attestDecl = &ast.Builtin{
|
||||
Name: "attestations.attestation",
|
||||
Decl: types.NewFunction(types.Args(types.S), dynamicObj),
|
||||
Nondeterministic: true,
|
||||
}
|
||||
|
||||
func RegoFunctions(resolver oci.AttestationResolver) []*tester.Builtin {
|
||||
return []*tester.Builtin{
|
||||
{
|
||||
Decl: verifyDecl,
|
||||
Func: rego.Function2(
|
||||
®o.Function{
|
||||
Name: verifyDecl.Name,
|
||||
Decl: verifyDecl.Decl,
|
||||
Memoize: true,
|
||||
Nondeterministic: verifyDecl.Nondeterministic,
|
||||
},
|
||||
verifyIntotoEnvelope),
|
||||
},
|
||||
{
|
||||
Decl: attestDecl,
|
||||
Func: rego.Function1(
|
||||
®o.Function{
|
||||
Name: attestDecl.Name,
|
||||
Decl: attestDecl.Decl,
|
||||
Memoize: true,
|
||||
Nondeterministic: attestDecl.Nondeterministic,
|
||||
},
|
||||
fetchIntotoAttestations(resolver)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func fetchIntotoAttestations(resolver oci.AttestationResolver) func(rego.BuiltinContext, *ast.Term) (*ast.Term, error) {
|
||||
return func(rCtx rego.BuiltinContext, predicateTypeTerm *ast.Term) (*ast.Term, error) {
|
||||
predicateTypeStr, ok := predicateTypeTerm.Value.(ast.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("predicateTypeTerm is not a string")
|
||||
}
|
||||
predicateType := string(predicateTypeStr)
|
||||
|
||||
envelopes, err := resolver.Attestations(rCtx.Context, predicateType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert each envelope to an ast.Value.
|
||||
values := make([]*ast.Term, len(envelopes))
|
||||
for i, envelope := range envelopes {
|
||||
value, err := ast.InterfaceToValue(envelope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values[i] = ast.NewTerm(value)
|
||||
}
|
||||
|
||||
// Wrap the values in an ast.Array and convert it to an ast.Term.
|
||||
array := ast.NewTerm(ast.NewArray(values...))
|
||||
|
||||
return array, nil
|
||||
}
|
||||
}
|
||||
func verifyIntotoEnvelope(rCtx rego.BuiltinContext, envTerm, keysTerm *ast.Term) (*ast.Term, error) {
|
||||
env := new(att.Envelope)
|
||||
var keys att.Keys
|
||||
err := ast.As(envTerm.Value, env)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to cast envelope: %w", err)
|
||||
}
|
||||
err = ast.As(keysTerm.Value, &keys)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to cast keys: %w", err)
|
||||
}
|
||||
keysmap := make(map[string]att.KeyMetadata, len(keys))
|
||||
for _, key := range keys {
|
||||
keysmap[key.ID] = key
|
||||
}
|
||||
payload, err := att.VerifyDSSE(rCtx.Context, env, keysmap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statement := new(intoto.Statement)
|
||||
|
||||
switch env.PayloadType {
|
||||
case intoto.PayloadType:
|
||||
err = json.Unmarshal(payload, statement)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal statement: %w", err)
|
||||
}
|
||||
// TODO: implement other types of envelope
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported payload type: %s", env.PayloadType)
|
||||
}
|
||||
|
||||
value, err := ast.InterfaceToValue(statement)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ast.NewTerm(value), nil
|
||||
}
|
||||
|
||||
// copied from opa
|
||||
func loadYAML(path string, bs []byte) (interface{}, error) {
|
||||
bs, err := yaml.YAMLToJSON(bs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%v: error converting YAML to JSON: %v", path, err)
|
||||
}
|
||||
return loadJSON(path, bs)
|
||||
}
|
||||
|
||||
func loadJSON(path string, bs []byte) (interface{}, error) {
|
||||
var x interface{}
|
||||
err := opa.UnmarshalJSON(bs, &x)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", path, err)
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
1
pkg/policy/testdata/mock-tuf-allow/doi/data.yaml
vendored
Normal file
1
pkg/policy/testdata/mock-tuf-allow/doi/data.yaml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
config:
|
||||
5
pkg/policy/testdata/mock-tuf-allow/doi/policy.rego
vendored
Normal file
5
pkg/policy/testdata/mock-tuf-allow/doi/policy.rego
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package docker
|
||||
|
||||
import rego.v1
|
||||
|
||||
allow := true
|
||||
10
pkg/policy/testdata/mock-tuf-allow/mapping.yaml
vendored
Normal file
10
pkg/policy/testdata/mock-tuf-allow/mapping.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# map repos to policies
|
||||
version: v1
|
||||
kind: policy-mapping
|
||||
policies:
|
||||
- origin:
|
||||
domain: docker.io
|
||||
prefix: library/
|
||||
name: docker-official-images
|
||||
description: Docker Official Images
|
||||
location: doi
|
||||
1
pkg/policy/testdata/mock-tuf-deny/doi/data.yaml
vendored
Normal file
1
pkg/policy/testdata/mock-tuf-deny/doi/data.yaml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
config:
|
||||
5
pkg/policy/testdata/mock-tuf-deny/doi/policy.rego
vendored
Normal file
5
pkg/policy/testdata/mock-tuf-deny/doi/policy.rego
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package docker
|
||||
|
||||
import rego.v1
|
||||
|
||||
allow := false
|
||||
10
pkg/policy/testdata/mock-tuf-deny/mapping.yaml
vendored
Normal file
10
pkg/policy/testdata/mock-tuf-deny/mapping.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# map repos to policies
|
||||
version: v1
|
||||
kind: policy-mapping
|
||||
policies:
|
||||
- origin:
|
||||
domain: docker.io
|
||||
prefix: library/
|
||||
name: docker-official-images
|
||||
description: Docker Official Images
|
||||
location: doi
|
||||
1
pkg/policy/testdata/mock-tuf-verify-sig/doi/data.yaml
vendored
Normal file
1
pkg/policy/testdata/mock-tuf-verify-sig/doi/data.yaml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
config:
|
||||
15
pkg/policy/testdata/mock-tuf-verify-sig/doi/policy.rego
vendored
Normal file
15
pkg/policy/testdata/mock-tuf-verify-sig/doi/policy.rego
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package docker
|
||||
|
||||
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
|
||||
}]
|
||||
|
||||
allow if {
|
||||
some env in attestations.attestation("foo")
|
||||
statement := attestations.verify_envelope(env, keys)
|
||||
}
|
||||
10
pkg/policy/testdata/mock-tuf-verify-sig/mapping.yaml
vendored
Normal file
10
pkg/policy/testdata/mock-tuf-verify-sig/mapping.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# map repos to policies
|
||||
version: v1
|
||||
kind: policy-mapping
|
||||
policies:
|
||||
- origin:
|
||||
domain: docker.io
|
||||
prefix: library/
|
||||
name: docker-official-images
|
||||
description: Docker Official Images
|
||||
location: doi
|
||||
1
pkg/policy/testdata/mock-tuf-wrong-key/doi/data.yaml
vendored
Normal file
1
pkg/policy/testdata/mock-tuf-wrong-key/doi/data.yaml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
config:
|
||||
19
pkg/policy/testdata/mock-tuf-wrong-key/doi/policy.rego
vendored
Normal file
19
pkg/policy/testdata/mock-tuf-wrong-key/doi/policy.rego
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package docker
|
||||
|
||||
import rego.v1
|
||||
|
||||
keys := {
|
||||
"a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4": {
|
||||
"id": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4",
|
||||
"key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHyZpSgzvqFqNv7f3x7865OS38rAb\nQMcff55zM2UH/KR3Pr84a8QsGDNgaNGzJQJWjtMSgfV8WnNoffNK+svFNg==\n-----END PUBLIC KEY-----",
|
||||
"from": "2023-12-15T14:00:00Z",
|
||||
"to": null,
|
||||
}
|
||||
}
|
||||
|
||||
allow if {
|
||||
some env in attestations.attestation("foo")
|
||||
statement := attestations.verify_envelope(env, keys)
|
||||
}
|
||||
|
||||
allow := true
|
||||
10
pkg/policy/testdata/mock-tuf-wrong-key/mapping.yaml
vendored
Normal file
10
pkg/policy/testdata/mock-tuf-wrong-key/mapping.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# map repos to policies
|
||||
version: v1
|
||||
kind: policy-mapping
|
||||
policies:
|
||||
- origin:
|
||||
domain: docker.io
|
||||
prefix: library/
|
||||
name: docker-official-images
|
||||
description: Docker Official Images
|
||||
location: doi
|
||||
Reference in New Issue
Block a user