opa: PrepareForEval error when using partial evaluation: "rego_unsafe_var_error: expression is unsafe"
Short description
With OPA go library versions v0.39.0
and v0.41.0
, when we use the every
keyword we’re seeing an unexpected error from PrepareForEval
, but only when we use WithPartialEval
:
1 error occurred: policy.rego:8: rego_unsafe_var_error: expression is unsafe
As far as we knew this error never came up when we were evaluating the rego.Rego
object directly. It started happening when we moved over to using PrepareForEval
.
For reproduction steps, policies, and example go code that reproduces the problem, see below.
Note that it seems to have something to do with the structure of modules/packages that we use–if I just put everything in the same file I can’t seem to reproduce the problem.
Code
main.go
package main
import (
"context"
_ "embed"
"fmt"
"github.com/open-policy-agent/opa/rego"
)
//go:embed iam.rego
var iamModule string
//go:embed internal.rego
var internalModule string
//go:embed policy.rego
var policyModule string
func main() {
ctx := context.Background()
modules := []func(r *rego.Rego){
rego.Module("iam.rego", iamModule),
rego.Module("internal.rego", internalModule),
rego.Module("policy.rego", policyModule),
rego.Query("data.iam.internal.check_access"),
}
input := rego.EvalInput(map[string]interface{}{})
r := rego.New(modules...)
q, err := r.PrepareForEval(ctx)
if err != nil {
fmt.Printf("PrepareForEval without partial: %s\n", err.Error())
return
}
_, err = q.Eval(ctx, input)
if err != nil {
fmt.Printf("Eval without partial: %s\n", err.Error())
return
}
q, err = r.PrepareForEval(ctx, rego.WithPartialEval())
if err != nil {
fmt.Printf("PrepareForEval with partial: %s\n", err.Error())
return
}
_, err = q.Eval(ctx, input)
if err != nil {
fmt.Printf("Eval with partial: %s\n", err.Error())
return
}
}
go.mod
module bug
go 1.18
require github.com/open-policy-agent/opa v0.41.0
require (
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/agnivade/levenshtein v1.0.1 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
github.com/vektah/gqlparser/v2 v2.4.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.1.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Policies
iam.rego
package iam
value_missing(role, resource, key) {
value := object.get(role, key, "")
value == ""
}
internal.rego
package iam.internal
check_access[identifier] {
data.resourceID.policyID.allow(input.subject.roles[_], input.resource.resources[resource_idx])
identifier := input.resource.resources[resource_idx].identifier
}
policy.rego
package resourceID.policyID
import future.keywords.every
foo(outer_role, resource, key) {
every role in outer_role {
# PrepareForEval error: policy.rego:8: rego_unsafe_var_error: expression is unsafe
not data.iam.value_missing(role, resource, key)
}
}
allow(role, resource) {
foo(role, resource, "bar")
}
Steps To Reproduce
- Create
main.go
,go.mod
, and all.rego
files as named and with the above contents - Run main.go:
go run .
- Observe error from
PrepareForEval()
but only underWithPartialEval()
:
$ go run .
PrepareForEval with partial: 1 error occurred: policy.rego:8: rego_unsafe_var_error: expression is unsafe
Expected behavior
We would expect that PrepareForEval()
completes without error using WithPartialEval()
, i.e. the above script runs without producing any output.
Additional context
We’ve successfully worked around this issue by avoiding the use of the every
keyword and instead using the “not-some-not” pattern mentioned in the docs, which results in Rego policies that do what we need them to do but are harder to read.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 18 (12 by maintainers)
Commits related to this issue
- ast/compile: reorder body for safety differently Our previous approach, ordering for closures first, and taking the growing set of output variables of the reordered body into account in a second step... — committed to srenatus/opa by srenatus 2 years ago
- ast/compile: reorder body for safety differently Our previous approach, ordering for closures first, and taking the growing set of output variables of the reordered body into account in a second step... — committed to srenatus/opa by srenatus 2 years ago
- ast/compile: reorder body for safety differently (#4801) Our previous approach, ordering for closures first, and taking the growing set of output variables of the reordered body into account in a se... — committed to open-policy-agent/opa by srenatus 2 years ago
@srenatus whoops my bad, just checked and the fix from
sr/issue-4766
does indeed fix our actual usage ofevery
where we originally saw this problemmany thanks !
@srenatus it does fix the error in the main.go above but unfortunately it doesn’t fix all instances of “unsafe expression” we’re seeing from our actual policies. There’s 2 places we had been using
every
and the other one must be different in some way 🤔I will see if I can reproduce the same situation in main.go again here, thank you