Skip to main content

Rego

FunctionDescriptionMeta
rego.metadata.chain

chain := rego.metadata.chain()

Returns the chain of metadata for the active rule. Ordered starting at the active rule, going outward to the most distant node in its package ancestry. A chain entry is a JSON document with two members: "path", an array representing the path of the node; and "annotations", a JSON document containing the annotations declared for the node. The first entry in the chain always points to the active rule, even if it has no declared annotations (in which case the "annotations" member is not present).

Returns:
chain (array[any])

each array entry represents a node in the path ancestry (chain) of the active rule that also has declared annotations

v0.40.0 SDK-dependent
rego.metadata.rule

output := rego.metadata.rule()

Returns annotations declared for the active rule and using the rule scope.

Returns:
output (any)

"rule" scope annotations for this rule; empty object if no annotations exist

v0.40.0 SDK-dependent
rego.parse_module

output := rego.parse_module(filename, rego)

Parses the input Rego string and returns an object representation of the AST.

Arguments:
filename (string)

file name to attach to AST nodes' locations

rego (string)

Rego module

Returns:
output (object[string: any])

AST object for the Rego module

SDK-dependent

Example

Rule Metadata

The following policy will deny the given input because:

  • the number is greater than 5
  • the subject does not have the admin role
policy.rego
package example

# METADATA
# title: Deny invalid numbers
# description: Numbers may not be higher than 5
# custom:
# severity: MEDIUM
deny contains format(rego.metadata.rule()) if {
input.number > 5
}

# METADATA
# title: Deny non-admin subjects
# description: Subject must have the 'admin' role
# custom:
# severity: HIGH
deny contains format(rego.metadata.rule()) if {
input.subject.role != "admin"
}

format(meta) := {"severity": meta.custom.severity, "reason": meta.description}
input.json
{
"number": 11,
"subject": {
"name": "John doe",
"role": "customer"
}
}
data.json
"{}"

Metadata Merge strategies

When multiple annotations are declared along the path ancestry (chain) for a rule, how any given annotation should be selected, inherited or merged depends on the semantics of the annotation, the context of the rule, and the preferences of the developer. OPA doesn't presume what merge strategy is appropriate; instead, this lies in the hands of the developer. The following example demonstrates how some string and list type annotations in a metadata chain can be merged into a single metadata object.

# METADATA
# title: My Example Package
# description: A set of rules illustrating how metadata annotations can be merged.
# authors:
# - John Doe <john@example.com>
# organizations:
# - Acme Corp.
package example

# METADATA
# scope: document
# description: A rule that merges metadata annotations in various ways.

# METADATA
# title: My Allow Rule
# authors:
# - Jane Doe <jane@example.com>
allow if {
meta := merge(rego.metadata.chain())
meta.title == "My Allow Rule" # 'title' pulled from 'rule' scope
meta.description == "A rule that merges metadata annotations in various ways." # 'description' pulled from 'document' scope
meta.authors == {
{"email": "jane@example.com", "name": "Jane Doe"}, # 'authors' joined from 'package' and 'rule' scopes
{"email": "john@example.com", "name": "John Doe"},
}
meta.organizations == {"Acme Corp."} # 'organizations' pulled from 'package' scope
}

allow if {
meta := merge(rego.metadata.chain())
meta.title == null # No 'title' present in 'rule' or 'document' scopes
meta.description == "A rule that merges metadata annotations in various ways." # 'description' pulled from 'document' scope
meta.authors == { # 'authors' pulled from 'package' scope
{"email": "john@example.com", "name": "John Doe"}
}
meta.organizations == {"Acme Corp."} # 'organizations' pulled from 'package' scope
}

merge(chain) := meta if {
ruleAndDoc := ["rule", "document"]
meta := {
"title": override_annot(chain, "title", ruleAndDoc), # looks for 'title' in 'rule' scope, then 'document' scope
"description": override_annot(chain, "description", ruleAndDoc), # looks for 'description' in 'rule' scope, then 'document' scope
"related_resources": override_annot(chain, "related_resources", ruleAndDoc), # looks for 'related_resources' in 'rule' scope, then 'document' scope
"authors": merge_annot(chain, "authors"), # merges all 'authors' across all scopes
"organizations": merge_annot(chain, "organizations"), # merges all 'organizations' across all scopes
}
}

override_annot(chain, name, scopes) := val if {
val := [v |
link := chain[_]
link.annotations.scope in scopes
v := link.annotations[name]
][0]
} else := null

merge_annot(chain, name) := val if {
val := {v |
v := chain[_].annotations[name][_]
}
} else := null