Handle errors from Go in Rego. Support for skipping TL (#47)

* Make TL logging/verification optional

* Return errors from go-lang fns

* Update pkg/policy/rego.go

Co-authored-by: Jonny Stoten <jonny@jonnystoten.com>

* Update pkg/attestation/sign.go

Co-authored-by: Joel Kamp <joel.kamp@docker.com>

* Move public key marshelling until later

* Simplify logSignature and pass down opts

---------

Co-authored-by: Jonny Stoten <jonny@jonnystoten.com>
Co-authored-by: Joel Kamp <joel.kamp@docker.com>
This commit is contained in:
James Carnegie
2024-06-06 09:59:32 +01:00
committed by GitHub
parent 3b5c506739
commit 4be882aeb0
17 changed files with 337 additions and 138 deletions

View File

@@ -133,21 +133,42 @@ func jsonGenerator[T any]() func(t *ast.Term, ec *rego.EvalContext) (any, error)
}
}
var dynamicObj = types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))
var arrayObj = types.NewArray(nil, dynamicObj)
var setObj = types.NewSet(dynamicObj)
var dynamicObj = types.NewObject(nil, types.NewDynamicProperty(types.A, types.A))
var verifyDecl = &ast.Builtin{
Name: "attestations.verify_envelope",
Decl: types.NewFunction(types.Args(dynamicObj, arrayObj), dynamicObj),
Name: "attest.verify",
Decl: types.NewFunction(types.Args(dynamicObj, dynamicObj), dynamicObj),
Nondeterministic: true,
}
var attestDecl = &ast.Builtin{
Name: "attestations.attestation",
Decl: types.NewFunction(types.Args(types.S), setObj),
Name: "attest.fetch",
Decl: types.NewFunction(types.Args(types.S), dynamicObj),
Nondeterministic: true,
}
func wrapFunctionResult(value *ast.Term, err error) (*ast.Term, error) {
var terms [][2]*ast.Term
if err != nil {
terms = append(terms, [2]*ast.Term{ast.StringTerm("error"), ast.StringTerm(err.Error())})
}
if value != nil {
terms = append(terms, [2]*ast.Term{ast.StringTerm("value"), value})
}
return ast.ObjectTerm(terms...), nil
}
func handleErrors1(f func(rCtx rego.BuiltinContext, a *ast.Term) (*ast.Term, error)) rego.Builtin1 {
return func(rCtx rego.BuiltinContext, a *ast.Term) (*ast.Term, error) {
return wrapFunctionResult(f(rCtx, a))
}
}
func handleErrors2(f func(rCtx rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error)) rego.Builtin2 {
return func(rCtx rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error) {
return wrapFunctionResult(f(rCtx, a, b))
}
}
func RegoFunctions(resolver oci.AttestationResolver) []*tester.Builtin {
return []*tester.Builtin{
{
@@ -159,7 +180,7 @@ func RegoFunctions(resolver oci.AttestationResolver) []*tester.Builtin {
Memoize: true,
Nondeterministic: verifyDecl.Nondeterministic,
},
verifyIntotoEnvelope),
handleErrors2(verifyIntotoEnvelope)),
},
{
Decl: attestDecl,
@@ -170,12 +191,12 @@ func RegoFunctions(resolver oci.AttestationResolver) []*tester.Builtin {
Memoize: true,
Nondeterministic: attestDecl.Nondeterministic,
},
fetchIntotoAttestations(resolver)),
handleErrors1(fetchIntotoAttestations(resolver))),
},
}
}
func fetchIntotoAttestations(resolver oci.AttestationResolver) func(rego.BuiltinContext, *ast.Term) (*ast.Term, error) {
func fetchIntotoAttestations(resolver oci.AttestationResolver) rego.Builtin1 {
return func(rCtx rego.BuiltinContext, predicateTypeTerm *ast.Term) (*ast.Term, error) {
predicateTypeStr, ok := predicateTypeTerm.Value.(ast.String)
if !ok {
@@ -205,22 +226,19 @@ func fetchIntotoAttestations(resolver oci.AttestationResolver) func(rego.Builtin
}
}
func verifyIntotoEnvelope(rCtx rego.BuiltinContext, envTerm, keysTerm *ast.Term) (*ast.Term, error) {
func verifyIntotoEnvelope(rCtx rego.BuiltinContext, envTerm, optsTerm *ast.Term) (*ast.Term, error) {
env := new(att.Envelope)
var keys att.Keys
opts := new(att.VerifyOptions)
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)
err = ast.As(optsTerm.Value, &opts)
if err != nil {
return nil, fmt.Errorf("failed to cast keys: %w", err)
return nil, fmt.Errorf("failed to cast verifier options: %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)
payload, err := att.VerifyDSSE(rCtx.Context, env, opts)
if err != nil {
return nil, err
}
@@ -242,7 +260,6 @@ func verifyIntotoEnvelope(rCtx rego.BuiltinContext, envTerm, keysTerm *ast.Term)
if err != nil {
return nil, err
}
return ast.NewTerm(value), nil
}

View File

@@ -3,17 +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,
}]
opts := {"keys": keys}
success if {
some env in attestations.attestation("foo")
statement := attestations.verify_envelope(env, keys)
some env in attest.fetch("foo")
statement := attest.verify(env, opts)
}
result := {
"success": success
}
result := {"success": success}

View File

@@ -3,19 +3,28 @@ package attest
import rego.v1
keys := [{
"id": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4",
"key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHyZpSgzvqFqNv7f3x7865OS38rAb\nQMcff55zM2UH/KR3Pr84a8QsGDNgaNGzJQJWjtMSgfV8WnNoffNK+svFNg==\n-----END PUBLIC KEY-----",
"from": "2023-12-15T14:00:00Z",
"to": null,
"id": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4",
"key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHyZpSgzvqFqNv7f3x7865OS38rAb\nQMcff55zM2UH/KR3Pr84a8QsGDNgaNGzJQJWjtMSgfV8WnNoffNK+svFNg==\n-----END PUBLIC KEY-----",
"from": "2023-12-15T14:00:00Z",
"to": null,
}]
default success := false
success if {
some env in attestations.attestation("foo")
statement := attestations.verify_envelope(env, keys)
provs(pred) := p if {
res := attest.fetch(pred)
not res.error
p := res.value
}
result := {
"success": success
atts := union({provs("foo")})
opts := {"keys": keys}
success if {
some env in atts
res := attest.verify(env, opts)
not res.error
}
result := {"success": success}