Files
boxmaker/crates/brokerd/tests/policy_redecide.rs
T
kyleandClaude Opus 5.5 2b3ae080b4 proto becomes bxcore, the one shared crate (M5b task 00)
Done the way the owner asked and the spike mapped: the crate and the
manifests renamed, then what the build reported fixed. Layers: 138
"proto unresolved" errors across 169 files; cargo fmt reordering
imports; six fixture paths built as strings in brokerd's and bxctl's
tests, which the compiler cannot see, and comments naming
crates/proto/; the crate-dependency check and its self-test. One more
the spike did not reach: a `\nproto =` key inside a printf format in
the self-test, which a word-boundary replacement skips because `n` and
`p` are both word characters.

make gate: gate: ok, 944 tests; no new entry in /tmp.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 01:48:38 -07:00

121 lines
4.1 KiB
Rust

//! 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 bxcore::{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(),
bxcore::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(bxcore::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);
}