Hand over the M3a plan: 22 tasks, their files, and the check record
Task files, the files they copy in (byte-identical to the reference on m3a-ref), each area's check record, and a README with the per-task table of what each check exposed. The handoff note is done with. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
//! 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);
|
||||
}
|
||||
Reference in New Issue
Block a user