hive-agent: default-deny ACP permission requests
acp_permits allows tools of the session's MCP servers, the read, edit and search kinds, and fetch with web_tools; every other kind, including execute, other and kinds it doesn't know, is refused. Before, anything but execute (and fetch without web_tools) was allowed, which was only safe while the preset's own config denied the risky built-ins. Refs #4391
This commit is contained in:
parent
a2ab40cc69
commit
868fc789d1
1 changed files with 67 additions and 11 deletions
|
|
@ -9,8 +9,8 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use anyhow::Result;
|
||||
use hive_runtime::{
|
||||
AcpRuntime, AgentRuntime, ClaudeRuntime, Config, PercentPolicy, PermissionPolicy, Runtime,
|
||||
RuntimeSpec, Sink,
|
||||
AcpRuntime, AgentRuntime, ClaudeRuntime, Config, PercentPolicy, PermissionAsk,
|
||||
PermissionPolicy, Runtime, RuntimeSpec, Sink,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
||||
|
|
@ -334,18 +334,33 @@ pub fn make_session(bus: &Bus) -> Result<AgentSession> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Which ACP tool-call kinds an ACP agent may run when it asks. Mirrors the
|
||||
/// claude built-ins (`hive_sh4re::permissions`): never a built-in shell
|
||||
/// (`execute`) — shell goes through `mcp__bash__run` — and web access
|
||||
/// (`fetch`) only with the `web_tools` group.
|
||||
/// The answer to an ACP agent's permission requests, see [`acp_permits`].
|
||||
fn acp_permission_policy() -> PermissionPolicy {
|
||||
let web =
|
||||
mcp_config::effective_tool_groups().contains(&hive_sh4re::permissions::ToolGroup::WebTools);
|
||||
std::sync::Arc::new(move |kind: &str| match kind {
|
||||
"execute" => false,
|
||||
std::sync::Arc::new(move |ask: &PermissionAsk<'_>| acp_permits(ask, web))
|
||||
}
|
||||
|
||||
/// Whether an ACP agent may run the tool call it asks about. Default-deny,
|
||||
/// allowing what a claude agent has:
|
||||
///
|
||||
/// - tools of the MCP servers the session was handed — which of those an
|
||||
/// agent gets is already decided by its tool groups (`mcp_config`);
|
||||
/// - the file tools, mirroring the claude built-ins
|
||||
/// (`hive_sh4re::permissions`): `read`, `edit`, `search`;
|
||||
/// - `fetch` with the `web_tools` group (`web`).
|
||||
///
|
||||
/// Everything else is refused, including a built-in shell (`execute`) —
|
||||
/// shell goes through `mcp__bash__run` — and any kind this list doesn't name.
|
||||
fn acp_permits(ask: &PermissionAsk<'_>, web: bool) -> bool {
|
||||
if ask.mcp_server.is_some() {
|
||||
return true;
|
||||
}
|
||||
match ask.kind {
|
||||
"read" | "edit" | "search" => true,
|
||||
"fetch" => web,
|
||||
_ => true,
|
||||
})
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Drive one turn end-to-end. The durable [`AgentSession`] owns the
|
||||
|
|
@ -795,7 +810,7 @@ fn archive_session(bus: &Bus, session: &AgentSession) {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{nonzero_or, parse_u64};
|
||||
use super::{PermissionAsk, acp_permits, nonzero_or, parse_u64};
|
||||
|
||||
#[test]
|
||||
fn an_absent_or_blank_value_is_not_a_number() {
|
||||
|
|
@ -840,4 +855,45 @@ mod tests {
|
|||
"rejected: 0 is not a duration"
|
||||
);
|
||||
}
|
||||
|
||||
fn ask(kind: &str) -> PermissionAsk<'_> {
|
||||
PermissionAsk {
|
||||
kind,
|
||||
mcp_server: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_permissions_default_to_deny() {
|
||||
for kind in [
|
||||
"execute",
|
||||
"delete",
|
||||
"move",
|
||||
"switch_mode",
|
||||
"other",
|
||||
"think",
|
||||
"",
|
||||
"new_kind",
|
||||
] {
|
||||
assert!(!acp_permits(&ask(kind), true), "{kind:?} was allowed");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_file_tools_and_mcp_servers_are_allowed() {
|
||||
for kind in ["read", "edit", "search"] {
|
||||
assert!(acp_permits(&ask(kind), false), "{kind:?} was refused");
|
||||
}
|
||||
let mcp = PermissionAsk {
|
||||
kind: "other",
|
||||
mcp_server: Some("hyperhive"),
|
||||
};
|
||||
assert!(acp_permits(&mcp, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_fetch_follows_the_web_tools_group() {
|
||||
assert!(acp_permits(&ask("fetch"), true));
|
||||
assert!(!acp_permits(&ask("fetch"), false));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue