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>
124 lines
3.3 KiB
Rust
124 lines
3.3 KiB
Rust
//! The ledger raises a session's state by the stricter of the grant's label and the result's own
|
|
//! (M5a spec, section 8). `memoryd` can make a `recall` result untrusted; if the session did not
|
|
//! follow, a clean session could recall a tainted note and then write it into `memory.md`. Do not
|
|
//! edit.
|
|
|
|
#[path = "support/build.rs"]
|
|
mod build;
|
|
#[path = "support/rig.rs"]
|
|
mod rig;
|
|
#[path = "support/sink.rs"]
|
|
mod sink;
|
|
#[path = "support/tmp.rs"]
|
|
mod tmp;
|
|
|
|
use brokerd::ledger::Call;
|
|
use brokerd::policy::{Label, SessionState};
|
|
use build::now;
|
|
use bxcore::{AuditEvent, CallId, DataClass, SessionId, ToolResponse};
|
|
use rig::Rig;
|
|
|
|
fn label(class: DataClass, untrusted: bool) -> Label {
|
|
Label { class, untrusted }
|
|
}
|
|
|
|
fn call(label: Label) -> Call {
|
|
Call {
|
|
session: SessionId::new("s1").unwrap(),
|
|
call: CallId(1),
|
|
decision: 0,
|
|
label,
|
|
}
|
|
}
|
|
|
|
fn result(label: Label) -> ToolResponse {
|
|
ToolResponse::Result {
|
|
content: "[1] memory-tainted · note".to_string(),
|
|
class: label.class,
|
|
untrusted: label.untrusted,
|
|
truncated: false,
|
|
}
|
|
}
|
|
|
|
fn state(rig: &Rig) -> SessionState {
|
|
rig.state().read(&SessionId::new("s1").unwrap()).unwrap()
|
|
}
|
|
|
|
/// The one result record's (class, untrusted, taint_after).
|
|
fn recorded(rig: &Rig) -> (DataClass, bool, DataClass) {
|
|
match rig.events().as_slice() {
|
|
[
|
|
AuditEvent::Result {
|
|
class,
|
|
untrusted,
|
|
taint_after,
|
|
..
|
|
},
|
|
] => (*class, *untrusted, *taint_after),
|
|
other => panic!("{other:?}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn a_recall_of_a_tainted_note_makes_a_clean_session_untrusted() {
|
|
let rig = Rig::new("label-recall");
|
|
let ledger = rig.ledger();
|
|
// `recall`'s built-in grant labels its results private and trusted; `memoryd` found a
|
|
// `memory-tainted` hit and said untrusted.
|
|
let found = label(DataClass::Private, true);
|
|
let answer = ledger.finish(
|
|
&call(label(DataClass::Private, false)),
|
|
result(found),
|
|
now(),
|
|
);
|
|
assert_eq!(answer, result(found), "passed on unchanged");
|
|
assert_eq!(
|
|
state(&rig),
|
|
SessionState {
|
|
taint: DataClass::Private,
|
|
untrusted: true
|
|
}
|
|
);
|
|
assert_eq!(
|
|
recorded(&rig),
|
|
(DataClass::Private, true, DataClass::Private)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn a_result_of_a_higher_class_than_its_grant_raises_the_taint() {
|
|
let rig = Rig::new("label-class");
|
|
let ledger = rig.ledger();
|
|
let found = label(DataClass::Secret, false);
|
|
ledger.finish(&call(label(DataClass::Public, false)), result(found), now());
|
|
assert_eq!(
|
|
state(&rig),
|
|
SessionState {
|
|
taint: DataClass::Secret,
|
|
untrusted: false
|
|
}
|
|
);
|
|
assert_eq!(
|
|
recorded(&rig),
|
|
(DataClass::Secret, false, DataClass::Secret)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn a_result_laxer_than_its_grant_still_raises_the_state_by_the_grant() {
|
|
let rig = Rig::new("label-laxer");
|
|
let ledger = rig.ledger();
|
|
ledger.finish(
|
|
&call(label(DataClass::Secret, true)),
|
|
result(label(DataClass::Public, false)),
|
|
now(),
|
|
);
|
|
assert_eq!(
|
|
state(&rig),
|
|
SessionState {
|
|
taint: DataClass::Secret,
|
|
untrusted: true
|
|
}
|
|
);
|
|
}
|