115 lines
3.2 KiB
Rego
115 lines
3.2 KiB
Rego
# =============================================================================
|
|
# AI Gateway Policy — OPA/Rego Reference Implementation
|
|
# =============================================================================
|
|
# This file is a reference-only translation of policies.yaml into OPA Rego.
|
|
# It is NOT processed by the gateway; it exists to show how the same rules
|
|
# would look if evaluated by Open Policy Agent.
|
|
# =============================================================================
|
|
|
|
package gateway.policy
|
|
|
|
import rego.v1
|
|
|
|
# ---------- data ----------
|
|
allowed_projects := {"SAFE", "PLATFORM", "OPS"}
|
|
allowed_changelogs := {"release_*.xml", "hotfix_*.xml"}
|
|
business_hours := {"start": "08:00", "end": "18:00", "timezone": "Europe/Oslo"}
|
|
rate_limit_default := 30
|
|
|
|
# ---------- default decision ----------
|
|
default decision := "deny"
|
|
|
|
# ---------- scope-based allow ----------
|
|
decision := "allow" if {
|
|
input.tool == "jira.*" # pattern: starts_with(input.tool, "jira.")
|
|
startswith(input.tool, "jira.")
|
|
"jira.read" in input.subject.scopes
|
|
input.action in {"read", "get", "list", "search"}
|
|
}
|
|
|
|
decision := "allow" if {
|
|
startswith(input.tool, "jira.")
|
|
"jira.write" in input.subject.scopes
|
|
input.action in {"create", "update", "delete"}
|
|
}
|
|
|
|
decision := "allow" if {
|
|
input.tool == "db.queryReadonly"
|
|
"db.read" in input.subject.scopes
|
|
}
|
|
|
|
# ---------- environment-based rules ----------
|
|
decision := "allow" if {
|
|
input.tool == "jenkins.deploy"
|
|
input.context.environment == "staging"
|
|
}
|
|
|
|
decision := "require_approval" if {
|
|
input.tool == "jenkins.deploy"
|
|
input.context.environment == "prod"
|
|
}
|
|
|
|
# ---------- argument allowlist ----------
|
|
decision := "deny" if {
|
|
input.tool == "jira.createTicket"
|
|
not input.arguments.project in allowed_projects
|
|
}
|
|
|
|
decision := "deny" if {
|
|
input.tool == "db.runLiquibase"
|
|
not glob_match_any(allowed_changelogs, input.arguments.changelog)
|
|
}
|
|
|
|
# ---------- block rule ----------
|
|
decision := "deny" if {
|
|
input.tool == "db.executeScript"
|
|
}
|
|
|
|
# ---------- rate limit ----------
|
|
decision := "deny" if {
|
|
input.context.rate.current > rate_limit_default
|
|
}
|
|
|
|
# ---------- time window ----------
|
|
decision := "deny" if {
|
|
input.tool == "jenkins.deploy"
|
|
not within_business_hours(input.context.timestamp)
|
|
}
|
|
|
|
# ---------- helpers ----------
|
|
within_business_hours(timestamp) if {
|
|
# Simplified: real implementation would parse timestamp and convert to Europe/Oslo
|
|
hour := to_number(substring(timestamp, 11, 2))
|
|
hour >= 8
|
|
hour < 18
|
|
}
|
|
|
|
glob_match_any(patterns, value) if {
|
|
some pattern in patterns
|
|
glob.match(pattern, ["/"], value)
|
|
}
|
|
|
|
# ---------- reason ----------
|
|
reason := "Project not in allowlist" if {
|
|
input.tool == "jira.createTicket"
|
|
not input.arguments.project in allowed_projects
|
|
}
|
|
|
|
reason := "Changelog not in allowlist" if {
|
|
input.tool == "db.runLiquibase"
|
|
not glob_match_any(allowed_changelogs, input.arguments.changelog)
|
|
}
|
|
|
|
reason := "db.executeScript is blocked by policy" if {
|
|
input.tool == "db.executeScript"
|
|
}
|
|
|
|
reason := "Rate limit exceeded" if {
|
|
input.context.rate.current > rate_limit_default
|
|
}
|
|
|
|
reason := "Deployments only allowed during business hours" if {
|
|
input.tool == "jenkins.deploy"
|
|
not within_business_hours(input.context.timestamp)
|
|
}
|