Rego is the declarative policy language used by Open Policy Agent (OPA). This tile covers writing and testing Rego policies for Kubernetes admission control, Terraform and infrastructure-as-code plan validation, Docker container authorization, HTTP API authorization, RBAC and role-based access control, data filtering, metadata annotations with opa inspect, and OPA policy testing with opa test.
96
94%
Does it follow best practices?
Impact
97%
1.18xAverage score across 32 eval scenarios
Low
Low-risk findings worth noting
Request body validation in Rego uses set subtraction to detect unknown fields and explicit iteration to detect missing required fields. This pattern efficiently validates that a POST body contains only allowed fields and all required fields are present.
To detect unknown fields, compute the set of submitted field names from input.body using a set comprehension, then subtract the allowed set. If the result is non-empty (!= set()), unknown fields are present.
body_fields := {field | input.body[field]}
body_fields - allowed_fields != set()This is more concise and efficient than iterating field-by-field — a single subtraction operation finds all unknown fields at once.
# METADATA
# title: Request Body Validation
# description: Validates request body structure — rejects unknown fields and enforces required fields
# authors:
# - API Security Team <api-security@example.com>
# custom:
# category: http-authorization
package httpapi.authz
import rego.v1
default allow := false
allowed_fields := {"username", "email", "display_name"}
required_fields := {"username", "email"}
# METADATA
# title: Allow valid POST requests
# description: Permits POST /api/users when the body has no unknown fields and all required fields are present
# entrypoint: true
# custom:
# severity: MEDIUM
allow if {
input.method == "POST"
input.path == "/api/users"
count(deny) == 0
}
# Reject unknown fields: compute submitted field names and use set subtraction
deny contains msg if {
input.method == "POST"
input.path == "/api/users"
body_fields := {field | input.body[field]}
body_fields - allowed_fields != set()
msg := sprintf("unknown fields in request body: %v", [body_fields - allowed_fields])
}
# Reject missing required fields
deny contains msg if {
input.method == "POST"
input.path == "/api/users"
some field in required_fields
not input.body[field]
msg := sprintf("required field missing from request body: %v", [field])
}Example Input (unknown field admin present):
{
"method": "POST",
"path": "/api/users",
"body": {
"username": "alice",
"email": "alice@example.com",
"admin": true
}
}Result: allow == false — body_fields is {"username", "email", "admin"}, so body_fields - allowed_fields is {"admin"} which is not set(), and the deny rule fires.
Example Input (valid body):
{
"method": "POST",
"path": "/api/users",
"body": {
"username": "alice",
"email": "alice@example.com"
}
}Result: allow == true — body_fields - allowed_fields is set() and both required fields are present.
Per the Regal file-missing-test-suffix rule, test files must use a _test.rego suffix. Because the test is in a separate _test package, import the policy and reference rules via the alias.
# authz_test.rego
package httpapi.authz_test
import rego.v1
import data.httpapi.authz # import the policy package under test
# Allow: only allowed fields, all required fields present
test_valid_body_allowed if {
authz.allow with input as {
"method": "POST",
"path": "/api/users",
"body": {"username": "alice", "email": "alice@example.com"}
}
}
# Deny: unknown field present — set subtraction detects it
test_unknown_field_denied if {
not authz.allow with input as {
"method": "POST",
"path": "/api/users",
"body": {"username": "alice", "email": "alice@example.com", "admin": true}
}
}
# Deny: required field missing
test_missing_required_field_denied if {
not authz.allow with input as {
"method": "POST",
"path": "/api/users",
"body": {"username": "alice"}
}
}.tessl-plugin
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10
scenario-11
scenario-12
scenario-13
scenario-14
scenario-15
scenario-16
scenario-17
scenario-18
scenario-19
scenario-20
scenario-21
scenario-22
scenario-23
scenario-24
scenario-25
scenario-26
scenario-27
scenario-28
scenario-29
scenario-30
scenario-31
scenario-32
skills
rego-domain-reference
references