Return VSA and rich errors from verification (#38)
* Start of richer results from verification * Pull out VSA code from signing * Expose attestation signing fns * Add VSA test * Notes for policy result * Require separate policy for VSA creation * Load test signing key from tests * Return rich object from policy * Add result object schema and fix tests * Ensure example test runs * Remove data.yaml files from mock policies * Don't run example - TUF policy isn't compatible * Add attestation to manifests for all subjects * Ensure adding attestation doesn't touch statements * Don't export sign function * Remove attestations from VerificationResult * Change bool to Outcome enum in result * Use outputLayout directly * Make clearer that Outcome strings are for VSA * Return multiple SLSA levels from policy * Fix unmarshalling of policy-id (#39) * Rename function * Rename policy.VerificationResult -> policy.Result * Re-add test for canonical input --------- Co-authored-by: James Carnegie <james.carnegie@docker.com> Co-authored-by: James Carnegie <kipz@users.noreply.github.com>
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/attest/pkg/oci"
|
||||
"github.com/open-policy-agent/opa/rego"
|
||||
)
|
||||
|
||||
type policyEvaluatorCtxKeyType struct{}
|
||||
@@ -27,5 +26,5 @@ func GetPolicyEvaluator(ctx context.Context) (PolicyEvaluator, error) {
|
||||
}
|
||||
|
||||
type PolicyEvaluator interface {
|
||||
Evaluate(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) (*rego.ResultSet, error)
|
||||
Evaluate(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *PolicyInput) (*Result, error)
|
||||
}
|
||||
|
||||
@@ -4,37 +4,29 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/attest/pkg/oci"
|
||||
"github.com/open-policy-agent/opa/rego"
|
||||
)
|
||||
|
||||
type MockPolicyEvaluator struct {
|
||||
EvaluateFunc func(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) (*rego.ResultSet, error)
|
||||
EvaluateFunc func(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *PolicyInput) (*Result, error)
|
||||
}
|
||||
|
||||
func (pe *MockPolicyEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) (*rego.ResultSet, error) {
|
||||
func (pe *MockPolicyEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *PolicyInput) (*Result, error) {
|
||||
if pe.EvaluateFunc != nil {
|
||||
return pe.EvaluateFunc(ctx, resolver, policy, input)
|
||||
return pe.EvaluateFunc(ctx, resolver, pctx, input)
|
||||
}
|
||||
return AllowedResult(), nil
|
||||
}
|
||||
|
||||
func GetMockPolicy() PolicyEvaluator {
|
||||
return &MockPolicyEvaluator{
|
||||
EvaluateFunc: func(ctx context.Context, resolver oci.AttestationResolver, pfs []*PolicyFile, input *PolicyInput) (*rego.ResultSet, error) {
|
||||
EvaluateFunc: func(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *PolicyInput) (*Result, error) {
|
||||
return AllowedResult(), nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func AllowedResult() *rego.ResultSet {
|
||||
return ®o.ResultSet{
|
||||
{
|
||||
Bindings: rego.Vars{},
|
||||
Expressions: []*rego.ExpressionValue{
|
||||
{
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
func AllowedResult() *Result {
|
||||
return &Result{
|
||||
Success: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/attest/pkg/oci"
|
||||
"github.com/docker/attest/pkg/tuf"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
|
||||
goyaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
@@ -20,6 +21,26 @@ const (
|
||||
PolicyMappingFileName = "mapping.yaml"
|
||||
)
|
||||
|
||||
type Summary struct {
|
||||
Subjects []intoto.Subject `json:"subjects"`
|
||||
SLSALevels []string `json:"slsa_levels"`
|
||||
Verifier string `json:"verifier"`
|
||||
PolicyURI string `json:"policy_uri"`
|
||||
}
|
||||
|
||||
type Violation struct {
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Attestation *intoto.Statement `json:"attestation"`
|
||||
Details map[string]any `json:"details"`
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Success bool `json:"success"`
|
||||
Violations []Violation `json:"violations"`
|
||||
Summary Summary `json:"summary"`
|
||||
}
|
||||
|
||||
type PolicyMappings struct {
|
||||
Version string `json:"version"`
|
||||
Kind string `json:"kind"`
|
||||
@@ -39,7 +60,7 @@ type PolicyMappingFile struct {
|
||||
}
|
||||
|
||||
type PolicyMirror struct {
|
||||
PolicyId string `json:"policy-id"`
|
||||
PolicyId string `yaml:"policy-id"`
|
||||
Mirror MirrorSpec `json:"mirror"`
|
||||
}
|
||||
|
||||
@@ -60,6 +81,11 @@ type PolicyOptions struct {
|
||||
LocalPolicyDir string
|
||||
}
|
||||
|
||||
type Policy struct {
|
||||
InputFiles []*PolicyFile
|
||||
Query string
|
||||
}
|
||||
|
||||
type PolicyInput struct {
|
||||
Digest string `json:"digest"`
|
||||
Purl string `json:"purl"`
|
||||
@@ -71,7 +97,7 @@ type PolicyFile struct {
|
||||
Content []byte
|
||||
}
|
||||
|
||||
func resolveLocalPolicy(opts *PolicyOptions, mapping *PolicyMapping) ([]*PolicyFile, error) {
|
||||
func resolveLocalPolicy(opts *PolicyOptions, mapping *PolicyMapping) (*Policy, error) {
|
||||
if opts.LocalPolicyDir == "" {
|
||||
return nil, fmt.Errorf("local policy dir not set")
|
||||
}
|
||||
@@ -88,10 +114,13 @@ func resolveLocalPolicy(opts *PolicyOptions, mapping *PolicyMapping) ([]*PolicyF
|
||||
Content: fileContents,
|
||||
})
|
||||
}
|
||||
return files, nil
|
||||
policy := &Policy{
|
||||
InputFiles: files,
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func loadLocalMappings(opts *PolicyOptions) (*PolicyMappings, error) {
|
||||
func LoadLocalMappings(opts *PolicyOptions) (*PolicyMappings, error) {
|
||||
if opts.LocalPolicyDir == "" {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -108,7 +137,7 @@ func loadLocalMappings(opts *PolicyOptions) (*PolicyMappings, error) {
|
||||
return mappings, nil
|
||||
}
|
||||
|
||||
func resolveTufPolicy(opts *PolicyOptions, mapping *PolicyMapping) ([]*PolicyFile, error) {
|
||||
func resolveTufPolicy(opts *PolicyOptions, mapping *PolicyMapping) (*Policy, error) {
|
||||
files := make([]*PolicyFile, 0, len(mapping.Files))
|
||||
for _, f := range mapping.Files {
|
||||
filename := f.Path
|
||||
@@ -121,7 +150,10 @@ func resolveTufPolicy(opts *PolicyOptions, mapping *PolicyMapping) ([]*PolicyFil
|
||||
Content: fileContents,
|
||||
})
|
||||
}
|
||||
return files, nil
|
||||
policy := &Policy{
|
||||
InputFiles: files,
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func loadTufMappings(tufClient tuf.TUFClient, localTargetsDir string) (*PolicyMappings, error) {
|
||||
@@ -163,7 +195,7 @@ func findPolicyMatch(named reference.Named, mappings *PolicyMappings) (*PolicyMa
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func ResolvePolicy(ctx context.Context, resolver oci.AttestationResolver, opts *PolicyOptions) ([]*PolicyFile, error) {
|
||||
func ResolvePolicy(ctx context.Context, resolver oci.AttestationResolver, opts *PolicyOptions) (*Policy, error) {
|
||||
imageName, err := resolver.ImageName(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get image name: %w", err)
|
||||
@@ -172,7 +204,7 @@ func ResolvePolicy(ctx context.Context, resolver oci.AttestationResolver, opts *
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse image name: %w", err)
|
||||
}
|
||||
localMappings, err := loadLocalMappings(opts)
|
||||
localMappings, err := LoadLocalMappings(opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load local policy mappings: %w", err)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/docker/attest/pkg/policy"
|
||||
"github.com/docker/attest/pkg/tuf"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func loadAttestation(t *testing.T, path string) *attestation.Envelope {
|
||||
@@ -32,61 +33,37 @@ 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
|
||||
isCanonical bool
|
||||
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?digest=sha256%da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620&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},
|
||||
{repo: "testdata/mock-tuf-allow", expectSuccess: true, isCanonical: false, resolver: defaultResolver},
|
||||
{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},
|
||||
{repo: "testdata/mock-tuf-allow-canonical", expectSuccess: true, isCanonical: true, resolver: defaultResolver},
|
||||
{repo: "testdata/mock-tuf-allow-canonical", expectSuccess: false, isCanonical: false, resolver: defaultResolver},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.repo, func(t *testing.T) {
|
||||
input := &policy.PolicyInput{
|
||||
Digest: "sha256:test-digest",
|
||||
Purl: "test-purl",
|
||||
IsCanonical: tc.isCanonical,
|
||||
}
|
||||
|
||||
tufClient := tuf.NewMockTufClient(tc.repo, test.CreateTempDir(t, "", "tuf-dest"))
|
||||
if tc.policy == nil {
|
||||
tc.policy = &policy.PolicyOptions{
|
||||
@@ -95,17 +72,29 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
policyFiles, err := policy.ResolvePolicy(ctx, tc.resolver, tc.policy)
|
||||
policy, err := policy.ResolvePolicy(ctx, tc.resolver, tc.policy)
|
||||
assert.NoErrorf(t, err, "failed to resolve policy")
|
||||
rs, err := re.Evaluate(ctx, tc.resolver, policyFiles, tc.input)
|
||||
result, err := re.Evaluate(ctx, tc.resolver, policy, input)
|
||||
require.NoErrorf(t, err, "Evaluate failed")
|
||||
|
||||
if tc.expectSuccess {
|
||||
assert.NoErrorf(t, err, "Evaluate failed")
|
||||
assert.True(t, rs.Allowed(), "Evaluate should have succeeded")
|
||||
assert.True(t, result.Success, "Evaluate should have succeeded")
|
||||
} else {
|
||||
assert.False(t, rs.Allowed(), "Evaluate should have failed")
|
||||
assert.False(t, result.Success, "Evaluate should have failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestLoadingMappings(t *testing.T) {
|
||||
opts := &policy.PolicyOptions{
|
||||
LocalPolicyDir: filepath.Join("testdata", "mock-tuf-allow"),
|
||||
}
|
||||
policyMappings, err := policy.LoadLocalMappings(opts)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(policyMappings.Mirrors), 1)
|
||||
for _, mirror := range policyMappings.Mirrors {
|
||||
assert.Equal(t, "docker-official-images", mirror.PolicyId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,17 +23,20 @@ import (
|
||||
|
||||
type regoEvaluator struct {
|
||||
debug bool
|
||||
query string
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultQuery = "result := data.attest.result"
|
||||
resultBinding = "result"
|
||||
)
|
||||
|
||||
func NewRegoEvaluator(debug bool) PolicyEvaluator {
|
||||
return ®oEvaluator{
|
||||
debug: debug,
|
||||
query: "data.attest.allow",
|
||||
}
|
||||
}
|
||||
|
||||
func (re *regoEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, files []*PolicyFile, input *PolicyInput) (*rego.ResultSet, error) {
|
||||
func (re *regoEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *PolicyInput) (*Result, error) {
|
||||
var regoOpts []func(*rego.Rego)
|
||||
|
||||
// Create a new in-memory store
|
||||
@@ -45,7 +48,7 @@ func (re *regoEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationR
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, target := range files {
|
||||
for _, target := range pctx.InputFiles {
|
||||
// load yaml as data (no rego opt for this!?)
|
||||
if filepath.Ext(target.Path) == ".yaml" {
|
||||
yamlData, err := loadYAML(target.Path, target.Content)
|
||||
@@ -74,12 +77,15 @@ func (re *regoEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationR
|
||||
rego.Dump(os.Stderr),
|
||||
)
|
||||
}
|
||||
|
||||
query := DefaultQuery
|
||||
if pctx.Query != "" {
|
||||
query = pctx.Query
|
||||
}
|
||||
regoOpts = append(regoOpts,
|
||||
rego.Query(re.query),
|
||||
rego.StrictBuiltinErrors(true),
|
||||
rego.Query(query),
|
||||
rego.Input(input),
|
||||
rego.Store(store),
|
||||
rego.GenerateJSON(jsonGenerator[Result]()),
|
||||
)
|
||||
for _, custom := range RegoFunctions(resolver) {
|
||||
regoOpts = append(regoOpts, custom.Func)
|
||||
@@ -87,11 +93,50 @@ func (re *regoEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationR
|
||||
|
||||
r := rego.New(regoOpts...)
|
||||
rs, err := r.Eval(ctx)
|
||||
return &rs, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(rs) == 0 {
|
||||
return nil, fmt.Errorf("no policy evaluation result")
|
||||
}
|
||||
binding, ok := rs[0].Bindings[resultBinding]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to extract verification result")
|
||||
}
|
||||
result, ok := binding.(Result)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to extract verification result")
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func jsonGenerator[T any]() func(t *ast.Term, ec *rego.EvalContext) (any, error) {
|
||||
return func(t *ast.Term, ec *rego.EvalContext) (any, error) {
|
||||
// TODO: this is horrible - we're converting the AST to JSON and then back to AST, then using ast.As to convert it to a struct
|
||||
// We can't use ast.As directly because it fails if the AST contains a set
|
||||
json, err := ast.JSON(t.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := ast.InterfaceToValue(json)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result T
|
||||
err = ast.As(v, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
var dynamicObj = types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))
|
||||
var arrayObj = types.NewArray(nil, dynamicObj)
|
||||
var setObj = types.NewSet(dynamicObj)
|
||||
|
||||
var verifyDecl = &ast.Builtin{
|
||||
Name: "attestations.verify_envelope",
|
||||
Decl: types.NewFunction(types.Args(dynamicObj, arrayObj), dynamicObj),
|
||||
@@ -99,7 +144,7 @@ var verifyDecl = &ast.Builtin{
|
||||
}
|
||||
var attestDecl = &ast.Builtin{
|
||||
Name: "attestations.attestation",
|
||||
Decl: types.NewFunction(types.Args(types.S), dynamicObj),
|
||||
Decl: types.NewFunction(types.Args(types.S), setObj),
|
||||
Nondeterministic: true,
|
||||
}
|
||||
|
||||
@@ -153,12 +198,13 @@ func fetchIntotoAttestations(resolver oci.AttestationResolver) func(rego.Builtin
|
||||
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...))
|
||||
// Wrap the values in an ast.Set and convert it to an ast.Term.
|
||||
set := ast.NewTerm(ast.NewSet(values...))
|
||||
|
||||
return array, nil
|
||||
return set, nil
|
||||
}
|
||||
}
|
||||
|
||||
func verifyIntotoEnvelope(rCtx rego.BuiltinContext, envTerm, keysTerm *ast.Term) (*ast.Term, error) {
|
||||
env := new(att.Envelope)
|
||||
var keys att.Keys
|
||||
|
||||
7
pkg/policy/testdata/mock-tuf-allow-canonical/doi/policy.rego
vendored
Normal file
7
pkg/policy/testdata/mock-tuf-allow-canonical/doi/policy.rego
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package attest
|
||||
|
||||
import rego.v1
|
||||
|
||||
result := {
|
||||
"success": input.isCanonical,
|
||||
}
|
||||
16
pkg/policy/testdata/mock-tuf-allow-canonical/mapping.yaml
vendored
Normal file
16
pkg/policy/testdata/mock-tuf-allow-canonical/mapping.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# map repos to policies
|
||||
version: v1
|
||||
kind: policy-mapping
|
||||
policies:
|
||||
- origin:
|
||||
domain: docker.io
|
||||
prefix: library/
|
||||
id: docker-official-images
|
||||
description: Docker Official Images
|
||||
files:
|
||||
- path: doi/policy.rego
|
||||
mirrors:
|
||||
- policy-id: docker-official-images
|
||||
mirror:
|
||||
domains: [localhost:5001, registry.local:5000]
|
||||
prefix: ""
|
||||
@@ -2,4 +2,6 @@ package attest
|
||||
|
||||
import rego.v1
|
||||
|
||||
allow := true
|
||||
result := {
|
||||
"success": true,
|
||||
}
|
||||
|
||||
@@ -9,3 +9,8 @@ policies:
|
||||
description: Docker Official Images
|
||||
files:
|
||||
- path: doi/policy.rego
|
||||
mirrors:
|
||||
- policy-id: docker-official-images
|
||||
mirror:
|
||||
domains: [localhost:5001, registry.local:5000]
|
||||
prefix: ""
|
||||
|
||||
@@ -2,4 +2,6 @@ package attest
|
||||
|
||||
import rego.v1
|
||||
|
||||
allow := false
|
||||
result := {
|
||||
"success": false,
|
||||
}
|
||||
|
||||
@@ -3,13 +3,17 @@ 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
|
||||
"id": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4",
|
||||
"key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgH23D1i2+ZIOtVjmfB7iFvX8AhVN\n9CPJ4ie9axw+WRHozGnRy99U2dRge3zueBBg2MweF0zrToXGig2v3YOrdw==\n-----END PUBLIC KEY-----",
|
||||
"from": "2023-12-15T14:00:00Z",
|
||||
"to": null
|
||||
}]
|
||||
|
||||
allow if {
|
||||
success if {
|
||||
some env in attestations.attestation("foo")
|
||||
statement := attestations.verify_envelope(env, keys)
|
||||
}
|
||||
|
||||
result := {
|
||||
"success": success
|
||||
}
|
||||
|
||||
@@ -2,18 +2,20 @@ package attest
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
keys := [{
|
||||
"id": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4",
|
||||
"key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHyZpSgzvqFqNv7f3x7865OS38rAb\nQMcff55zM2UH/KR3Pr84a8QsGDNgaNGzJQJWjtMSgfV8WnNoffNK+svFNg==\n-----END PUBLIC KEY-----",
|
||||
"from": "2023-12-15T14:00:00Z",
|
||||
"to": null,
|
||||
}]
|
||||
|
||||
allow if {
|
||||
default success := false
|
||||
|
||||
success if {
|
||||
some env in attestations.attestation("foo")
|
||||
statement := attestations.verify_envelope(env, keys)
|
||||
}
|
||||
|
||||
allow := true
|
||||
result := {
|
||||
"success": success
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user