//! Table tests for `policy::redecide`: an approval lets a call through only if the grants and //! the session's state, as they are when it is approved, still say `ask` or `auto`. Do not edit. #[path = "support/build.rs"] mod build; use brokerd::grants::GrantSet; use brokerd::policy::{Ask, Label, Outcome, decide, redecide}; use build::{grant, now, private, read, secret, set}; use proto::{DataClass, DenyReason, Mode}; const PATH: &str = "/home/kyle/notes/a.md"; fn asking() -> build::Build { grant("asks", "read_file", Mode::Ask).paths(&["/home/kyle/notes"]) } /// An `Ask` for `PATH`, decided under `asking()` alone at `private`. fn pending() -> Ask { match decide(read(PATH), &set(vec![asking()]), private(), now()) { Outcome::Ask(ask) => ask, other => panic!("expected ask, got {other:?}"), } } #[test] fn still_ask_lets_the_call_run_under_the_same_grant() { let decision = redecide(pending(), &set(vec![asking()]), private(), now()).unwrap(); assert_eq!(decision.grant(), "asks"); assert_eq!(decision.request(), &read(PATH)); assert_eq!(decision.matched_path(), Some("/home/kyle/notes")); assert_eq!( decision.label(), Label { class: DataClass::Private, untrusted: true } ); } #[test] fn auto_now_lets_the_call_run_under_the_grant_that_matches_now() { // The owner has since replaced the ask grant with an auto grant of another name and label. let grants = set(vec![ grant("now-auto", "read_file", Mode::Auto) .paths(&["/home/kyle"]) .class(DataClass::Secret) .trusted(), ]); let decision = redecide(pending(), &grants, private(), now()).unwrap(); assert_eq!(decision.grant(), "now-auto"); assert_eq!(decision.grant_sha256(), proto::sha256(b"now-auto").unwrap()); assert_eq!(decision.matched_path(), Some("/home/kyle")); assert_eq!(decision.paths(), ["/home/kyle"]); assert_eq!( decision.label(), Label { class: DataClass::Secret, untrusted: false } ); } #[test] fn the_grant_file_was_removed() { let denial = redecide(pending(), &GrantSet::default(), private(), now()).unwrap_err(); assert_eq!(denial.reason, DenyReason::NoGrant); assert_eq!(denial.grant, None); } #[test] fn the_taint_rose_past_max_taint_while_the_approval_waited() { let narrow = || asking().max_taint(DataClass::Private); let ask = match decide(read(PATH), &set(vec![narrow()]), private(), now()) { Outcome::Ask(ask) => ask, other => panic!("expected ask, got {other:?}"), }; let denial = redecide(ask, &set(vec![narrow()]), secret(), now()).unwrap_err(); assert_eq!(denial.reason, DenyReason::TaintTooHigh); } #[test] fn the_grant_expired_while_the_approval_waited() { let grants = set(vec![asking().expires("2026-09-18T12:10:00.000Z")]); let ask = match decide(read(PATH), &grants, private(), now()) { Outcome::Ask(ask) => ask, other => panic!("expected ask, got {other:?}"), }; assert_eq!(ask.expires(), Some(build::ts("2026-09-18T12:10:00.000Z"))); let later = build::ts("2026-09-18T12:10:00.000Z"); let denial = redecide(ask, &grants, private(), later).unwrap_err(); assert_eq!(denial.reason, DenyReason::GrantExpired); } #[test] fn a_deny_grant_was_added_while_the_approval_waited() { let grants = set(vec![ asking(), grant("no-notes", "read_file", Mode::Deny).paths(&["/home/kyle"]), ]); let denial = redecide(pending(), &grants, private(), now()).unwrap_err(); assert_eq!(denial.reason, DenyReason::DeniedByGrant); assert_eq!(denial.grant.as_deref(), Some("no-notes")); assert_eq!( denial.grant_sha256, Some(proto::sha256(b"no-notes").unwrap()) ); } #[test] fn the_grants_now_cover_other_arguments_only() { let grants = set(vec![ grant("asks", "read_file", Mode::Ask).paths(&["/home/kyle/other"]), ]); let denial = redecide(pending(), &grants, private(), now()).unwrap_err(); assert_eq!(denial.reason, DenyReason::NoGrant); }