//! Builders for grants and requests, for the policy tests. Do not edit. //! //! Included with `#[path = "support/build.rs"] mod build;`. #![allow(dead_code)] // each test file uses a different part of this module use brokerd::grants::{GrantSet, LoadedGrant}; use brokerd::policy::{Ask, Decision, Denial, Outcome, SessionState}; use proto::{ CallId, Constraints, DataClass, DenyReason, Grant, Mode, SessionId, Timestamp, ToolRequest, }; /// The moment every policy test decides at. pub const NOW: &str = "2026-09-18T12:00:00.000Z"; pub fn ts(text: &str) -> Timestamp { Timestamp::parse(text).unwrap() } pub fn now() -> Timestamp { ts(NOW) } pub struct Build(LoadedGrant); /// A grant with the widest settings: it applies at every taint, never expires, and labels its /// results `private` and untrusted. Each test narrows what it is about. pub fn grant(id: &str, tool: &str, mode: Mode) -> Build { Build(LoadedGrant { id: id.to_string(), grant: Grant { tool: tool.to_string(), mode, max_taint: DataClass::Secret, result_class: DataClass::Private, untrusted: true, expires: None, secret: None, constraints: Constraints::default(), }, // Stands in for the file's hash, and differs from grant to grant. sha256: proto::sha256(id.as_bytes()).unwrap(), }) } impl Build { pub fn paths(mut self, paths: &[&str]) -> Build { self.0.grant.constraints.paths = paths.iter().map(|p| p.to_string()).collect(); self } pub fn hosts(mut self, hosts: &[&str]) -> Build { self.0.grant.constraints.hosts = hosts.iter().map(|h| h.to_string()).collect(); self } pub fn max_taint(mut self, class: DataClass) -> Build { self.0.grant.max_taint = class; self } pub fn class(mut self, class: DataClass) -> Build { self.0.grant.result_class = class; self } pub fn trusted(mut self) -> Build { self.0.grant.untrusted = false; self } pub fn expires(mut self, at: &str) -> Build { self.0.grant.expires = Some(ts(at)); self } pub fn done(self) -> LoadedGrant { self.0 } } pub fn set(grants: Vec) -> GrantSet { GrantSet::from_grants(grants.into_iter().map(Build::done).collect()) .unwrap_or_else(|problems| panic!("the test's grants are not valid: {problems:?}")) } pub fn request(tool: &str, arguments: &str) -> ToolRequest { ToolRequest { session: SessionId::new("s1").unwrap(), call: CallId(1), tool: tool.to_string(), arguments: arguments.to_string(), } } pub fn read(path: &str) -> ToolRequest { request("read_file", &format!(r#"{{"path":"{path}"}}"#)) } pub fn write(path: &str) -> ToolRequest { request( "write_file", &format!(r#"{{"path":"{path}","content":"x"}}"#), ) } pub fn shell(cwd: Option<&str>) -> ToolRequest { match cwd { Some(cwd) => request("shell", &format!(r#"{{"command":"ls","cwd":"{cwd}"}}"#)), None => request("shell", r#"{"command":"ls"}"#), } } pub fn fetch(url: &str) -> ToolRequest { request("http_fetch", &format!(r#"{{"url":"{url}"}}"#)) } pub fn at(taint: DataClass) -> SessionState { SessionState { taint, untrusted: false, } } pub fn private() -> SessionState { at(DataClass::Private) } pub fn secret() -> SessionState { at(DataClass::Secret) } pub fn allowed(outcome: Outcome) -> Decision { match outcome { Outcome::Allowed(decision) => decision, other => panic!("expected allowed, got {other:?}"), } } pub fn asked(outcome: Outcome) -> Ask { match outcome { Outcome::Ask(ask) => ask, other => panic!("expected ask, got {other:?}"), } } pub fn denied(outcome: Outcome) -> Denial { match outcome { Outcome::Denied(denial) => denial, other => panic!("expected denied, got {other:?}"), } } /// The reason of a denial. Only `denied_by_grant` may name a grant. pub fn reason(outcome: Outcome) -> DenyReason { let denial = denied(outcome); if denial.reason != DenyReason::DeniedByGrant { assert_eq!(denial.grant, None, "only denied_by_grant names a grant"); assert_eq!(denial.grant_sha256, None); } denial.reason }