feat(types): add ToolApprovalClass + tool_classifier (no behavior change yet)#3092
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8d1c0cbeaa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if let Some(explicit) = explicit_class_from_schema(&def.input_schema) { | ||
| return explicit; |
There was a problem hiding this comment.
Prevent untrusted schema from downgrading class
classify_tool returns the class from input_schema metadata before considering the tool name, so any x-tool-class / metadata.tool_class value can lower the risk class. MCP tool definitions are built from server-provided schema (McpConnection::register_tool stores remote input_schema directly in ToolDefinition), so a remote tool can self-label as readonly_scoped and potentially bypass stricter approval tiers once this classifier is consumed by gating logic. Restrict this override to trusted/local definitions or only allow metadata to increase severity.
Useful? React with 👍 / 👎.
|
|
||
| fn classify_by_name(name: &str) -> ToolApprovalClass { | ||
| match name { | ||
| "file_read" | "glob" | "grep" | "ls" | "cat" => ToolApprovalClass::ReadonlyScoped, |
There was a problem hiding this comment.
Include file_list in read-only classification map
The read-only name match omits the canonical built-in file_list, even though aliases like glob, grep, and ls are normalized to file_list (tool_compat::map_tool_name) and builtin_tool_definitions exposes file_list as the directory-read tool. If callers classify normalized or built-in names, file_list falls through to Unknown instead of ReadonlyScoped, which will over-gate harmless listing operations and undermine the intended tiered policy behavior.
Useful? React with 👍 / 👎.
Why
Today every gated tool funnels through one approval path with the same prompt UX, regardless of whether the tool is reading a workspace file or rebooting the kernel. That's been fine while the tool surface was small, but it forces an all-or-nothing policy and gives operators no leverage to auto-allow safe reads while still gating execs and control-plane changes.
This PR lays the foundation for a tiered approval ladder by introducing a single source of truth for what kind of risk a tool represents. It is purely additive: no caller is wired up yet, and
ApprovalManageris intentionally untouched.Modeled after openclaw's
AcpApprovalClass(7 classes, severity-ordered).What changes
librefang_types::tool_class::ToolApprovalClasswith 7 variants (ReadonlyScoped,ReadonlySearch,Mutating,ExecCapable,ControlPlane,Interactive,Unknown).Display,Default(->Unknown), serdesnake_case.severity_rank() -> u8returning0..=6(least-to-most cautious).from_snake_case(&str) -> Option<Self>for round-tripping explicit annotations.librefang_runtime::tool_classifier::classify_tool(name, definition) -> ToolApprovalClass.file_read | glob | grep | ls | cat -> ReadonlyScoped, etc.).ToolDefinition::input_schemacarries anx-tool-class(or nestedmetadata.tool_class) string and it parses, that wins over the name heuristic. Unrecognized values fall back to the name match — they don't poison the result.ToolDefinitionitself is not modified, so the 30+ existing struct-init sites stay source-compatible.use librefang_types::ToolApprovalClass;anduse librefang_runtime::classify_tool;.(file_read, None)case, explicit metadata override, severity ordering, andserde_jsonround-trip producing"readonly_scoped".Out of scope (deliberately)
ApprovalManagerincrates/librefang-kernel/src/approval.rs.These will land in follow-up PRs that consume this classifier.
Files touched
crates/librefang-types/src/tool_class.rs(new)crates/librefang-types/src/lib.rs(module + re-export)crates/librefang-runtime/src/tool_classifier.rs(new)crates/librefang-runtime/src/lib.rs(module + re-export)