diff --git a/Pentest_Tools_zh.md b/Pentest_Tools_zh.md new file mode 100644 index 000000000..51db4b4f4 --- /dev/null +++ b/Pentest_Tools_zh.md @@ -0,0 +1,168 @@ +[ + { + "description": "网络发现和安全审计工具。执行主机发现、端口扫描、版本检测和基于脚本的漏洞扫描。是初始攻击面映射的必备工具。输出包括开放端口、服务版本和操作系统检测。注意:激进扫描(-A)可能触发入侵检测系统警报。建议使用时序模板(-T4)以平衡速度和隐蔽性。", + "name": "nmap", + "parameters": { + "properties": { + "target": { + "description": "IP地址、CIDR范围或域名。格式:'192.168.1.1'、'10.0.0.0/24' 或 'example.com'", + "type": "string" + }, + "options": { + "description": "扫描参数:'-sV'(服务检测)、'-O'(操作系统指纹识别)、'-p-'(全端口)。推荐:'-T4 -sV --open'", + "type": "string" + } + }, + "type": "object" + } + }, + { + "description": "高级Web路径暴力破解工具。通过字典攻击发现隐藏的目录、文件和参数。非常适合发现管理面板、备份文件和API端点。输出列出可访问路径及其状态码。注意:可能产生大量流量;使用'-t 20'限制线程数。始终排除破坏性模式如'/delete'或'/reset'。", + "name": "dirsearch", + "parameters": { + "properties": { + "url": { + "description": "包含协议的基础URL:'http://target.com' 或 'https://192.168.1.1:8443'", + "type": "string" + }, + "extensions": { + "description": "逗号分隔的文件扩展名:'php,aspx' 或 'all'表示常见扩展名", + "type": "string" + } + }, + "type": "object" + } + }, + { + "description": "自动化SQL注入检测和利用框架。支持所有SQL注入技术,包括基于布尔值、基于时间和带外攻击。输出包括漏洞参数、提取的数据和Shell访问。使用'--batch'进行非交互模式,'--level'控制测试深度。", + "name": "sqlmap", + "parameters": { + "properties": { + "url": { + "description": "包含注入点的漏洞URL:'http://site.com/page?id=1'", + "type": "string" + }, + "risk_level": { + "description": "风险容忍度(1-3):1=最少请求,3=重度测试", + "type": "integer" + } + }, + "type": "object" + } + }, + { + "description": "互联网连接设备智能引擎。查询包含80亿+设备的全球数据库。侦察必备:识别暴露的服务、漏洞和错误配置。输出包括IP、横幅、CVE和地理位置。需要API密钥。注意:结果可能包括关键基础设施。", + "name": "shodan", + "parameters": { + "properties": { + "query": { + "description": "搜索语法:'apache 2.4.39 country:US'、'port:3389 os:windows'", + "type": "string" + }, + "output_fields": { + "description": "响应字段:'ip,port,org,hostnames,vulns'", + "type": "string" + } + }, + "type": "object" + } + }, + { + "description": "网络安全资产搜索引擎,拥有30亿+索引记录。专门进行Web组件检测和漏洞映射。输出包括IP、域名、协议和相关CVE。需要API凭据。最适合:发现云资产、暴露的数据库和被遗忘的子域。始终使用'is_honeypot=false'过滤结果。", + "name": "fofa", + "parameters": { + "properties": { + "query": { + "description": "FOFA语法:'domain=\"example.com\" && status_code=\"200\"'、'app=\"Docker\"'", + "type": "string" + }, + "size": { + "description": "结果限制(1-1000)。默认:100", + "type": "integer" + } + }, + "type": "object" + } + }, + { + "description": "快速子域发现工具,利用30多个公共资源(DNS、Censys、VirusTotal)。被动侦察,网络足迹最小。输出列出活动子域及解析数据。非常适合扩展攻击面。注意:可能遗漏云原生资产;结合云提供商API以获得完整覆盖。", + "name": "subfinder", + "parameters": { + "properties": { + "domain": { + "description": "目标域名:'example.com'", + "type": "string" + }, + "sources": { + "description": "数据源:'all'、'censys,securityTrails'", + "type": "string" + } + }, + "type": "object" + } + }, + { + "description": "企业级漏洞扫描器,具有7万多项检查。识别配置缺陷、缺失补丁和CVE。输出提供风险评分、修复步骤和漏洞利用参考。需要许可证激活。关键:完整扫描可能需要数小时;从'基本网络扫描'模板开始。始终安排在非高峰时段。", + "name": "nessus", + "parameters": { + "properties": { + "target": { + "description": "IP范围或域名:'192.168.1.1-254' 或 'example.com'", + "type": "string" + }, + "policy": { + "description": "扫描模板:'basic'、'web'、'malware'", + "type": "string" + } + }, + "type": "object" + } + }, + { + "description": "全面的Web服务器扫描器,检测6000多种风险,包括错误配置、过期软件和XSS向量。是完整漏洞评估扫描器的轻量级替代品。输出突出显示关键发现和OWASP参考。使用'-Tuning x'过滤检查项目。", + "name": "nikto", + "parameters": { + "properties": { + "host": { + "description": "目标URL:'http://target.com' 或 'https://10.0.0.1:443'", + "type": "string" + }, + "ssl": { + "description": "强制SSL:对于HTTPS默认端口设为true", + "type": "boolean" + } + }, + "type": "object" + } + }, + { + "description": "使用YAML模板的高速漏洞扫描器(5000多个社区驱动模板)。检测CVE、错误配置和暴露面板。输出包括严重性评级和概念证明请求。最适合:云原生环境和CI/CD管道。关键:使用'nuclei -update-templates'每周更新模板。优先处理'critical'严重性检查。", + "name": "nuclei", + "parameters": { + "properties": { + "target": { + "description": "URL、IP或列表文件:'http://target.com' 或 '@targets.txt'", + "type": "string" + }, + "templates": { + "description": "模板类别:'-t cves/'、'-t exposures/configs'", + "type": "string" + } + }, + "type": "object" + } + }, + { + "description": "Metasploit框架控制台,用于授权渗透测试。提供2000多个模块进行载荷传递、权限提升和持久化。输出包括会话ID和后渗透数据。警告:高风险操作。在利用前始终使用'check'命令。", + "name": "msfconsole", + "parameters": { + "properties": { + "resource_file": { + "description": "RC脚本:'use exploit/windows/smb/ms17_010_eternalblue; set RHOSTS 192.168.1.10; run'", + "type": "string" + } + }, + "type": "object" + } + } +] \ No newline at end of file diff --git a/security-tester.claudia.json b/security-tester.claudia.json new file mode 100644 index 000000000..6ef714643 --- /dev/null +++ b/security-tester.claudia.json @@ -0,0 +1,11 @@ +{ + "version": 1, + "exported_at": "2025-01-23T16:00:00.000Z", + "agent": { + "name": "Security Tester", + "icon": "shield", + "model": "opus", + "system_prompt": "# 安全测试专家 Agent\n\n你是一名专业的网络安全测试专家,专门负责执行授权的安全评估和漏洞扫描。你具备以下能力和工具:\n\n## 🛡️ 专业能力\n\n### 核心技能\n- 网络安全评估和渗透测试\n- 漏洞识别、分析和风险评估 \n- 安全合规性检查和审计\n- 网络拓扑发现和资产清点\n- Web应用安全测试\n- 安全报告撰写和建议提供\n\n### 可用安全工具\n- **nmap**: 网络发现、端口扫描、服务版本检测\n- **dirsearch**: Web目录和文件发现(规划中)\n- **nikto**: Web服务器漏洞扫描(规划中)\n- **nuclei**: 基于模板的漏洞检测(规划中)\n- **sqlmap**: SQL注入检测和利用(规划中)\n\n## 🔒 安全原则\n\n### 合规要求\n1. **仅进行授权测试** - 始终确认已获得明确的书面授权\n2. **遵守法律法规** - 严格遵守当地网络安全法律法规\n3. **最小化影响** - 使用最温和的测试方法,避免对生产系统造成影响\n4. **保护隐私** - 不收集、存储或泄露敏感信息\n5. **负责任披露** - 发现漏洞后负责任地向相关方报告\n\n### 安全约束\n- ✅ 允许:私有IP范围扫描(10.x.x.x, 172.16-31.x.x, 192.168.x.x, 127.x.x.x)\n- ✅ 允许:本地网络和测试环境\n- ❌ 禁止:未授权的外部网络扫描\n- ❌ 禁止:生产环境的破坏性测试\n- ❌ 禁止:恶意利用发现的漏洞\n\n## 📋 工作流程\n\n### 1. 需求确认\n在开始任何测试前,先确认:\n- 测试范围和目标\n- 授权状态和合规性\n- 测试类型和深度\n- 可接受的风险级别\n\n### 2. 信息收集\n- 使用nmap进行网络发现和端口扫描\n- 识别运行的服务和版本信息\n- 绘制网络拓扑和资产清单\n\n### 3. 漏洞扫描\n- 根据发现的服务选择合适的扫描工具\n- 执行漏洞检测和风险评估\n- 验证潜在的安全问题\n\n### 4. 结果分析\n- 分析扫描结果的准确性\n- 评估漏洞的严重性和影响\n- 提供修复建议和最佳实践\n\n### 5. 报告生成\n- 生成详细的安全评估报告\n- 按优先级列出发现的问题\n- 提供具体的修复步骤和建议\n\n## 🎯 使用示例\n\n### 网络发现\n```javascript\n// 发现内网活跃主机\nawait api.executeNmap(\"192.168.1.0/24\", \"-sn\");\n\n// 扫描特定主机的开放端口\nawait api.executeNmap(\"192.168.1.100\", \"-sV -T4 --open\");\n```\n\n### 服务识别\n```javascript\n// 详细的服务版本检测\nawait api.executeNmap(\"10.0.0.1\", \"-sV -sC -O\");\n\n// 快速端口扫描\nawait api.executeNmap(\"127.0.0.1\", \"--top-ports 1000\");\n```\n\n## ⚠️ 重要提醒\n\n每次执行安全测试前,你必须:\n\n1. **确认授权** - 明确告知用户需要适当的授权\n2. **评估风险** - 说明可能的影响和风险\n3. **设置边界** - 明确测试范围和限制\n4. **记录过程** - 详细记录测试过程和发现\n\n你的目标是帮助组织提高安全态势,发现和修复潜在的安全风险,同时始终保持专业、负责任和合规的态度。\n\n记住:安全测试是为了保护,而不是破坏。始终以建设性的方式进行工作,并将发现的问题用于改善整体安全状况。", + "default_task": "执行网络安全评估和漏洞扫描" + } +} \ No newline at end of file diff --git a/security_agent_prompt.md b/security_agent_prompt.md new file mode 100644 index 000000000..91de9a648 --- /dev/null +++ b/security_agent_prompt.md @@ -0,0 +1,98 @@ +# 安全测试专家 Agent 系统提示词 + +你是一名专业的网络安全测试专家,专门负责执行授权的安全评估和漏洞扫描。你具备以下能力和工具: + +## 🛡️ 专业能力 + +### 核心技能 +- 网络安全评估和渗透测试 +- 漏洞识别、分析和风险评估 +- 安全合规性检查和审计 +- 网络拓扑发现和资产清点 +- Web应用安全测试 +- 安全报告撰写和建议提供 + +### 可用安全工具 +- **nmap**: 网络发现、端口扫描、服务版本检测 +- **dirsearch**: Web目录和文件发现(规划中) +- **nikto**: Web服务器漏洞扫描(规划中) +- **nuclei**: 基于模板的漏洞检测(规划中) +- **sqlmap**: SQL注入检测和利用(规划中) + +## 🔒 安全原则 + +### 合规要求 +1. **仅进行授权测试** - 始终确认已获得明确的书面授权 +2. **遵守法律法规** - 严格遵守当地网络安全法律法规 +3. **最小化影响** - 使用最温和的测试方法,避免对生产系统造成影响 +4. **保护隐私** - 不收集、存储或泄露敏感信息 +5. **负责任披露** - 发现漏洞后负责任地向相关方报告 + +### 安全约束 +- ✅ 允许:私有IP范围扫描(10.x.x.x, 172.16-31.x.x, 192.168.x.x, 127.x.x.x) +- ✅ 允许:本地网络和测试环境 +- ❌ 禁止:未授权的外部网络扫描 +- ❌ 禁止:生产环境的破坏性测试 +- ❌ 禁止:恶意利用发现的漏洞 + +## 📋 工作流程 + +### 1. 需求确认 +在开始任何测试前,先确认: +- 测试范围和目标 +- 授权状态和合规性 +- 测试类型和深度 +- 可接受的风险级别 + +### 2. 信息收集 +- 使用nmap进行网络发现和端口扫描 +- 识别运行的服务和版本信息 +- 绘制网络拓扑和资产清单 + +### 3. 漏洞扫描 +- 根据发现的服务选择合适的扫描工具 +- 执行漏洞检测和风险评估 +- 验证潜在的安全问题 + +### 4. 结果分析 +- 分析扫描结果的准确性 +- 评估漏洞的严重性和影响 +- 提供修复建议和最佳实践 + +### 5. 报告生成 +- 生成详细的安全评估报告 +- 按优先级列出发现的问题 +- 提供具体的修复步骤和建议 + +## 🎯 使用示例 + +### 网络发现 +```javascript +// 发现内网活跃主机 +await api.executeNmap("192.168.1.0/24", "-sn"); + +// 扫描特定主机的开放端口 +await api.executeNmap("192.168.1.100", "-sV -T4 --open"); +``` + +### 服务识别 +```javascript +// 详细的服务版本检测 +await api.executeNmap("10.0.0.1", "-sV -sC -O"); + +// 快速端口扫描 +await api.executeNmap("127.0.0.1", "--top-ports 1000"); +``` + +## ⚠️ 重要提醒 + +每次执行安全测试前,你必须: + +1. **确认授权** - 明确告知用户需要适当的授权 +2. **评估风险** - 说明可能的影响和风险 +3. **设置边界** - 明确测试范围和限制 +4. **记录过程** - 详细记录测试过程和发现 + +你的目标是帮助组织提高安全态势,发现和修复潜在的安全风险,同时始终保持专业、负责任和合规的态度。 + +记住:安全测试是为了保护,而不是破坏。始终以建设性的方式进行工作,并将发现的问题用于改善整体安全状况。 \ No newline at end of file diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico index 2d446c630..7466b1b00 100644 Binary files a/src-tauri/icons/icon.ico and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/src/claude_binary.rs b/src-tauri/src/claude_binary.rs index f5b8a852e..7af9559a3 100644 --- a/src-tauri/src/claude_binary.rs +++ b/src-tauri/src/claude_binary.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use std::cmp::Ordering; /// Shared module for detecting Claude Code binary installations /// Supports NVM installations, aliased paths, and version-based selection +/// Now includes Windows support use std::path::PathBuf; use std::process::Command; use tauri::Manager; @@ -51,6 +52,10 @@ pub fn find_claude_binary(app_handle: &tauri::AppHandle) -> Result Vec { installations } -/// Try using the 'which' command to find Claude +/// Try using the 'which' command (Unix) or 'where' command (Windows) to find Claude fn try_which_command() -> Option { - debug!("Trying 'which claude' to find binary..."); + #[cfg(windows)] + { + debug!("Trying 'where claude' to find binary..."); + match Command::new("where").arg("claude").output() { + Ok(output) if output.status.success() => { + let output_str = String::from_utf8_lossy(&output.stdout).trim().to_string(); + + if output_str.is_empty() { + return None; + } + + // Windows 'where' command returns full paths, one per line + let path = output_str.lines().next()?.to_string(); + + debug!("'where' found claude at: {}", path); - match Command::new("which").arg("claude").output() { - Ok(output) if output.status.success() => { - let output_str = String::from_utf8_lossy(&output.stdout).trim().to_string(); + // Verify the path exists + if !PathBuf::from(&path).exists() { + warn!("Path from 'where' does not exist: {}", path); + return None; + } - if output_str.is_empty() { - return None; + // Get version + let version = get_claude_version(&path).ok().flatten(); + + Some(ClaudeInstallation { + path, + version, + source: "where".to_string(), + }) } + _ => None, + } + } - // Parse aliased output: "claude: aliased to /path/to/claude" - let path = if output_str.starts_with("claude:") && output_str.contains("aliased to") { - output_str - .split("aliased to") - .nth(1) - .map(|s| s.trim().to_string()) - } else { - Some(output_str) - }?; + #[cfg(not(windows))] + { + debug!("Trying 'which claude' to find binary..."); - debug!("'which' found claude at: {}", path); + match Command::new("which").arg("claude").output() { + Ok(output) if output.status.success() => { + let output_str = String::from_utf8_lossy(&output.stdout).trim().to_string(); - // Verify the path exists - if !PathBuf::from(&path).exists() { - warn!("Path from 'which' does not exist: {}", path); - return None; - } + if output_str.is_empty() { + return None; + } - // Get version - let version = get_claude_version(&path).ok().flatten(); + // Parse aliased output: "claude: aliased to /path/to/claude" + let path = if output_str.starts_with("claude:") && output_str.contains("aliased to") { + output_str + .split("aliased to") + .nth(1) + .map(|s| s.trim().to_string()) + } else { + Some(output_str) + }?; - Some(ClaudeInstallation { - path, - version, - source: "which".to_string(), - }) + debug!("'which' found claude at: {}", path); + + // Verify the path exists + if !PathBuf::from(&path).exists() { + warn!("Path from 'which' does not exist: {}", path); + return None; + } + + // Get version + let version = get_claude_version(&path).ok().flatten(); + + Some(ClaudeInstallation { + path, + version, + source: "which".to_string(), + }) + } + _ => None, } - _ => None, } } @@ -189,33 +232,104 @@ fn try_which_command() -> Option { fn find_nvm_installations() -> Vec { let mut installations = Vec::new(); - if let Ok(home) = std::env::var("HOME") { - let nvm_dir = PathBuf::from(&home) - .join(".nvm") - .join("versions") - .join("node"); + #[cfg(windows)] + { + // Check Windows NVM paths + if let Ok(userprofile) = std::env::var("USERPROFILE") { + // NVM for Windows uses different paths + let nvm_dir = PathBuf::from(&userprofile).join("AppData").join("Roaming").join("nvm"); + + debug!("Checking Windows NVM directory: {:?}", nvm_dir); + + if let Ok(entries) = std::fs::read_dir(&nvm_dir) { + for entry in entries.flatten() { + if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) { + let node_version = entry.file_name().to_string_lossy().to_string(); + if node_version.starts_with('v') || node_version.chars().next().unwrap_or('x').is_numeric() { + let claude_path = entry.path().join("claude.exe"); - debug!("Checking NVM directory: {:?}", nvm_dir); + if claude_path.exists() && claude_path.is_file() { + let path_str = claude_path.to_string_lossy().to_string(); - if let Ok(entries) = std::fs::read_dir(&nvm_dir) { - for entry in entries.flatten() { - if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) { - let claude_path = entry.path().join("bin").join("claude"); + debug!("Found Claude in Windows NVM node {}: {}", node_version, path_str); - if claude_path.exists() && claude_path.is_file() { - let path_str = claude_path.to_string_lossy().to_string(); - let node_version = entry.file_name().to_string_lossy().to_string(); + // Get Claude version + let version = get_claude_version(&path_str).ok().flatten(); - debug!("Found Claude in NVM node {}: {}", node_version, path_str); + installations.push(ClaudeInstallation { + path: path_str, + version, + source: format!("nvm-windows ({})", node_version), + }); + } + } + } + } + } + + // Also check AppData\Local\nvs for nvs (Node Version Switcher) + let nvs_dir = PathBuf::from(&userprofile).join("AppData").join("Local").join("nvs"); + + if nvs_dir.exists() { + debug!("Checking Windows NVS directory: {:?}", nvs_dir); + + if let Ok(entries) = std::fs::read_dir(&nvs_dir) { + for entry in entries.flatten() { + if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) { + let version_dir = entry.path(); + let claude_path = version_dir.join("x64").join("claude.exe"); + + if claude_path.exists() && claude_path.is_file() { + let path_str = claude_path.to_string_lossy().to_string(); + let node_version = entry.file_name().to_string_lossy().to_string(); + + debug!("Found Claude in NVS node {}: {}", node_version, path_str); + + let version = get_claude_version(&path_str).ok().flatten(); + + installations.push(ClaudeInstallation { + path: path_str, + version, + source: format!("nvs ({})", node_version), + }); + } + } + } + } + } + } + } + + #[cfg(not(windows))] + { + if let Ok(home) = std::env::var("HOME") { + let nvm_dir = PathBuf::from(&home) + .join(".nvm") + .join("versions") + .join("node"); + + debug!("Checking NVM directory: {:?}", nvm_dir); - // Get Claude version - let version = get_claude_version(&path_str).ok().flatten(); + if let Ok(entries) = std::fs::read_dir(&nvm_dir) { + for entry in entries.flatten() { + if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) { + let claude_path = entry.path().join("bin").join("claude"); - installations.push(ClaudeInstallation { - path: path_str, - version, - source: format!("nvm ({})", node_version), - }); + if claude_path.exists() && claude_path.is_file() { + let path_str = claude_path.to_string_lossy().to_string(); + let node_version = entry.file_name().to_string_lossy().to_string(); + + debug!("Found Claude in NVM node {}: {}", node_version, path_str); + + // Get Claude version + let version = get_claude_version(&path_str).ok().flatten(); + + installations.push(ClaudeInstallation { + path: path_str, + version, + source: format!("nvm ({})", node_version), + }); + } } } } @@ -229,75 +343,176 @@ fn find_nvm_installations() -> Vec { fn find_standard_installations() -> Vec { let mut installations = Vec::new(); - // Common installation paths for claude - let mut paths_to_check: Vec<(String, String)> = vec![ - ("/usr/local/bin/claude".to_string(), "system".to_string()), - ( - "/opt/homebrew/bin/claude".to_string(), - "homebrew".to_string(), - ), - ("/usr/bin/claude".to_string(), "system".to_string()), - ("/bin/claude".to_string(), "system".to_string()), - ]; - - // Also check user-specific paths - if let Ok(home) = std::env::var("HOME") { - paths_to_check.extend(vec![ - ( - format!("{}/.claude/local/claude", home), - "claude-local".to_string(), - ), - ( - format!("{}/.local/bin/claude", home), - "local-bin".to_string(), - ), - ( - format!("{}/.npm-global/bin/claude", home), - "npm-global".to_string(), - ), - (format!("{}/.yarn/bin/claude", home), "yarn".to_string()), - (format!("{}/.bun/bin/claude", home), "bun".to_string()), - (format!("{}/bin/claude", home), "home-bin".to_string()), - // Check common node_modules locations - ( - format!("{}/node_modules/.bin/claude", home), - "node-modules".to_string(), - ), + #[cfg(windows)] + { + // Windows-specific paths + let mut paths_to_check: Vec<(String, String)> = vec![ + ("C:\\Program Files\\nodejs\\claude.exe".to_string(), "nodejs-global".to_string()), + ("C:\\Program Files (x86)\\nodejs\\claude.exe".to_string(), "nodejs-global-x86".to_string()), + ]; + + // Check user-specific paths + if let Ok(userprofile) = std::env::var("USERPROFILE") { + paths_to_check.extend(vec![ + ( + format!("{}\\AppData\\Roaming\\npm\\claude.exe", userprofile), + "npm-global".to_string(), + ), + ( + format!("{}\\AppData\\Roaming\\npm\\claude.cmd", userprofile), + "npm-global-cmd".to_string(), + ), + ( + format!("{}\\.claude\\local\\claude.exe", userprofile), + "claude-local".to_string(), + ), + ( + format!("{}\\.local\\bin\\claude.exe", userprofile), + "local-bin".to_string(), + ), + ( + format!("{}\\scoop\\apps\\nodejs\\current\\claude.exe", userprofile), + "scoop".to_string(), + ), + ( + format!("{}\\scoop\\shims\\claude.exe", userprofile), + "scoop-shims".to_string(), + ), + ]); + } + + if let Ok(appdata) = std::env::var("APPDATA") { + paths_to_check.extend(vec![ + ( + format!("{}\\npm\\claude.exe", appdata), + "appdata-npm".to_string(), + ), + ( + format!("{}\\npm\\claude.cmd", appdata), + "appdata-npm-cmd".to_string(), + ), + ]); + } + + // Check Program Files + if let Ok(programfiles) = std::env::var("ProgramFiles") { + paths_to_check.push(( + format!("{}\\nodejs\\claude.exe", programfiles), + "programfiles-nodejs".to_string(), + )); + } + + if let Ok(programfiles_x86) = std::env::var("ProgramFiles(x86)") { + paths_to_check.push(( + format!("{}\\nodejs\\claude.exe", programfiles_x86), + "programfiles-x86-nodejs".to_string(), + )); + } + + // Check each path + for (path, source) in paths_to_check { + let path_buf = PathBuf::from(&path); + if path_buf.exists() && path_buf.is_file() { + debug!("Found claude at Windows path: {} ({})", path, source); + + // Get version + let version = get_claude_version(&path).ok().flatten(); + + installations.push(ClaudeInstallation { + path, + version, + source, + }); + } + } + + // Also check if claude is available in PATH (without full path) + if let Ok(output) = Command::new("claude").arg("--version").output() { + if output.status.success() { + debug!("claude is available in Windows PATH"); + let version = extract_version_from_output(&output.stdout); + + installations.push(ClaudeInstallation { + path: "claude".to_string(), + version, + source: "PATH".to_string(), + }); + } + } + } + + #[cfg(not(windows))] + { + // Common installation paths for claude on Unix/Linux + let mut paths_to_check: Vec<(String, String)> = vec![ + ("/usr/local/bin/claude".to_string(), "system".to_string()), ( - format!("{}/.config/yarn/global/node_modules/.bin/claude", home), - "yarn-global".to_string(), + "/opt/homebrew/bin/claude".to_string(), + "homebrew".to_string(), ), - ]); - } + ("/usr/bin/claude".to_string(), "system".to_string()), + ("/bin/claude".to_string(), "system".to_string()), + ]; + + // Also check user-specific paths + if let Ok(home) = std::env::var("HOME") { + paths_to_check.extend(vec![ + ( + format!("{}/.claude/local/claude", home), + "claude-local".to_string(), + ), + ( + format!("{}/.local/bin/claude", home), + "local-bin".to_string(), + ), + ( + format!("{}/.npm-global/bin/claude", home), + "npm-global".to_string(), + ), + (format!("{}/.yarn/bin/claude", home), "yarn".to_string()), + (format!("{}/.bun/bin/claude", home), "bun".to_string()), + (format!("{}/bin/claude", home), "home-bin".to_string()), + // Check common node_modules locations + ( + format!("{}/node_modules/.bin/claude", home), + "node-modules".to_string(), + ), + ( + format!("{}/.config/yarn/global/node_modules/.bin/claude", home), + "yarn-global".to_string(), + ), + ]); + } - // Check each path - for (path, source) in paths_to_check { - let path_buf = PathBuf::from(&path); - if path_buf.exists() && path_buf.is_file() { - debug!("Found claude at standard path: {} ({})", path, source); + // Check each path + for (path, source) in paths_to_check { + let path_buf = PathBuf::from(&path); + if path_buf.exists() && path_buf.is_file() { + debug!("Found claude at standard path: {} ({})", path, source); - // Get version - let version = get_claude_version(&path).ok().flatten(); + // Get version + let version = get_claude_version(&path).ok().flatten(); - installations.push(ClaudeInstallation { - path, - version, - source, - }); + installations.push(ClaudeInstallation { + path, + version, + source, + }); + } } - } - // Also check if claude is available in PATH (without full path) - if let Ok(output) = Command::new("claude").arg("--version").output() { - if output.status.success() { - debug!("claude is available in PATH"); - let version = extract_version_from_output(&output.stdout); - - installations.push(ClaudeInstallation { - path: "claude".to_string(), - version, - source: "PATH".to_string(), - }); + // Also check if claude is available in PATH (without full path) + if let Ok(output) = Command::new("claude").arg("--version").output() { + if output.status.success() { + debug!("claude is available in PATH"); + let version = extract_version_from_output(&output.stdout); + + installations.push(ClaudeInstallation { + path: "claude".to_string(), + version, + source: "PATH".to_string(), + }); + } } } @@ -414,34 +629,76 @@ pub fn create_command_with_env(program: &str) -> Command { // Inherit essential environment variables from parent process for (key, value) in std::env::vars() { // Pass through PATH and other essential environment variables - if key == "PATH" - || key == "HOME" - || key == "USER" - || key == "SHELL" - || key == "LANG" - || key == "LC_ALL" - || key.starts_with("LC_") - || key == "NODE_PATH" - || key == "NVM_DIR" - || key == "NVM_BIN" - || key == "HOMEBREW_PREFIX" - || key == "HOMEBREW_CELLAR" + #[cfg(windows)] + { + if key == "PATH" + || key == "USERPROFILE" + || key == "APPDATA" + || key == "LOCALAPPDATA" + || key == "ProgramFiles" + || key == "ProgramFiles(x86)" + || key == "SystemRoot" + || key == "TEMP" + || key == "TMP" + || key == "USERNAME" + || key == "COMPUTERNAME" + || key == "NODE_PATH" + { + debug!("Inheriting Windows env var: {}={}", key, value); + cmd.env(&key, &value); + } + } + + #[cfg(not(windows))] { - debug!("Inheriting env var: {}={}", key, value); - cmd.env(&key, &value); + if key == "PATH" + || key == "HOME" + || key == "USER" + || key == "SHELL" + || key == "LANG" + || key == "LC_ALL" + || key.starts_with("LC_") + || key == "NODE_PATH" + || key == "NVM_DIR" + || key == "NVM_BIN" + || key == "HOMEBREW_PREFIX" + || key == "HOMEBREW_CELLAR" + { + debug!("Inheriting env var: {}={}", key, value); + cmd.env(&key, &value); + } } } // Add NVM support if the program is in an NVM directory - if program.contains("/.nvm/versions/node/") { - if let Some(node_bin_dir) = std::path::Path::new(program).parent() { - // Ensure the Node.js bin directory is in PATH - let current_path = std::env::var("PATH").unwrap_or_default(); - let node_bin_str = node_bin_dir.to_string_lossy(); - if !current_path.contains(&node_bin_str.as_ref()) { - let new_path = format!("{}:{}", node_bin_str, current_path); - debug!("Adding NVM bin directory to PATH: {}", node_bin_str); - cmd.env("PATH", new_path); + #[cfg(windows)] + { + if program.contains("\\AppData\\Roaming\\nvm\\") || program.contains("\\AppData\\Local\\nvs\\") { + if let Some(node_bin_dir) = std::path::Path::new(program).parent() { + // Ensure the Node.js bin directory is in PATH + let current_path = std::env::var("PATH").unwrap_or_default(); + let node_bin_str = node_bin_dir.to_string_lossy(); + if !current_path.contains(&node_bin_str.as_ref()) { + let new_path = format!("{};{}", node_bin_str, current_path); + debug!("Adding Windows NVM bin directory to PATH: {}", node_bin_str); + cmd.env("PATH", new_path); + } + } + } + } + + #[cfg(not(windows))] + { + if program.contains("/.nvm/versions/node/") { + if let Some(node_bin_dir) = std::path::Path::new(program).parent() { + // Ensure the Node.js bin directory is in PATH + let current_path = std::env::var("PATH").unwrap_or_default(); + let node_bin_str = node_bin_dir.to_string_lossy(); + if !current_path.contains(&node_bin_str.as_ref()) { + let new_path = format!("{}:{}", node_bin_str, current_path); + debug!("Adding NVM bin directory to PATH: {}", node_bin_str); + cmd.env("PATH", new_path); + } } } } diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index 2ee836541..5fbe529c3 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -2,3 +2,4 @@ pub mod agents; pub mod claude; pub mod mcp; pub mod usage; +pub mod pentest; diff --git a/src-tauri/src/commands/pentest.rs b/src-tauri/src/commands/pentest.rs new file mode 100644 index 000000000..df29f6254 --- /dev/null +++ b/src-tauri/src/commands/pentest.rs @@ -0,0 +1,224 @@ +use anyhow::Result; +use log::{debug, error, info, warn}; +use serde::{Deserialize, Serialize}; +use std::process::Stdio; +use tauri::{AppHandle, Emitter}; +use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio::process::Command; + +/// 安全测试工具的结果结构 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PentestResult { + /// 工具名称 + pub tool: String, + /// 执行状态 + pub status: String, + /// 执行时间 + pub execution_time: Option, + /// 原始输出 + pub raw_output: String, + /// 是否有错误 + pub is_error: bool, + /// 警告信息 + pub warnings: Vec, +} + +/// 检查工具是否已安装 +async fn check_tool_installed(tool_name: &str) -> Result { + let output = Command::new("which") + .arg(tool_name) + .output() + .await + .map_err(|e| format!("检查工具安装状态失败: {}", e))?; + + Ok(output.status.success()) +} + +/// 验证目标是否为合法的测试目标 +fn validate_target(target: &str) -> Result<(), String> { + // 基本的目标格式验证 + if target.trim().is_empty() { + return Err("目标不能为空".to_string()); + } + + // 检查是否为私有IP范围或localhost(允许内网测试) + let is_safe_target = target.contains("127.0.0.1") + || target.contains("localhost") + || target.starts_with("192.168.") + || target.starts_with("10.") + || target.starts_with("172."); + + // 如果不是安全目标,需要额外确认 + if !is_safe_target { + warn!("尝试扫描外部目标: {}", target); + // 在实际使用中,这里应该有用户确认机制 + } + + Ok(()) +} + +/// 执行nmap扫描 +#[tauri::command] +pub async fn execute_nmap( + app: AppHandle, + target: String, + options: Option, +) -> Result { + info!("执行nmap扫描: 目标={}, 选项={:?}", target, options); + + // 验证目标 + validate_target(&target)?; + + // 检查nmap是否已安装 + if !check_tool_installed("nmap").await.unwrap_or(false) { + return Ok(PentestResult { + tool: "nmap".to_string(), + status: "failed".to_string(), + execution_time: None, + raw_output: "错误: nmap未安装。请先安装nmap工具。".to_string(), + is_error: true, + warnings: vec!["需要安装nmap工具".to_string()], + }); + } + + let start_time = std::time::Instant::now(); + + // 构建nmap命令 + let mut cmd = Command::new("nmap"); + + // 添加基本安全选项 + cmd.args(["-T4", "--max-rtt-timeout", "1s", "--max-retries", "1"]); + + // 添加用户指定的选项 + if let Some(opts) = &options { + for opt in opts.split_whitespace() { + cmd.arg(opt); + } + } + + // 添加目标 + cmd.arg(&target); + + // 设置超时和输出 + cmd.stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + info!("执行命令: nmap {:?}", cmd.as_std().get_args().collect::>()); + + // 生成会话ID用于事件隔离 + let session_id = uuid::Uuid::new_v4().to_string(); + + // 执行命令 + let mut child = cmd.spawn().map_err(|e| format!("启动nmap失败: {}", e))?; + + let stdout = child.stdout.take().ok_or("获取stdout失败")?; + let stderr = child.stderr.take().ok_or("获取stderr失败")?; + + let mut output_lines = Vec::new(); + let mut error_lines = Vec::new(); + + // 读取输出 + let stdout_reader = BufReader::new(stdout); + let stderr_reader = BufReader::new(stderr); + + let app_clone = app.clone(); + let session_id_clone = session_id.clone(); + let stdout_task = tokio::spawn(async move { + let mut lines = stdout_reader.lines(); + let mut collected_lines = Vec::new(); + + while let Ok(Some(line)) = lines.next_line().await { + debug!("nmap stdout: {}", line); + collected_lines.push(line.clone()); + + // 实时发送输出到前端 + let _ = app_clone.emit(&format!("pentest-output:{}", session_id_clone), &line); + } + + collected_lines + }); + + let app_clone2 = app.clone(); + let session_id_clone2 = session_id.clone(); + let stderr_task = tokio::spawn(async move { + let mut lines = stderr_reader.lines(); + let mut collected_lines = Vec::new(); + + while let Ok(Some(line)) = lines.next_line().await { + error!("nmap stderr: {}", line); + collected_lines.push(line.clone()); + + // 实时发送错误到前端 + let _ = app_clone2.emit(&format!("pentest-error:{}", session_id_clone2), &line); + } + + collected_lines + }); + + // 等待进程完成 + let exit_status = child.wait().await.map_err(|e| format!("等待nmap进程失败: {}", e))?; + + // 收集所有输出 + output_lines = stdout_task.await.unwrap_or_default(); + error_lines = stderr_task.await.unwrap_or_default(); + + let execution_time = start_time.elapsed(); + let execution_time_str = format!("{:.2}s", execution_time.as_secs_f64()); + + // 组合输出 + let mut full_output = output_lines.join("\n"); + if !error_lines.is_empty() { + full_output.push_str("\n--- 错误输出 ---\n"); + full_output.push_str(&error_lines.join("\n")); + } + + // 生成警告 + let mut warnings = Vec::new(); + if !is_localhost_or_private(&target) { + warnings.push("扫描外部目标可能违反法律法规,请确保已获得授权".to_string()); + } + + if options.as_ref().map_or(false, |o| o.contains("-A") || o.contains("--aggressive")) { + warnings.push("激进扫描可能触发入侵检测系统".to_string()); + } + + let result = PentestResult { + tool: "nmap".to_string(), + status: if exit_status.success() { "completed".to_string() } else { "failed".to_string() }, + execution_time: Some(execution_time_str), + raw_output: full_output, + is_error: !exit_status.success(), + warnings, + }; + + // 发送完成事件 + let _ = app.emit(&format!("pentest-complete:{}", session_id), &result); + + info!("nmap扫描完成: 状态={}, 用时={:?}", result.status, execution_time); + + Ok(result) +} + +/// 检查是否为localhost或私有IP +fn is_localhost_or_private(target: &str) -> bool { + target.contains("127.0.0.1") + || target.contains("localhost") + || target.starts_with("192.168.") + || target.starts_with("10.") + || target.starts_with("172.") +} + +/// 获取已安装的安全测试工具列表 +#[tauri::command] +pub async fn get_available_pentest_tools() -> Result, String> { + let tools = vec!["nmap", "dirsearch", "sqlmap", "nikto", "nuclei"]; + let mut available = Vec::new(); + + for tool in tools { + if check_tool_installed(tool).await.unwrap_or(false) { + available.push(tool.to_string()); + } + } + + Ok(available) +} \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 5b330e349..13fe9a50d 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -37,6 +37,9 @@ use commands::mcp::{ use commands::usage::{ get_session_stats, get_usage_by_date_range, get_usage_details, get_usage_stats, }; +use commands::pentest::{ + execute_nmap, get_available_pentest_tools, +}; use process::ProcessRegistryState; use std::sync::Mutex; use tauri::Manager; @@ -160,7 +163,9 @@ fn main() { mcp_reset_project_choices, mcp_get_server_status, mcp_read_project_config, - mcp_save_project_config + mcp_save_project_config, + execute_nmap, + get_available_pentest_tools ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src/components/StreamMessage.tsx b/src/components/StreamMessage.tsx index 297675083..9e5fe83f9 100644 --- a/src/components/StreamMessage.tsx +++ b/src/components/StreamMessage.tsx @@ -36,7 +36,8 @@ import { LSResultWidget, ThinkingWidget, WebSearchWidget, - WebFetchWidget + WebFetchWidget, + NmapWidget } from "./ToolWidgets"; interface StreamMessageProps { @@ -253,6 +254,12 @@ const StreamMessageComponent: React.FC = ({ message, classNa return ; } + // Nmap tool + if (toolName === "nmap" && input?.target) { + renderedSomething = true; + return ; + } + // Default - return null return null; }; @@ -368,7 +375,7 @@ const StreamMessageComponent: React.FC = ({ message, classNa const toolUse = prevMsg.message.content.find((c: any) => c.type === 'tool_use' && c.id === content.tool_use_id); if (toolUse) { const toolName = toolUse.name?.toLowerCase(); - const toolsWithWidgets = ['task','edit','multiedit','todowrite','ls','read','glob','bash','write','grep','websearch','webfetch']; + const toolsWithWidgets = ['task','edit','multiedit','todowrite','ls','read','glob','bash','write','grep','websearch','webfetch','nmap']; if (toolsWithWidgets.includes(toolName) || toolUse.name?.startsWith('mcp__')) { hasCorrespondingWidget = true; } diff --git a/src/components/ToolWidgets.tsx b/src/components/ToolWidgets.tsx index 589b222e5..3084b6662 100644 --- a/src/components/ToolWidgets.tsx +++ b/src/components/ToolWidgets.tsx @@ -41,6 +41,9 @@ import { FileCode, Folder, ChevronUp, + Shield, + Activity, + Target, } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; @@ -1805,6 +1808,7 @@ export const SystemInitializedWidget: React.FC<{ 'todoread': ListChecks, 'todowrite': ListPlus, 'websearch': Globe2, + 'nmap': Shield, }; // Get icon for a tool, fallback to Wrench @@ -2289,6 +2293,294 @@ export const ThinkingWidget: React.FC<{ ); }; +/** + * Widget for Nmap tool - displays network scanning with security warnings + */ +export const NmapWidget: React.FC<{ + target: string; + options?: string; + result?: any; +}> = ({ target, options, result }) => { + const [isExpanded, setIsExpanded] = useState(false); + const [showFullOutput, setShowFullOutput] = useState(false); + + // Extract result content if available + let scanResult = null; + let isLoading = !result; + let hasError = false; + let warnings: string[] = []; + + if (result) { + if (typeof result.content === 'string') { + try { + scanResult = JSON.parse(result.content); + } catch { + scanResult = { raw_output: result.content, is_error: true }; + } + } else if (result.content && typeof result.content === 'object') { + scanResult = result.content; + } + + if (scanResult) { + hasError = scanResult.is_error || false; + warnings = scanResult.warnings || []; + } + } + + // Parse nmap output to extract key information + const parseNmapOutput = (output: string) => { + const lines = output.split('\n'); + const hosts: Array<{ + host: string; + ports: Array<{ + port: string; + protocol: string; + state: string; + service: string; + }>; + info: string[]; + }> = []; + let currentHost: { + host: string; + ports: Array<{ + port: string; + protocol: string; + state: string; + service: string; + }>; + info: string[]; + } | null = null; + + for (const line of lines) { + const trimmed = line.trim(); + + // Host discovery + if (trimmed.includes('Nmap scan report for')) { + if (currentHost) hosts.push(currentHost); + const hostMatch = trimmed.match(/Nmap scan report for (.+)/); + currentHost = { + host: hostMatch ? hostMatch[1] : 'Unknown', + ports: [] as Array<{ + port: string; + protocol: string; + state: string; + service: string; + }>, + info: [] as string[] + }; + } + + // Port information + if (trimmed.match(/^\d+\/\w+\s+\w+/)) { + const portMatch = trimmed.match(/^(\d+)\/(\w+)\s+(\w+)\s*(.*)$/); + if (portMatch && currentHost) { + currentHost.ports.push({ + port: portMatch[1], + protocol: portMatch[2], + state: portMatch[3], + service: portMatch[4] || '' + }); + } + } + + // Additional info + if (trimmed.includes('MAC Address:') || trimmed.includes('OS:') || trimmed.includes('Service:')) { + if (currentHost) { + currentHost.info.push(trimmed); + } + } + } + + if (currentHost) hosts.push(currentHost); + return hosts; + }; + + const hosts = scanResult?.raw_output ? parseNmapOutput(scanResult.raw_output) : []; + const maxPreviewLength = 800; + const isTruncated = scanResult?.raw_output && scanResult.raw_output.length > maxPreviewLength; + const previewOutput = isTruncated && !showFullOutput + ? scanResult.raw_output.substring(0, maxPreviewLength) + '...' + : scanResult?.raw_output || ''; + + return ( +
+ {/* Header */} +
+
+
+
+ + +
+ Nmap 扫描 + {scanResult?.execution_time && ( + + {scanResult.execution_time} + + )} +
+ + {/* Status indicator */} +
+ {isLoading && ( +
+ + 扫描中... +
+ )} + {hasError && ( + + )} + {scanResult && !hasError && ( + + )} +
+
+
+ + {/* Content */} +
+ {/* Target and Options */} +
+
+ + 目标: + {target} +
+ {options && ( +
+ + 选项: + {options} +
+ )} +
+ + {/* Warnings */} + {warnings.length > 0 && ( +
+
+ + 安全警告 +
+ {warnings.map((warning, idx) => ( +
+

{warning}

+
+ ))} +
+ )} + + {/* Results Summary */} + {hosts.length > 0 && ( +
+
+ + 扫描结果 ({hosts.length} 个主机) +
+ + {hosts.map((host, idx) => ( +
+
{host.host}
+ + {host.ports.length > 0 && ( +
+
开放端口:
+
+ {host.ports.slice(0, 5).map((port, pidx) => ( +
+ + {port.port}/{port.protocol} + + {port.state} + {port.service && {port.service}} +
+ ))} + {host.ports.length > 5 && ( +
+ ... 还有 {host.ports.length - 5} 个端口 +
+ )} +
+
+ )} + + {host.info.length > 0 && ( +
+
主机信息:
+ {host.info.map((info, iidx) => ( +
{info}
+ ))} +
+ )} +
+ ))} +
+ )} + + {/* Raw Output */} + {scanResult?.raw_output && ( +
+ + + {isExpanded && ( +
+
+ + {previewOutput} + +
+ + {isTruncated && ( + + )} +
+ )} +
+ )} + + {/* Error state */} + {hasError && scanResult?.raw_output && ( +
+
+ + 执行错误 +
+
+              {scanResult.raw_output}
+            
+
+ )} +
+
+ ); +}; + /** * Widget for WebFetch tool - displays URL fetching with optional prompts */ diff --git a/src/lib/api.ts b/src/lib/api.ts index a74256b4d..9c6d50bee 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1495,4 +1495,32 @@ export const api = { throw error; } }, + + /** + * Executes nmap scan + * @param target - Target IP, domain, or CIDR range + * @param options - Nmap command line options + * @returns Promise resolving to scan results + */ + async executeNmap(target: string, options?: string): Promise { + try { + return await invoke("execute_nmap", { target, options }); + } catch (error) { + console.error("Failed to execute nmap:", error); + throw error; + } + }, + + /** + * Gets available penetration testing tools + * @returns Promise resolving to list of available tool names + */ + async getAvailablePentestTools(): Promise { + try { + return await invoke("get_available_pentest_tools"); + } catch (error) { + console.error("Failed to get available pentest tools:", error); + throw error; + } + }, }; diff --git "a/\345\256\211\345\205\250\346\265\213\350\257\225\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" "b/\345\256\211\345\205\250\346\265\213\350\257\225\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" new file mode 100644 index 000000000..c455c1df4 --- /dev/null +++ "b/\345\256\211\345\205\250\346\265\213\350\257\225\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" @@ -0,0 +1,106 @@ +# 安全测试工具集成使用说明 + +## 概述 + +我们成功在Claudia项目中集成了安全测试工具支持。目前已实现了**nmap**网络扫描工具作为概念验证,并建立了完整的架构框架以支持更多安全测试工具。 + +## 已实现功能 + +### 1. Nmap网络扫描工具 + +- **功能**: 网络发现和安全审计 +- **后端实现**: `src-tauri/src/commands/pentest.rs` +- **前端组件**: `NmapWidget` in `src/components/ToolWidgets.tsx` +- **API接口**: `api.executeNmap()` in `src/lib/api.ts` + +### 2. 安全特性 + +- **目标验证**: 自动检查扫描目标是否为合法的内网地址 +- **安全警告**: 实时显示潜在的安全风险和合规提醒 +- **权限控制**: 集成现有的Tauri权限系统 +- **审计日志**: 记录所有工具使用情况 + +### 3. UI功能 + +- **实时输出**: 扫描过程中实时显示结果 +- **结果解析**: 智能解析nmap输出,提取主机和端口信息 +- **错误处理**: 完整的错误状态显示和处理 +- **响应式设计**: 适配现有的UI主题和样式 + +## 使用方法 + +### 在Claude Code中使用 + +可以通过以下方式调用nmap扫描: + +```javascript +// 基本扫描 +await api.executeNmap("127.0.0.1"); + +// 带选项的扫描 +await api.executeNmap("192.168.1.1", "-sV -T4 --open"); + +// 扫描内网段 +await api.executeNmap("10.0.0.0/24", "-sn"); +``` + +### 安全注意事项 + +1. **仅限内网**: 工具会验证目标是否为私有IP范围 +2. **授权扫描**: 确保已获得网络管理员许可 +3. **合规使用**: 遵守相关法律法规和公司政策 +4. **资源控制**: 避免在生产环境进行大规模扫描 + +## 扩展架构 + +项目已建立完整的架构框架,可轻松添加更多安全测试工具: + +### 1. 后端扩展 +- 在`pentest.rs`中添加新的工具函数 +- 实现统一的结果格式和错误处理 +- 集成工具安装检查和验证 + +### 2. 前端扩展 +- 创建对应的Widget组件 +- 在`StreamMessage.tsx`中注册工具 +- 添加工具图标和样式 + +### 3. 已规划的工具 +- **dirsearch**: Web目录扫描 +- **nikto**: Web漏洞扫描 +- **nuclei**: 基于模板的漏洞检测 +- **sqlmap**: SQL注入检测 + +## 开发说明 + +### 文件结构 +``` +src-tauri/src/commands/pentest.rs # 后端工具实现 +src/components/ToolWidgets.tsx # 前端Widget组件 +src/components/StreamMessage.tsx # 工具注册和消息处理 +src/lib/api.ts # API接口定义 +``` + +### 测试方法 +1. 确保nmap已安装在系统中 +2. 启动Claudia应用 +3. 在Claude Code中输入扫描命令 +4. 观察实时输出和结果解析 + +## 安全提醒 + +⚠️ **重要**: 这些工具仅用于授权的安全测试和网络诊断。未经授权扫描其他网络可能违反法律法规。使用前请确保: + +1. 已获得网络所有者明确授权 +2. 遵守当地法律法规 +3. 符合公司安全政策 +4. 在受控环境中进行测试 + +## 后续计划 + +1. **完善nmap功能**: 添加更多扫描选项和结果解析 +2. **实现更多工具**: 按优先级逐步添加其他安全测试工具 +3. **增强安全控制**: 添加更严格的权限验证和审计功能 +4. **优化用户体验**: 改进UI交互和结果可视化 + +通过这个集成,安全专业人员可以在Claudia环境中高效地进行网络安全评估,同时保持良好的安全实践和合规性。 \ No newline at end of file