refactor: remove explicit closures. expose rego fns (#170)
This commit is contained in:
147
policy/rego.go
147
policy/rego.go
@@ -88,10 +88,7 @@ func (re *regoEvaluator) Evaluate(ctx context.Context, resolver attestation.Reso
|
|||||||
rego.Store(store),
|
rego.Store(store),
|
||||||
rego.GenerateJSON(jsonGenerator[Result]()),
|
rego.GenerateJSON(jsonGenerator[Result]()),
|
||||||
)
|
)
|
||||||
regoFnOpts := ®oFnOpts{
|
regoFnOpts := NewRegoFunctionOptions(resolver, re.attestationVerifier)
|
||||||
attestationResolver: resolver,
|
|
||||||
attestationVerifier: re.attestationVerifier,
|
|
||||||
}
|
|
||||||
for _, custom := range RegoFunctions(regoFnOpts) {
|
for _, custom := range RegoFunctions(regoFnOpts) {
|
||||||
regoOpts = append(regoOpts, custom.Func)
|
regoOpts = append(regoOpts, custom.Func)
|
||||||
}
|
}
|
||||||
@@ -175,7 +172,7 @@ func handleErrors2(f func(rCtx rego.BuiltinContext, a, b *ast.Term) (*ast.Term,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegoFunctions(regoOpts *regoFnOpts) []*tester.Builtin {
|
func RegoFunctions(regoOpts *RegoFnOpts) []*tester.Builtin {
|
||||||
return []*tester.Builtin{
|
return []*tester.Builtin{
|
||||||
{
|
{
|
||||||
Decl: verifyDecl,
|
Decl: verifyDecl,
|
||||||
@@ -186,7 +183,7 @@ func RegoFunctions(regoOpts *regoFnOpts) []*tester.Builtin {
|
|||||||
Memoize: true,
|
Memoize: true,
|
||||||
Nondeterministic: verifyDecl.Nondeterministic,
|
Nondeterministic: verifyDecl.Nondeterministic,
|
||||||
},
|
},
|
||||||
handleErrors2(verifyInTotoEnvelope(regoOpts))),
|
handleErrors2(regoOpts.verifyInTotoEnvelope)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Decl: attestDecl,
|
Decl: attestDecl,
|
||||||
@@ -197,89 +194,97 @@ func RegoFunctions(regoOpts *regoFnOpts) []*tester.Builtin {
|
|||||||
Memoize: true,
|
Memoize: true,
|
||||||
Nondeterministic: attestDecl.Nondeterministic,
|
Nondeterministic: attestDecl.Nondeterministic,
|
||||||
},
|
},
|
||||||
handleErrors1(fetchInTotoAttestations(regoOpts))),
|
handleErrors1(regoOpts.fetchInTotoAttestations)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchInTotoAttestations(regoOpts *regoFnOpts) rego.Builtin1 {
|
// because we don't control the signature here (blame rego)
|
||||||
return func(rCtx rego.BuiltinContext, predicateTypeTerm *ast.Term) (*ast.Term, error) {
|
// nolint:gocritic
|
||||||
predicateTypeStr, ok := predicateTypeTerm.Value.(ast.String)
|
func (regoOpts *RegoFnOpts) fetchInTotoAttestations(rCtx rego.BuiltinContext, predicateTypeTerm *ast.Term) (*ast.Term, error) {
|
||||||
if !ok {
|
predicateTypeStr, ok := predicateTypeTerm.Value.(ast.String)
|
||||||
return nil, fmt.Errorf("predicateTypeTerm is not a string")
|
if !ok {
|
||||||
}
|
return nil, fmt.Errorf("predicateTypeTerm is not a string")
|
||||||
predicateType := string(predicateTypeStr)
|
}
|
||||||
|
predicateType := string(predicateTypeStr)
|
||||||
|
|
||||||
envelopes, err := regoOpts.attestationResolver.Attestations(rCtx.Context, predicateType)
|
envelopes, err := regoOpts.attestationResolver.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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
values[i] = ast.NewTerm(value)
|
||||||
// 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.Set and convert it to an ast.Term.
|
|
||||||
set := ast.NewTerm(ast.NewSet(values...))
|
|
||||||
|
|
||||||
return set, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap the values in an ast.Set and convert it to an ast.Term.
|
||||||
|
set := ast.NewTerm(ast.NewSet(values...))
|
||||||
|
|
||||||
|
return set, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type regoFnOpts struct {
|
type RegoFnOpts struct {
|
||||||
attestationResolver attestation.Resolver
|
attestationResolver attestation.Resolver
|
||||||
attestationVerifier attestation.Verifier
|
attestationVerifier attestation.Verifier
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyInTotoEnvelope(regoOpts *regoFnOpts) rego.Builtin2 {
|
// this is exported for testing here and in clients of the library.
|
||||||
return func(rCtx rego.BuiltinContext, envTerm, optsTerm *ast.Term) (*ast.Term, error) {
|
func NewRegoFunctionOptions(resolver attestation.Resolver, verifier attestation.Verifier) *RegoFnOpts {
|
||||||
env := new(attestation.Envelope)
|
return &RegoFnOpts{
|
||||||
opts := new(attestation.VerifyOptions)
|
attestationResolver: resolver,
|
||||||
err := ast.As(envTerm.Value, env)
|
attestationVerifier: verifier,
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to cast envelope: %w", err)
|
|
||||||
}
|
|
||||||
err = ast.As(optsTerm.Value, &opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to cast verifier options: %w", err)
|
|
||||||
}
|
|
||||||
payload, err := attestation.VerifyDSSE(rCtx.Context, regoOpts.attestationVerifier, env, opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to verify envelope: %w", 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = VerifySubject(rCtx.Context, statement.Subject, regoOpts.attestationResolver)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to verify subject: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
value, err := ast.InterfaceToValue(statement)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ast.NewTerm(value), nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// because we don't control the signature here (blame rego)
|
||||||
|
// nolint:gocritic
|
||||||
|
func (regoOpts *RegoFnOpts) verifyInTotoEnvelope(rCtx rego.BuiltinContext, envTerm, optsTerm *ast.Term) (*ast.Term, error) {
|
||||||
|
env := new(attestation.Envelope)
|
||||||
|
opts := new(attestation.VerifyOptions)
|
||||||
|
err := ast.As(envTerm.Value, env)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to cast envelope: %w", err)
|
||||||
|
}
|
||||||
|
err = ast.As(optsTerm.Value, &opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to cast verifier options: %w", err)
|
||||||
|
}
|
||||||
|
payload, err := attestation.VerifyDSSE(rCtx.Context, regoOpts.attestationVerifier, env, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to verify envelope: %w", 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = VerifySubject(rCtx.Context, statement.Subject, regoOpts.attestationResolver)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to verify subject: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := ast.InterfaceToValue(statement)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ast.NewTerm(value), nil
|
||||||
|
}
|
||||||
|
|
||||||
func loadYAML(path string, bs []byte) (interface{}, error) {
|
func loadYAML(path string, bs []byte) (interface{}, error) {
|
||||||
var x interface{}
|
var x interface{}
|
||||||
bs, err := yaml.YAMLToJSON(bs)
|
bs, err := yaml.YAMLToJSON(bs)
|
||||||
|
|||||||
Reference in New Issue
Block a user