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

  1. Create main.go, go.mod, and all .rego files as named and with the above contents
  2. Run main.go: go run .
  3. Observe error from PrepareForEval() but only under WithPartialEval():
$ 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

Most upvoted comments

@srenatus whoops my bad, just checked and the fix from sr/issue-4766 does indeed fix our actual usage of every where we originally saw this problem

many 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