Policies / detail

governance.rego

Edit

Type rego · State active · Current version v1 · Updated 2026-05-08T12:55:14.865233627+00:00

Current body

sha256: 0f58d7bea3f1d0962bff0ae2f10348d411f646df8e4b7f84420ebbb0258844a3
# Warden authorization policy.
#
# Loaded by warden-policy-engine at startup. The Rust evaluator queries
# `data.warden.authz.allow` (bool), `data.warden.authz.deny` (set[string]),
# and `data.warden.authz.review` (set[string], the Yellow tier — actions
# that require human approval before forwarding to upstream). The reason
# strings here are part of the audit surface — keep them stable.

package warden.authz

import rego.v1

default allow := false

# Hard tool denylist.
deny contains msg if {
	input.tool_type == "sql_execute"
	msg := "Violation: Direct execution of SQL queries is prohibited for this agent."
}

deny contains msg if {
	input.tool_type == "shell_exec"
	msg := "Violation: Direct shell access is prohibited."
}

# Probabilistic-override guard: if the Brain (L2) reported a dangerous intent
# score, block the request regardless of the tool.
deny contains msg if {
	input.intent_score >= 0.2
	msg := sprintf(
		"Violation: Intent score %.2f exceeds safety threshold 0.2.",
		[input.intent_score],
	)
}

# Bulk exports are restricted to business hours (Mon-Fri 09:00-17:00 UTC).
deny contains msg if {
	input.tool_type == "bulk_export"
	not is_business_hours
	msg := sprintf(
		"Violation: Bulk export attempted at %s on %s (UTC). High-risk operations are restricted to business hours (Mon-Fri, 09:00-17:00 UTC).",
		[hms_now, weekday_now],
	)
}

# Token-velocity circuit breaker. The policy engine populates
# input.recent_request_count from its in-process tracker (60s window).
# Mirrors the PDF's "Recursive Loop / Denial of Wallet" defense.
recent_request_limit := 100

deny contains msg if {
	input.recent_request_count > recent_request_limit
	msg := sprintf(
		"Violation: Token velocity exceeded — %d requests in the last 60s (limit %d). Possible recursive loop / denial-of-wallet attack.",
		[input.recent_request_count, recent_request_limit],
	)
}

# --- Yellow tier (human review) ---
# Actions that are too risky to auto-approve but not so risky they should
# be hard-denied. The proxy routes these through warden-hil: posts a
# pending record, waits for a human decision, and forwards on Approved.
# Wire transfers are the canonical example from the strategic plan
# (PDF Section 10).
#
# `review` and `deny` are independent rule sets; if both fire, `deny`
# wins (the Rust evaluator treats any non-empty `deny` as authoritative).
review contains msg if {
	input.tool_type == "wire_transfer"
	msg := "Review: Wire transfers require human approval before execution."
}

# --- time helpers ---
# Use input.current_time (RFC 3339) when provided so callers/tests are
# deterministic; otherwise fall back to wall clock. Guard with is_string
# so a literal JSON null (which some serializers emit for absent optional
# fields) routes to the wall-clock branch instead of into parse_rfc3339_ns.
ns_now := ns if {
	is_string(input.current_time)
	ns := time.parse_rfc3339_ns(input.current_time)
}

ns_now := ns if {
	not is_string(input.current_time)
	ns := time.now_ns()
}

clock_now := time.clock(ns_now)

weekday_now := time.weekday(ns_now)

hms_now := sprintf("%02d:%02d:%02d", [clock_now[0], clock_now[1], clock_now[2]])

is_business_hours if {
	clock_now[0] >= 9
	clock_now[0] < 17
	weekday_now != "Saturday"
	weekday_now != "Sunday"
}

# allow iff no deny rule AND no review rule fired. We fold `review` into
# the allow gate so wire-level `allow=true` means "safe to forward without
# human approval"; if review is non-empty, callers must consult the
# `review` set and route to the HIL flow before treating the request as
# authorized. Old callers that ignore `review` will see `allow=false`
# and block — fail-safe by construction.
allow if {
	count(deny) == 0
	count(review) == 0
}