feat: enhance safety and crash resilience in file operations; add fsync to critical writes and checks for path traversal
This commit is contained in:
+14
-6
@@ -18,23 +18,31 @@ pub struct Harness;
|
||||
impl Harness {
|
||||
/// Decide whether a tool call is allowed to execute.
|
||||
///
|
||||
/// Flow: if the tool isn't flagged risky, allow immediately → otherwise
|
||||
/// defer to `classify`.
|
||||
/// Flow: if the tool isn't flagged risky, allow immediately → basic
|
||||
/// content checks (path traversal) → defer to `classify`.
|
||||
///
|
||||
/// Why: `_args` and `_workspace_roots` are accepted for a future
|
||||
/// content-aware classifier but currently unused — `classify` is a
|
||||
/// stub that always allows.
|
||||
/// 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.
|
||||
///
|
||||
/// Return: `Verdict::Allow` or `Verdict::Block(reason)`.
|
||||
pub fn gate_tool_call(
|
||||
tool_name: &str,
|
||||
_args: &serde_json::Value,
|
||||
args: &serde_json::Value,
|
||||
_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.
|
||||
if matches!(tool_name, "write" | "edit" | "delete") {
|
||||
if let Some(path) = args.get("path").and_then(|v| v.as_str()) {
|
||||
if path.contains("..") {
|
||||
return Verdict::Block("path traversal detected in 'path' argument".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::classify(tool_name)
|
||||
}
|
||||
|
||||
|
||||
@@ -101,9 +101,13 @@ pub fn run_subagent(ctx: SubagentContext, tx: mpsc::Sender<SubagentEvent>) -> an
|
||||
let (tools, tdefs) = build_subagent_tools(&ctx.allowed_tools);
|
||||
let tdefs_opt: Option<Vec<ToolDef>> = if tdefs.is_empty() { None } else { Some(tdefs) };
|
||||
|
||||
// Cache provider config once before the loop instead of re-resolving
|
||||
// from disk on every step (Settings::load + AppConfig::load each parse
|
||||
// JSON files, and the config cannot change between steps).
|
||||
let (api_key, model, base_url) = resolve_provider_config();
|
||||
let client = crate::service::provider::LlmClient::new(api_key, model, base_url);
|
||||
|
||||
for step in 0..ctx.max_steps {
|
||||
let (api_key, model, base_url) = resolve_provider_config();
|
||||
let client = crate::service::provider::LlmClient::new(api_key, model, base_url);
|
||||
|
||||
// Use the structured tool-calling API so the LLM can request tools with
|
||||
// proper arguments, exactly like the main agent does.
|
||||
|
||||
Reference in New Issue
Block a user