feat: enhance memory management tools; improve lesson tracking and update descriptions for clarity
This commit is contained in:
+60
-12
@@ -2,13 +2,9 @@
|
||||
//! before it executes.
|
||||
|
||||
/// Outcome of gating a tool call: whether it's allowed to run.
|
||||
///
|
||||
/// Why: `Block` carries a reason string for surfacing to the user/log, even
|
||||
/// though nothing currently produces `Block` (classify() always allows).
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Verdict {
|
||||
Allow,
|
||||
#[allow(dead_code)]
|
||||
Block(String),
|
||||
}
|
||||
|
||||
@@ -19,23 +15,20 @@ impl Harness {
|
||||
/// Decide whether a tool call is allowed to execute.
|
||||
///
|
||||
/// Flow: if the tool isn't flagged risky, allow immediately → basic
|
||||
/// content checks (path traversal) → defer to `classify`.
|
||||
///
|
||||
/// Why: `classify` is currently a stub that always allows; the basic
|
||||
/// checks here serve as defense-in-depth alongside the shell filters
|
||||
/// and `resolve_path` in the tool modules.
|
||||
/// content checks (path traversal in paths AND command args) →
|
||||
/// workspace-root validation for output paths → classify.
|
||||
///
|
||||
/// Return: `Verdict::Allow` or `Verdict::Block(reason)`.
|
||||
pub fn gate_tool_call(
|
||||
tool_name: &str,
|
||||
args: &serde_json::Value,
|
||||
_workspace_roots: &[&std::path::Path],
|
||||
workspace_roots: &[&std::path::Path],
|
||||
) -> Verdict {
|
||||
|
||||
if !crate::tool::tool_is_risky(tool_name) {
|
||||
return Verdict::Allow;
|
||||
}
|
||||
// Basic path traversal check for file-mutating tools.
|
||||
// Path traversal check for file-mutating tools.
|
||||
if matches!(tool_name, "write" | "edit" | "delete") {
|
||||
if let Some(path) = args.get("path").and_then(|v| v.as_str()) {
|
||||
if path.contains("..") {
|
||||
@@ -43,11 +36,66 @@ impl Harness {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Path traversal and dangerous content check for bash commands.
|
||||
if tool_name == "bash" {
|
||||
let cmd = args.get("command").and_then(|v| v.as_str()).unwrap_or("");
|
||||
if cmd.contains("..") {
|
||||
return Verdict::Block("path traversal detected in bash command".to_string());
|
||||
}
|
||||
let dangerous_patterns = [
|
||||
"rm -rf /", "rm -rf --no-preserve-root",
|
||||
"rm -rf ~", "rm -fr /", "mkfs.", "dd if=",
|
||||
":(){", "> /dev/sda", "chmod -R 000 /",
|
||||
];
|
||||
for pat in &dangerous_patterns {
|
||||
if cmd.contains(pat) {
|
||||
return Verdict::Block(format!("destructive command pattern blocked: {}", pat));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(out_path) = Self::find_output_path(tool_name, args) {
|
||||
if !workspace_roots.is_empty()
|
||||
&& !out_path.starts_with("/tmp")
|
||||
&& !out_path.is_absolute()
|
||||
{
|
||||
let allowed = workspace_roots.iter().any(|r| out_path.starts_with(r));
|
||||
if !allowed {
|
||||
return Verdict::Block(format!(
|
||||
"output path '{:?}' is outside all workspace roots", out_path
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::classify(tool_name)
|
||||
}
|
||||
|
||||
/// Extract a candidate output path from a tool call, if one exists.
|
||||
///
|
||||
/// Used to verify that writes and file mutations stay inside workspace roots.
|
||||
fn find_output_path(tool_name: &str, args: &serde_json::Value) -> Option<std::path::PathBuf> {
|
||||
match tool_name {
|
||||
"write" | "edit" | "delete" | "read" => {
|
||||
args.get("path").and_then(|v| v.as_str()).map(std::path::PathBuf::from)
|
||||
}
|
||||
"bash" => {
|
||||
let cmd = args.get("command").and_then(|v| v.as_str())?;
|
||||
let lower = cmd.to_lowercase();
|
||||
for prefix in &["cp ", "mv ", "install ", "ln -s ", "cat >", "cat >>"] {
|
||||
if let Some(rest) = lower.strip_prefix(prefix) {
|
||||
if let Some(target) = rest.split_whitespace().last() {
|
||||
if !target.starts_with('-') {
|
||||
return Some(std::path::PathBuf::from(target));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn classify(cmd: &str) -> Verdict {
|
||||
// Classify known-dangerous patterns beyond path traversal.
|
||||
match cmd {
|
||||
"bash" | "write" | "edit" | "delete" | "git_operator" => Verdict::Allow,
|
||||
_ => Verdict::Allow,
|
||||
|
||||
Reference in New Issue
Block a user