opa icon indicating copy to clipboard operation
opa copied to clipboard

Add new built-in function to query for metadata at provided ref

Open anderseknert opened this issue 2 years ago • 3 comments

Metadata annotations provides many benefits for separating a policy project's metadata from its policy logic. This separation however comes with a cost, as we'll need to duplicate parts of metadata into data accessible in Rego. We need to do this since the capabilities to query metadata from inside of Rego are limited to metadata pertaining to the rule it's queried from (including the chain from which it inherits metadata). Having a built-in function that's able to query for metadata at any location, sort of like object.get but for metadata, would unlock a whole bunch of interesting things, like:

  • Dynamic policy composition, where metadata determines the rules to evaluate.
  • Including metadata attributes in returned decisions, like the common "reasons" seen in responses.
  • Using metadata like severity levels as part of policy decisions.
  • "Introspection" capabilities to e.g. generate docs or other things from metadata directly from Rego.

anderseknert avatar Oct 11 '23 09:10 anderseknert

@anderseknert technically you could use rego.metadata.chain() which was a little difficult to find but is documented here. This shows the path ancestry including paths and all annotations. Here is a playground example of how you could use it. Not the most robust solution, but you could add some field validation as well. Great for Dynamic policy composition.

aalsabag avatar Feb 07 '24 23:02 aalsabag

Thanks @aalsabag! The rego.metadata.chain function however works only to obtain the chain of metadata for the rule from which it is called. What's being requested here is getting the metadata from another rule or package. Simple example:

package p

foo := rego.metadata.get(["data", "p", "bar"])

# METADATA
# description: this is bar
bar {
    # ...
}

anderseknert avatar Feb 08 '24 19:02 anderseknert

Yup! Which is why I too would love this feature. Thanks for raising the issue @anderseknert . So the way I'm getting around that now is by creating a function in each policy file that returns the metadata. I even have a helper/util function to extract what I need. Not the prettiest of solutions, but it's gotten the job done for now and is going to be easy to refactor once this feature comes in. Util Function:

package utils.metadata

policy_metadata(chain) := {
	"policy": chain[1].annotations.title,
	"version": chain[1].annotations.custom.version,
}

Call in each policy:

package policies.abc
import data.utils.metadata

policy_metadata contains msg if {
	msg := metadata.policy_metadata(rego.metadata.chain())
}

aalsabag avatar Feb 08 '24 20:02 aalsabag