Policy Authoring Guide¶
This guide explains how to write effective policies for Plan-Lint using Rego, the policy language used by the Open Policy Agent (OPA).
Introduction to Rego¶
Rego is a declarative query language designed for expressing policies. In Plan-Lint, we use Rego to define rules that agent plans must follow before execution is allowed.
Basic Rego Concepts¶
A Rego policy consists of:
- Package declaration: Defines the namespace for your policy
- Rules: Logical expressions that evaluate to true or false
- Functions: Reusable blocks of logic
- Data references: Ways to access the input document
Creating Your First Policy¶
Let's start with a basic policy that checks if a plan contains sensitive operations:
package plan_lint
# Define a rule that flags file deletion operations
violation[{"message": msg}] {
step := input.steps[_]
step.tool == "delete_file"
msg := sprintf("Plan attempts to delete a file: %v", [step.parameters.target_file])
}
# Flag large transactions
violation[{"message": msg}] {
step := input.steps[_]
step.tool == "execute_transaction"
to_number(step.parameters.amount) > 1000
msg := sprintf("Transaction amount exceeds limit: %v", [step.parameters.amount])
}
Input Structure¶
Plan-Lint passes the plan structure to your policies as the input document. A typical plan structure looks like:
{
"steps": [
{
"id": "step1",
"tool": "tool_name",
"parameters": {
"param1": "value1",
"param2": "value2"
},
"depends_on": ["step0"]
}
]
}
Common Policy Patterns¶
Checking Tool Usage¶
Restrict which tools can be used:
violation[{"message": msg}] {
step := input.steps[_]
forbidden_tools := {"execute_sql", "delete_file", "system_command"}
step.tool == forbidden_tools[_]
msg := sprintf("Forbidden tool usage: %v", [step.tool])
}
Parameter Validation¶
Check parameter values for potential issues:
violation[{"message": msg}] {
step := input.steps[_]
step.tool == "execute_sql"
contains(step.parameters.query, "DROP TABLE")
msg := "SQL query contains potentially dangerous DROP TABLE statement"
}
Dependency Validation¶
Ensure proper step dependencies:
violation[{"message": msg}] {
step := input.steps[_]
step.tool == "commit_transaction"
# Check that there's a step that begins the transaction
not has_begin_transaction
msg := "Transaction committed without being started"
}
has_begin_transaction {
some i
step := input.steps[i]
step.tool == "begin_transaction"
}
Testing Your Policies¶
You can test your policies in isolation using the OPA CLI:
# Save your policy to a file named policy.rego
# Create a test plan file plan.json
# Run OPA evaluation
opa eval -i plan.json -d policy.rego "data.plan_lint.violation"
Alternatively, use Plan-Lint's built-in policy testing:
from plan_lint import validate_plan
result = validate_plan(
plan_data,
policies=["path/to/your/policy.rego"]
)
print(f"Valid: {result.valid}")
for violation in result.violations:
print(f"Violation: {violation.message}")
Advanced Techniques¶
Using Context Information¶
Policies can use external context provided to the validator:
violation[{"message": msg}] {
# Access context variables
role := input.context.user_role
role != "admin"
# Check for privileged operations
step := input.steps[_]
privileged_tools := {"system_command", "execute_sql"}
step.tool == privileged_tools[_]
msg := sprintf("User with role '%v' cannot use tool '%v'", [role, step.tool])
}
Pattern Matching with Regex¶
Use regex for more flexible matching:
violation[{"message": msg}] {
step := input.steps[_]
step.tool == "web_request"
url := step.parameters.url
# Check if URL is not HTTPS
not regex.match("^https://", url)
msg := sprintf("Insecure URL detected: %v", [url])
}
Best Practices¶
- Keep policies simple and focused: Each policy should address a specific concern
- Use meaningful violation messages: Explain why something was flagged
- Group related rules together: Organize rules by the type of protection they provide
- Comment your policy code: Explain the rationale behind complex rules
- Test with both valid and invalid plans: Ensure policies are catching what they should
Example Policy Library¶
Plan-Lint comes with several example policies you can adapt:
security.rego: Basic security checks for dangerous operationsdata_privacy.rego: Checks for exposure of PII and sensitive dataauthorization.rego: Role-based access controls
You can find these in the examples/policies directory of the Plan-Lint repository.