open-policy-agent/opa

Packages inside the dynamic extent of a rule's ref are rejected at compile-time

johanfylling opened this issue · 0 comments

The general case

The following modules

Module A:

package overlap

p[x] := 1 {
    x := "foo"
}

Module B:

package overlap.p

bar := 2

will generate the compile-time error:

1 error occurred: a.rego:1: rego_type_error: package overlap.p conflicts with rule p[__local0__] defined at a.rego:3

However, module A and B can be expressed as a single module:

package overlap

p[x] := 1 {
    x := "foo"
}

p.bar := 2

which is valid rego, and will not be rejected by the compiler.

Side note: the error reports the bad package to be at a.rego:1, but it's actually at b.rego:1

The optimized build case

The above scenario can unintentionally be triggered through optimized compilation, e.g. when building an optimized bundle.

If building the policy:

package overlap

p[x] := 1 {
    x := input.x
}

p.bar := input.y

main {
    p[input.x]
}

with opa build --optimize 1 .

a bundle with the following content will be produced:

optimized/overlap.rego:

package overlap

main {
	__local2__1 = input.x
	data.overlap.p[__local2__1] = _term_1_11
	_term_1_11
}

p[__local0__3] = 1 {
	__local0__3 = input.x
}

optimized/overlap/p.rego:

package overlap.p

bar = __local1__2 {
	__local1__2 = input.y
}

which will produce the following error when evaluated with opa eval -d bundle.tar.gz -fpretty 'data':

1 error occurred: /optimized/overlap.rego:1: rego_type_error: package overlap.p conflicts with rule p[__local0__3] defined at /optimized/overlap.rego:9

Suggested fix

Allow packages to be declared within the dynamic extent of a rule's ref. The document defined at the package path by the module might pose a conflict at eval-time, but cannot be determined to do so at compile-time.

Example 1: package extends partial object rule (valid)

a.rego:

package overlap
p[x] := 1 { x := input.list[_] }

b.rego:

package overlap.p
q := 2

Example 2: package redeclares value of single-value rule (invalid)

a.rego:

package overlap
p := 1

b.rego:

package overlap.p
q := 2

Example 3: package redeclares value of multi-value rule (invalid)

a.rego:

package overlap
p[x] { x := input.list[_] }

b.rego:

package overlap.p
q := 2