use std::sync::LazyLock;
use serde::{Deserialize, Serialize};
const POLICY_TOML: &str = include_str!("approval_review_policy.toml");
pub static APPROVAL_REVIEW_POLICY: LazyLock<ApprovalReviewPolicy> = LazyLock::new(|| {
toml::from_str(POLICY_TOML).expect("bundled approval_review_policy.toml parses")
});
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ApprovalReviewPolicy {
pub version: u32,
pub reviewer: ReviewerConfig,
pub breaker: BreakerConfig,
pub floor: FloorConfig,
pub denylist: DenylistConfig,
pub trust: TrustConfig,
pub verdict: VerdictConfig,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ReviewerConfig {
pub model: String,
pub effort: String,
pub timeout_ms: u64,
pub on_error: OnReviewerError,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OnReviewerError {
#[default]
Deny,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct BreakerConfig {
pub max_consecutive_denials: u32,
pub max_denials_per_turn: u32,
pub cell_flag_denied_trial_share: f64,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct FloorConfig {
pub never_grant: Vec<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct DenylistConfig {
pub categories: Vec<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct TrustConfig {
pub trusted_inputs: Vec<String>,
pub untrusted_inputs: Vec<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct VerdictConfig {
pub risk_levels: Vec<String>,
pub authorization_levels: Vec<String>,
pub thresholds: std::collections::BTreeMap<String, String>,
}
impl ApprovalReviewPolicy {
pub fn bundled() -> &'static Self {
&APPROVAL_REVIEW_POLICY
}
pub fn is_floor(&self, category: &str) -> bool {
self.floor.never_grant.iter().any(|c| c == category)
}
pub fn is_denylisted(&self, category: &str) -> bool {
self.denylist.categories.iter().any(|c| c == category)
}
pub fn approval_permitted(&self, risk: &str, authorization: &str) -> bool {
let Some(required) = self.verdict.thresholds.get(risk) else {
return false;
};
if required == "never" {
return false;
}
let rank = |level: &str| {
self.verdict
.authorization_levels
.iter()
.position(|l| l == level)
};
match (rank(authorization), rank(required)) {
(Some(have), Some(need)) => have >= need,
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bundled_policy_parses() {
let policy = ApprovalReviewPolicy::bundled();
assert_eq!(policy.version, 1);
assert_eq!(policy.reviewer.on_error, OnReviewerError::Deny);
assert!(policy.breaker.max_consecutive_denials > 0);
}
#[test]
fn the_floor_is_not_empty() {
let policy = ApprovalReviewPolicy::bundled();
assert!(!policy.floor.never_grant.is_empty());
assert!(policy.is_floor("credential_exfiltration"));
assert!(!policy.is_floor("read_a_source_file"));
}
#[test]
fn critical_risk_can_never_be_approved() {
let policy = ApprovalReviewPolicy::bundled();
for authorization in &policy.verdict.authorization_levels {
assert!(
!policy.approval_permitted("critical", authorization),
"critical must not be approvable at authorization {authorization}"
);
}
}
#[test]
fn higher_authorization_clears_higher_risk() {
let policy = ApprovalReviewPolicy::bundled();
assert!(policy.approval_permitted("low", "unknown"));
assert!(!policy.approval_permitted("high", "low"));
assert!(policy.approval_permitted("high", "medium"));
assert!(policy.approval_permitted("high", "high"));
}
#[test]
fn an_uninterpretable_verdict_is_not_an_approval() {
let policy = ApprovalReviewPolicy::bundled();
assert!(!policy.approval_permitted("catastrophic", "high"));
assert!(!policy.approval_permitted("low", "absolute"));
}
}