Refactor: Remove security module and related functionality

- Deleted the `security` module and its associated files, including `daemon.rs` and `install.rs`.
- Removed references to security features in various modules, including `mod.rs`, `mode/mod.rs`, and `input.rs`.
- Updated the `MiscState` struct to eliminate security-related fields.
- Adjusted the `apply_action` function to remove security action handling.
- Increased the maximum limits for tool-only turns and agent steps in `actions/mod.rs`.
- Modified the review prompt to exclude security checks.
- Cleaned up the `git_operator` and `shell` tools to remove catastrophic guard checks.
- Removed internet-related tools and their references from the tool module.
This commit is contained in:
asepharyana
2026-07-12 03:56:43 +07:00
parent 4bfbe1d1b9
commit a974118b5a
39 changed files with 79 additions and 2175 deletions
+1 -5
View File
@@ -12,7 +12,7 @@ impl Tool for GitOperator {
}
fn description(&self) -> &'static str {
"Execute git operations with catastrophic guard protection"
"Execute git operations"
}
fn parameters(&self) -> Value {
@@ -46,10 +46,6 @@ impl Tool for GitOperator {
.collect()
})
.ok_or_else(|| anyhow!("missing required argument: args"))?;
let full_cmd_str = format!("git {} {}", operation, arg_list.join(" "));
let workspace_roots: Vec<&std::path::Path> = _ctx.workspaces.iter().map(|p| p.as_path()).collect();
crate::app::catastrophic::CatastrophicGuard::check_all(&full_cmd_str, &workspace_roots)
.map_err(|e| anyhow!("catastrophic guard blocked: {}", e))?;
let output = Command::new("git")
.arg(&operation)
.args(&arg_list)
-78
View File
@@ -1,78 +0,0 @@
use std::fs;
use std::io::copy;
use std::path::PathBuf;
use std::time::Duration;
use serde_json::{json, Value};
use anyhow::{Result, anyhow};
use super::super::Tool;
use super::super::ToolCtx;
use super::super::resolve_path;
pub struct Download;
impl Tool for Download {
fn name(&self) -> &'static str {
"download"
}
fn description(&self) -> &'static str {
"Download a file from a URL to a local path"
}
fn parameters(&self) -> Value {
json!({
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "URL to download from"
},
"path": {
"type": "string",
"description": "Local path to save the file (relative to workspace root)"
}
},
"required": ["url", "path"]
})
}
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String> {
if !ctx.internet_mode.can_download() {
anyhow::bail!("download requires internet mode Full, current mode: {:?}", ctx.internet_mode);
}
let url = args.get("url")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: url"))?
.to_string();
let rel = args.get("path")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: path"))?
.to_string();
let path: PathBuf = resolve_path(&ctx.workspaces, &rel)?;
crate::app::catastrophic::CatastrophicGuard::check_download_path(&path)
.map_err(|e| anyhow!("download blocked: {}", e))?;
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.map_err(|e| anyhow!("failed to create parent directories: {}", e))?;
}
let client = reqwest::blocking::Client::builder()
.timeout(Duration::from_secs(120))
.user_agent("ZedSex/1.0")
.build()
.map_err(|e| anyhow!("failed to create HTTP client: {}", e))?;
let response = client.get(&url)
.send()
.map_err(|e| anyhow!("failed to download '{}': {}", url, e))?;
let status = response.status();
if !status.is_success() {
anyhow::bail!("download '{}' returned HTTP {}", url, status.as_u16());
}
let total: u64 = response.content_length().unwrap_or(0);
let mut file = fs::File::create(&path)
.map_err(|e| anyhow!("failed to create file '{}': {}", rel, e))?;
let mut content = response;
let written = copy(&mut content, &mut file)
.map_err(|e| anyhow!("failed to write to '{}': {}", rel, e))?;
Ok(format!("downloaded {} of {} bytes to {}", written, total, rel))
}
}
-78
View File
@@ -1,78 +0,0 @@
use std::time::Duration;
use serde_json::{json, Value};
use anyhow::{Result, anyhow};
use super::super::Tool;
use super::super::ToolCtx;
pub struct Fetch;
impl Tool for Fetch {
fn name(&self) -> &'static str {
"fetch"
}
fn description(&self) -> &'static str {
"Fetch a URL and convert the HTML content to markdown"
}
fn parameters(&self) -> Value {
json!({
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "URL to fetch"
}
},
"required": ["url"]
})
}
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String> {
if !ctx.internet_mode.can_fetch() {
anyhow::bail!("fetch requires internet mode Full, current mode: {:?}", ctx.internet_mode);
}
let url = args.get("url")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: url"))?
.to_string();
let client = reqwest::blocking::Client::builder()
.timeout(Duration::from_secs(30))
.user_agent("ZedSex/1.0")
.build()
.map_err(|e| anyhow!("failed to create HTTP client: {}", e))?;
let response = client.get(&url)
.send()
.map_err(|e| anyhow!("failed to fetch '{}': {}", url, e))?;
let status = response.status();
if !status.is_success() {
anyhow::bail!("fetch '{}' returned HTTP {}", url, status.as_u16());
}
let content_type = response.headers()
.get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.unwrap_or("")
.to_string();
let body = response.text()
.map_err(|e| anyhow!("failed to read response body: {}", e))?;
if content_type.contains("text/html") || content_type.contains("application/xhtml") || content_type.is_empty() {
let markdown = html_to_markdown(&body)?;
Ok(markdown)
} else {
let preview = body.chars().take(2000).collect::<String>();
Ok(format!("Content-Type: {}\n\n{}", content_type, preview))
}
}
}
pub(crate) fn html_to_markdown(html: &str) -> Result<String> {
let frag = scraper::Html::parse_document(html);
let sel = scraper::Selector::parse("body")
.map_err(|e| anyhow!("failed to parse selector: {}", e))?;
let body = frag.select(&sel).next()
.map(|e| e.inner_html())
.unwrap_or_else(|| html.to_string());
let text = scraper::Html::parse_fragment(&body);
let result: String = text.root_element().text().collect::<Vec<_>>().join("\n");
Ok(result)
}
-3
View File
@@ -1,3 +0,0 @@
pub mod download;
pub mod fetch;
pub mod search;
-242
View File
@@ -1,242 +0,0 @@
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use anyhow::{Result, anyhow};
use super::super::Tool;
use super::super::ToolCtx;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum SearchProvider {
Tavily,
Brave,
SerpApi,
Google,
}
impl SearchProvider {
pub fn from_str(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"tavily" => Some(SearchProvider::Tavily),
"brave" => Some(SearchProvider::Brave),
"serpapi" | "serp_api" => Some(SearchProvider::SerpApi),
"google" => Some(SearchProvider::Google),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResult {
pub title: String,
pub url: String,
pub snippet: String,
}
pub struct Search;
impl Tool for Search {
fn name(&self) -> &'static str {
"web_search"
}
fn description(&self) -> &'static str {
"Search the web for information using a configured search provider (Tavily, Brave, SerpAPI, or Google)."
}
fn parameters(&self) -> Value {
json!({
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query"
},
"num_results": {
"type": "integer",
"description": "Number of results to return (default: 5)",
"default": 5
}
},
"required": ["query"]
})
}
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String> {
if !ctx.internet_mode.can_search() {
anyhow::bail!("web_search requires internet mode Full, current mode: {:?}", ctx.internet_mode);
}
let query = args.get("query")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: query"))?
.to_string();
let num_results = args.get("num_results")
.and_then(|v| v.as_u64())
.unwrap_or(5) as usize;
let provider = detect_search_provider();
match provider {
Some(p) => search_with_provider(&p, &query, num_results),
None => Ok(format!(
"No search provider configured for query '{}'.\n\
Set ZESDEX_SEARCH_PROVIDER and corresponding API key env vars.\n\
Supported: tavily (ZESDEX_TAVILY_API_KEY), \
brave (ZESDEX_BRAVE_API_KEY), \
serpapi (ZESDEX_SERPAPI_KEY), \
google (ZESDEX_GOOGLE_API_KEY).",
query
)),
}
}
}
fn detect_search_provider() -> Option<SearchProvider> {
if std::env::var("ZESDEX_SEARCH_PROVIDER").ok().is_some() {
let provider_str = std::env::var("ZESDEX_SEARCH_PROVIDER").unwrap_or_default();
if let Some(p) = SearchProvider::from_str(&provider_str) {
return Some(p);
}
}
if std::env::var("ZESDEX_TAVILY_API_KEY").ok().filter(|k| !k.is_empty()).is_some() {
return Some(SearchProvider::Tavily);
}
if std::env::var("ZESDEX_BRAVE_API_KEY").ok().filter(|k| !k.is_empty()).is_some() {
return Some(SearchProvider::Brave);
}
if std::env::var("ZESDEX_SERPAPI_KEY").ok().filter(|k| !k.is_empty()).is_some() {
return Some(SearchProvider::SerpApi);
}
if std::env::var("ZESDEX_GOOGLE_API_KEY").ok().filter(|k| !k.is_empty()).is_some() {
return Some(SearchProvider::Google);
}
None
}
fn search_with_provider(provider: &SearchProvider, query: &str, num_results: usize) -> Result<String> {
let results = match provider {
SearchProvider::Tavily => search_tavily(query, num_results)?,
SearchProvider::Brave => search_brave(query, num_results)?,
SearchProvider::SerpApi => search_serpapi(query, num_results)?,
SearchProvider::Google => search_google(query, num_results)?,
};
if results.is_empty() {
return Ok(format!("No results found for '{}'.", query));
}
let mut output = format!("Search results for '{}':\n\n", query);
for (i, r) in results.iter().enumerate() {
output.push_str(&format!("{}. {}\n {}\n {}\n\n", i + 1, r.title, r.url, r.snippet));
}
Ok(output)
}
fn search_tavily(query: &str, num_results: usize) -> Result<Vec<SearchResult>> {
let api_key = std::env::var("ZESDEX_TAVILY_API_KEY")
.map_err(|_| anyhow!("ZESDEX_TAVILY_API_KEY not set"))?;
let client = reqwest::blocking::Client::builder()
.timeout(std::time::Duration::from_secs(15))
.build()?;
let body = json!({
"api_key": api_key,
"query": query,
"max_results": num_results,
"include_answer": false,
"search_depth": "basic",
});
let resp = client.post("https://api.tavily.com/search")
.header("Content-Type", "application/json")
.json(&body)
.send()?;
if !resp.status().is_success() {
anyhow::bail!("Tavily API error: {}", resp.status());
}
let data: Value = resp.json()?;
let results = data["results"].as_array().cloned().unwrap_or_default();
Ok(results.iter().filter_map(|r| {
Some(SearchResult {
title: r["title"].as_str()?.to_string(),
url: r["url"].as_str()?.to_string(),
snippet: r["content"].as_str().unwrap_or("").to_string(),
})
}).collect())
}
fn search_brave(query: &str, num_results: usize) -> Result<Vec<SearchResult>> {
let api_key = std::env::var("ZESDEX_BRAVE_API_KEY")
.map_err(|_| anyhow!("ZESDEX_BRAVE_API_KEY not set"))?;
let client = reqwest::blocking::Client::builder()
.timeout(std::time::Duration::from_secs(15))
.build()?;
let resp = client.get("https://api.search.brave.com/res/v1/web/search")
.header("Accept", "application/json")
.header("Accept-Encoding", "gzip")
.header("X-Subscription-Token", &api_key)
.query(&[("q", query), ("count", &num_results.to_string())])
.send()?;
if !resp.status().is_success() {
anyhow::bail!("Brave API error: {}", resp.status());
}
let data: Value = resp.json()?;
let results = data["web"]["results"].as_array().cloned().unwrap_or_default();
Ok(results.iter().filter_map(|r| {
Some(SearchResult {
title: r["title"].as_str()?.to_string(),
url: r["url"].as_str()?.to_string(),
snippet: r["description"].as_str().unwrap_or("").to_string(),
})
}).collect())
}
fn search_serpapi(query: &str, num_results: usize) -> Result<Vec<SearchResult>> {
let api_key = std::env::var("ZESDEX_SERPAPI_KEY")
.map_err(|_| anyhow!("ZESDEX_SERPAPI_KEY not set"))?;
let client = reqwest::blocking::Client::builder()
.timeout(std::time::Duration::from_secs(15))
.build()?;
let resp = client.get("https://serpapi.com/search.json")
.query(&[
("q", query),
("api_key", &api_key),
("engine", "google"),
("num", &num_results.to_string()),
])
.send()?;
if !resp.status().is_success() {
anyhow::bail!("SerpAPI error: {}", resp.status());
}
let data: Value = resp.json()?;
let results = data["organic_results"].as_array().cloned().unwrap_or_default();
Ok(results.iter().filter_map(|r| {
let title = r.get("title")?.as_str()?.to_string();
let url = r.get("link")?.as_str()?.to_string();
let snippet = r.get("snippet").and_then(|s| s.as_str()).unwrap_or("").to_string();
Some(SearchResult { title, url, snippet })
}).collect())
}
fn search_google(query: &str, num_results: usize) -> Result<Vec<SearchResult>> {
let api_key = std::env::var("ZESDEX_GOOGLE_API_KEY")
.map_err(|_| anyhow!("ZESDEX_GOOGLE_API_KEY not set"))?;
let cx = std::env::var("ZESDEX_GOOGLE_CX")
.map_err(|_| anyhow!("ZESDEX_GOOGLE_CX (Custom Search Engine ID) not set"))?;
let client = reqwest::blocking::Client::builder()
.timeout(std::time::Duration::from_secs(15))
.build()?;
let resp = client.get("https://www.googleapis.com/customsearch/v1")
.query(&[
("q", query),
("key", &api_key),
("cx", &cx),
("num", &num_results.min(10).to_string()),
])
.send()?;
if !resp.status().is_success() {
anyhow::bail!("Google Custom Search API error: {}", resp.status());
}
let data: Value = resp.json()?;
let results = data["items"].as_array().cloned().unwrap_or_default();
Ok(results.iter().filter_map(|r| {
let title = r.get("title")?.as_str()?.to_string();
let url = r.get("link")?.as_str()?.to_string();
let snippet = r.get("snippet").and_then(|s| s.as_str()).unwrap_or("").to_string();
Some(SearchResult { title, url, snippet })
}).collect())
}
-8
View File
@@ -7,7 +7,6 @@ pub mod fs;
pub mod git_cred;
pub mod git_operator;
pub mod git_worktree;
pub mod internet;
pub mod memory;
pub mod plan;
pub mod search;
@@ -39,7 +38,6 @@ pub struct ToolCtx {
pub _download_dir: PathBuf,
pub worktrees_dir: PathBuf,
pub dir_cache: std::sync::Arc<tokio::sync::RwLock<super::app::state::misc::DirCache>>,
pub internet_mode: super::model::settings::InternetMode,
pub origin: crate::app::state::types::Origin,
pub graduated_checks: Vec<GraduatedCheck>,
}
@@ -67,7 +65,6 @@ pub struct ToolCtxBuilder {
pub download_dir: PathBuf,
pub worktrees_dir: PathBuf,
pub dir_cache: std::sync::Arc<tokio::sync::RwLock<super::app::state::misc::DirCache>>,
pub internet_mode: super::model::settings::InternetMode,
pub origin: crate::app::state::types::Origin,
pub graduated_checks: Vec<GraduatedCheck>,
}
@@ -81,7 +78,6 @@ impl Default for ToolCtxBuilder {
download_dir: PathBuf::new(),
worktrees_dir: PathBuf::new(),
dir_cache: std::sync::Arc::new(tokio::sync::RwLock::new(super::app::state::misc::DirCache::new())),
internet_mode: super::model::settings::InternetMode::Off,
origin: crate::app::state::types::Origin::Main,
graduated_checks: Vec::new(),
}
@@ -99,7 +95,6 @@ impl ToolCtxBuilder {
_download_dir: self.download_dir,
worktrees_dir: self.worktrees_dir,
dir_cache: self.dir_cache,
internet_mode: self.internet_mode,
origin: self.origin,
graduated_checks: self.graduated_checks,
}
@@ -125,9 +120,6 @@ pub fn all_tools() -> Vec<Box<dyn Tool>> {
Box::new(super::tool::plan::PlanReady),
Box::new(super::tool::workflow::WorkflowRun),
Box::new(super::tool::workflow::NoteFinding),
Box::new(super::tool::internet::fetch::Fetch),
Box::new(super::tool::internet::download::Download),
Box::new(super::tool::internet::search::Search),
Box::new(super::tool::memory::remember::Remember),
Box::new(super::tool::memory::forget::Forget),
Box::new(super::tool::memory::recall::Recall),
+2 -5
View File
@@ -13,7 +13,7 @@ impl Tool for Bash {
}
fn description(&self) -> &'static str {
"Execute a shell command via bash -c with catastrophic guard protection"
"Execute a shell command via bash -c"
}
fn parameters(&self) -> Value {
@@ -41,16 +41,13 @@ impl Tool for Bash {
})
}
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String> {
fn run(&self, _ctx: &ToolCtx, args: &Value) -> Result<String> {
let cmd = args.get("command")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: command"))?
.to_string();
let _description = args.get("description").and_then(|v| v.as_str()).unwrap_or("");
let timeout_ms = args.get("timeout").and_then(|v| v.as_u64()).unwrap_or(120000).min(600000);
let workspace_roots: Vec<&std::path::Path> = ctx.workspaces.iter().map(|p| p.as_path()).collect();
crate::app::catastrophic::CatastrophicGuard::check_all(&cmd, &workspace_roots)
.map_err(|e| anyhow!("catastrophic guard blocked: {}", e))?;
super::shell_filter::credentials::check_credential_read(&cmd)
.map_err(|e| anyhow!("blocked: {}", e))?;
super::shell_filter::git::check_git_destructive(&cmd)