brokerd: recall and memory, decided under built-in grants
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
//! `recall` and `memory` in `brokerd`: their arguments, the built-in grants that allow them in
|
||||
//! every session, and grant files that name them refused (M5a spec, section 8). Do not edit.
|
||||
|
||||
#[path = "support/build.rs"]
|
||||
mod build;
|
||||
#[path = "support/tmp.rs"]
|
||||
mod tmp;
|
||||
|
||||
use brokerd::args::{ToolArgs, ToolName, parse};
|
||||
use brokerd::builtin::{BUILT_IN, MEMORY, RECALL};
|
||||
use brokerd::grants::{GrantSet, load};
|
||||
use brokerd::policy::{Label, Outcome, decide};
|
||||
use build::{allowed, grant, now, private, request, secret, set};
|
||||
use proto::{DataClass, DenyReason, Mode, sha256};
|
||||
use tmp::TempDir;
|
||||
|
||||
#[test]
|
||||
fn the_names_and_arguments_of_the_memory_tools() {
|
||||
assert_eq!(ToolName::parse("recall"), Some(ToolName::Recall));
|
||||
assert_eq!(ToolName::parse("memory"), Some(ToolName::Memory));
|
||||
assert_eq!(
|
||||
(ToolName::Recall.as_str(), ToolName::Memory.as_str()),
|
||||
("recall", "memory")
|
||||
);
|
||||
assert!(ToolName::Recall.is_memory() && ToolName::Memory.is_memory());
|
||||
assert!(
|
||||
ToolName::ALL.iter().all(|t| !t.is_memory()),
|
||||
"ALL is the container tools"
|
||||
);
|
||||
let recall = parse(ToolName::Recall, r#"{ "k": 3, "query": "launch" }"#).unwrap();
|
||||
assert_eq!(recall.tool(), ToolName::Recall);
|
||||
assert_eq!(recall.canonical_json(), r#"{"query":"launch","k":3}"#);
|
||||
let memory = parse(ToolName::Memory, r#"{"text":"x","action":"note"}"#).unwrap();
|
||||
assert!(matches!(memory, ToolArgs::Memory(_)));
|
||||
assert_eq!(memory.canonical_json(), r#"{"action":"note","text":"x"}"#);
|
||||
for (tool, bad) in [
|
||||
(ToolName::Recall, r#"{"query":""}"#),
|
||||
(ToolName::Recall, r#"{"query":"x","k":11}"#),
|
||||
(
|
||||
ToolName::Memory,
|
||||
r#"{"action":"add","file":"soul","text":"x"}"#,
|
||||
),
|
||||
(
|
||||
ToolName::Memory,
|
||||
r#"{"action":"add","file":"memory","text":"a\n§\nb"}"#,
|
||||
),
|
||||
(
|
||||
ToolName::Memory,
|
||||
r#"{"action":"consolidate","file":"memory","entries":[]}"#,
|
||||
),
|
||||
] {
|
||||
assert!(parse(tool, bad).is_err(), "{bad}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn both_are_allowed_in_every_session_by_the_built_in_grants() {
|
||||
let grants = set(vec![]);
|
||||
for state in [private(), secret()] {
|
||||
let d = allowed(decide(
|
||||
request("recall", r#"{"query":"x"}"#),
|
||||
&grants,
|
||||
state,
|
||||
now(),
|
||||
));
|
||||
assert_eq!(d.grant(), "builtin-recall");
|
||||
assert_eq!(d.grant_sha256(), sha256(RECALL.as_bytes()).unwrap());
|
||||
assert_eq!(
|
||||
d.label(),
|
||||
Label {
|
||||
class: DataClass::Private,
|
||||
untrusted: false
|
||||
}
|
||||
);
|
||||
let d = allowed(decide(
|
||||
request("memory", r#"{"action":"note","text":"x"}"#),
|
||||
&grants,
|
||||
state,
|
||||
now(),
|
||||
));
|
||||
assert_eq!(d.grant(), "builtin-memory");
|
||||
assert_eq!(d.grant_sha256(), sha256(MEMORY.as_bytes()).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_set_without_built_ins_has_no_memory_tools() {
|
||||
let outcome = decide(
|
||||
request("recall", r#"{"query":"x"}"#),
|
||||
&GrantSet::default(),
|
||||
private(),
|
||||
now(),
|
||||
);
|
||||
assert!(
|
||||
matches!(outcome, Outcome::Denied(ref d) if d.reason == DenyReason::NoGrant),
|
||||
"{outcome:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grant_files_cannot_name_the_memory_tools_or_take_a_built_in_id() {
|
||||
let dir = TempDir::new("memory-grants");
|
||||
std::fs::write(
|
||||
dir.path().join("mine.toml"),
|
||||
"tool = \"memory\"\nmode = \"ask\"\nmax_taint = \"secret\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(
|
||||
dir.path().join("off.toml"),
|
||||
"tool = \"recall\"\nmode = \"deny\"\nmax_taint = \"secret\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(
|
||||
dir.path().join("builtin-x.toml"),
|
||||
"tool = \"shell\"\nmode = \"ask\"\nmax_taint = \"private\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
let problems = load(dir.path()).unwrap_err();
|
||||
let got: Vec<(String, String)> = problems.into_iter().map(|p| (p.file, p.problem)).collect();
|
||||
assert!(
|
||||
got.contains(&("mine.toml".to_string(), BUILT_IN.to_string())),
|
||||
"{got:?}"
|
||||
);
|
||||
assert!(
|
||||
got.contains(&("off.toml".to_string(), BUILT_IN.to_string())),
|
||||
"{got:?}"
|
||||
);
|
||||
assert!(
|
||||
got.contains(&(
|
||||
"builtin-x.toml".to_string(),
|
||||
"grant ids starting builtin- are reserved".to_string()
|
||||
)),
|
||||
"{got:?}"
|
||||
);
|
||||
let with_shell = set(vec![grant("s", "shell", Mode::Ask)]);
|
||||
assert_eq!(
|
||||
with_shell.grants().len(),
|
||||
1,
|
||||
"the built-ins are not listed as the owner's"
|
||||
);
|
||||
assert_eq!(with_shell.matchable().count(), 3);
|
||||
}
|
||||
Reference in New Issue
Block a user