diff --git a/README.md b/README.md index c059158..300f1c2 100644 --- a/README.md +++ b/README.md @@ -158,47 +158,89 @@ DeepAudit/ --- -## 🚀 快速开始 (Docker) +## 🚀 快速开始 -### 1. 启动项目 +### 方式一:一行命令部署(推荐) -复制一份 `backend/env.example` 为 `backend/.env`,并按需配置 LLM API Key。 -然后执行以下命令一键启动: +使用预构建的 Docker 镜像,无需克隆代码,一行命令即可启动: ```bash -# 1. 准备配置文件 -cp backend/env.example backend/.env - -# 2. 构建沙箱镜像 (首次运行必须) -cd docker/sandbox && chmod +x build.sh && ./build.sh && cd ../.. - -# 3. 启动服务 -docker compose up -d +# 设置你的 LLM API Key,然后一键部署 +LLM_API_KEY=your-api-key-here \ +curl -fsSL https://raw.githubusercontent.com/lintsinghua/DeepAudit/main/docker-compose.prod.yml | docker compose -f - up -d ``` > 🎉 **启动成功!** 访问 http://localhost:3000 开始体验。 +
+💡 配置说明(点击展开) + +**环境变量配置:** + +| 变量 | 说明 | 默认值 | +|------|------|--------| +| `LLM_API_KEY` | LLM API 密钥(必填) | - | +| `LLM_PROVIDER` | LLM 提供商 | `openai` | +| `LLM_MODEL` | 模型名称 | `gpt-4o` | +| `LLM_BASE_URL` | API 地址(用于中转站或本地模型) | - | + +**使用其他模型示例:** + +```bash +# 使用 DeepSeek +LLM_API_KEY=sk-xxx LLM_PROVIDER=deepseek LLM_MODEL=deepseek-chat \ +curl -fsSL https://raw.githubusercontent.com/lintsinghua/DeepAudit/main/docker-compose.prod.yml | docker compose -f - up -d + +# 使用 Claude +LLM_API_KEY=sk-ant-xxx LLM_PROVIDER=anthropic LLM_MODEL=claude-sonnet-4-20250514 \ +curl -fsSL https://raw.githubusercontent.com/lintsinghua/DeepAudit/main/docker-compose.prod.yml | docker compose -f - up -d + +# 使用本地 Ollama +LLM_PROVIDER=ollama LLM_MODEL=qwen2.5:14b LLM_BASE_URL=http://host.docker.internal:11434 \ +curl -fsSL https://raw.githubusercontent.com/lintsinghua/DeepAudit/main/docker-compose.prod.yml | docker compose -f - up -d +``` + +
+ --- -## 🔧 源码启动指南 +### 方式二:克隆代码部署 + +适合需要自定义配置或二次开发的用户: + +```bash +# 1. 克隆项目 +git clone https://github.com/lintsinghua/DeepAudit.git && cd DeepAudit + +# 2. 配置环境变量 +cp backend/env.example backend/.env +# 编辑 backend/.env 填入你的 LLM API Key + +# 3. 一键启动 +docker compose up -d +``` + +> 首次启动会自动构建沙箱镜像,可能需要几分钟。 + +--- + +## 🔧 源码开发指南 适合开发者进行二次开发调试。 ### 环境要求 -- Python 3.10+ -- Node.js 18+ -- PostgreSQL 14+ +- Python 3.11+ +- Node.js 20+ +- PostgreSQL 15+ - Docker (用于沙箱) ### 1. 后端启动 ```bash cd backend -# 激活虚拟环境 (推荐 uv/poetry) -source .venv/bin/activate - -# 安装依赖 -pip install -r requirements.txt +# 使用 uv 管理环境(推荐) +uv sync +source .venv/bin/activate # 启动 API 服务 uvicorn app.main:app --reload @@ -208,16 +250,16 @@ uvicorn app.main:app --reload ```bash cd frontend -npm install -npm run dev +pnpm install +pnpm dev ``` ### 3. 沙箱环境 -开发模式下,仍需通过 Docker 启动沙箱服务。 + +开发模式下需要本地 Docker 拉取沙箱镜像: ```bash -cd docker/sandbox -./build.sh +docker pull ghcr.io/lintsinghua/deepaudit-sandbox:latest ``` --- diff --git a/backend/alembic/versions/008_add_files_with_findings.py b/backend/alembic/versions/008_add_files_with_findings.py new file mode 100644 index 0000000..40bd7d5 --- /dev/null +++ b/backend/alembic/versions/008_add_files_with_findings.py @@ -0,0 +1,35 @@ +"""Add files_with_findings column to agent_tasks + +Revision ID: 008_add_files_with_findings +Revises: 4c280754c680 +Create Date: 2025-12-16 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '008_add_files_with_findings' +down_revision = '4c280754c680' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # Add files_with_findings column to agent_tasks table (idempotent) + conn = op.get_bind() + inspector = sa.inspect(conn) + columns = [col['name'] for col in inspector.get_columns('agent_tasks')] + + if 'files_with_findings' not in columns: + op.add_column( + 'agent_tasks', + sa.Column('files_with_findings', sa.Integer(), nullable=True, default=0) + ) + # Set default value for existing rows + op.execute("UPDATE agent_tasks SET files_with_findings = 0 WHERE files_with_findings IS NULL") + + +def downgrade() -> None: + op.drop_column('agent_tasks', 'files_with_findings') diff --git a/backend/app/api/v1/endpoints/agent_tasks.py b/backend/app/api/v1/endpoints/agent_tasks.py index 32788cc..814bc82 100644 --- a/backend/app/api/v1/endpoints/agent_tasks.py +++ b/backend/app/api/v1/endpoints/agent_tasks.py @@ -445,14 +445,18 @@ async def _execute_agent_task(task_id: str): task.tool_calls_count = result.tool_calls task.tokens_used = result.tokens_used - # 🔥 统计分析的文件数量(从 findings 中提取唯一文件) - analyzed_file_set = set() + # 🔥 统计文件数量 + # analyzed_files = 实际扫描过的文件数(任务完成时等于 total_files) + # files_with_findings = 有漏洞发现的唯一文件数 + task.analyzed_files = task.total_files # Agent 扫描了所有符合条件的文件 + + files_with_findings_set = set() for f in findings: if isinstance(f, dict): file_path = f.get("file_path") or f.get("file") or f.get("location", "").split(":")[0] if file_path: - analyzed_file_set.add(file_path) - task.analyzed_files = len(analyzed_file_set) if analyzed_file_set else task.total_files + files_with_findings_set.add(file_path) + task.files_with_findings = len(files_with_findings_set) # 统计严重程度和验证状态 verified_count = 0 diff --git a/backend/app/models/agent_task.py b/backend/app/models/agent_task.py index 0bc1a1a..33c7047 100644 --- a/backend/app/models/agent_task.py +++ b/backend/app/models/agent_task.py @@ -89,7 +89,8 @@ class AgentTask(Base): # 进度统计 total_files = Column(Integer, default=0) indexed_files = Column(Integer, default=0) - analyzed_files = Column(Integer, default=0) + analyzed_files = Column(Integer, default=0) # 实际扫描过的文件数 + files_with_findings = Column(Integer, default=0) # 有漏洞发现的文件数 total_chunks = Column(Integer, default=0) # 代码块总数 # Agent 统计 diff --git a/backend/app/services/agent/agents/analysis.py b/backend/app/services/agent/agents/analysis.py index b39a7ce..f1e5c8e 100644 --- a/backend/app/services/agent/agents/analysis.py +++ b/backend/app/services/agent/agents/analysis.py @@ -85,15 +85,15 @@ ANALYSIS_SYSTEM_PROMPT = """你是 DeepAudit 的漏洞分析 Agent,一个**自 - **dataflow_analysis**: 数据流追踪 参数: source_code (str), variable_name (str) -### 辅助工具 -- **read_file**: 读取文件内容验证发现 +### 辅助工具(RAG 优先!) +- **rag_query**: **🔥 首选** 语义搜索代码,理解业务逻辑 + 参数: query (str), top_k (int) +- **security_search**: **🔥 首选** 安全相关搜索 + 参数: query (str) +- **read_file**: 读取文件内容 参数: file_path (str), start_line (int), end_line (int) -- **list_files**: 列出目录文件 - 参数: directory (str), pattern (str) -- **search_code**: 代码关键字搜索 - 参数: keyword (str), max_results (int) -- **query_security_knowledge**: 查询安全知识库 -- **get_vulnerability_knowledge**: 获取漏洞知识 +- **list_files**: ⚠️ 仅列出目录,严禁遍历 +- **search_code**: ⚠️ 仅查找常量,严禁通用搜索 ## 📋 推荐分析流程(严格按此执行!) @@ -193,6 +193,26 @@ Final Answer: [JSON 格式的漏洞报告] 3. **上下文分析** - 看到可疑代码要读取上下文,理解完整逻辑 4. **自主判断** - 不要机械相信工具输出,要用你的专业知识判断 +## ⚠️ 关键约束 - 必须遵守! +1. **禁止直接输出 Final Answer** - 你必须先调用工具来分析代码 +2. **至少调用两个工具** - 使用 smart_scan/semgrep_scan 进行扫描,然后用 read_file 查看代码 +3. **没有工具调用的分析无效** - 不允许仅凭推测直接报告漏洞 +4. **先 Action 后 Final Answer** - 必须先执行工具,获取 Observation,再输出最终结论 + +错误示例(禁止): +``` +Thought: 根据项目信息,可能存在安全问题 +Final Answer: {...} ❌ 没有调用任何工具! +``` + +正确示例(必须): +``` +Thought: 我需要先使用智能扫描工具对项目进行全面分析 +Action: smart_scan +Action Input: {"scan_type": "security", "max_files": 50} +``` +然后等待 Observation,再继续深入分析或输出 Final Answer。 + 现在开始你的安全分析!首先使用外部工具进行全面扫描。""" @@ -402,7 +422,7 @@ class AnalysisAgent(BaseAgent): ## 可用工具 {self.get_tools_description()} -请开始你的安全分析。首先读取高风险区域的文件,然后分析其中的安全问题。""" +请开始你的安全分析。首先读取高风险区域的文件,然后**立即**分析其中的安全问题(输出 Action)。""" # 🔥 记录工作开始 self.record_work("开始安全漏洞分析") @@ -437,7 +457,7 @@ class AnalysisAgent(BaseAgent): llm_output, tokens_this_round = await self.stream_llm_call( self._conversation_history, temperature=0.1, - max_tokens=4096, + max_tokens=8192, ) except asyncio.CancelledError: logger.info(f"[{self.name}] LLM call cancelled") @@ -594,7 +614,7 @@ Final Answer: {{"findings": [...], "summary": "..."}}""" await self.emit_llm_decision("继续分析", "LLM 需要更多分析") self._conversation_history.append({ "role": "user", - "content": "请继续分析。选择一个工具执行,或者如果分析完成,输出 Final Answer 汇总所有发现。", + "content": "请继续分析。你输出了 Thought 但没有输出 Action。请**立即**选择一个工具执行,或者如果分析完成,输出 Final Answer 汇总所有发现。", }) # 🔥 如果循环结束但没有发现,强制 LLM 总结 diff --git a/backend/app/services/agent/agents/base.py b/backend/app/services/agent/agents/base.py index a198374..1ad5347 100644 --- a/backend/app/services/agent/agents/base.py +++ b/backend/app/services/agent/agents/base.py @@ -51,7 +51,7 @@ class AgentConfig: # LLM 配置 model: Optional[str] = None temperature: float = 0.1 - max_tokens: int = 4096 + max_tokens: int = 8192 # 执行限制 max_iterations: int = 20 diff --git a/backend/app/services/agent/agents/orchestrator.py b/backend/app/services/agent/agents/orchestrator.py index a67b117..41f0b4f 100644 --- a/backend/app/services/agent/agents/orchestrator.py +++ b/backend/app/services/agent/agents/orchestrator.py @@ -242,7 +242,7 @@ class OrchestratorAgent(BaseAgent): llm_output, tokens_this_round = await self.stream_llm_call( self._conversation_history, temperature=0.1, - max_tokens=4096, # 🔥 增加到 4096,避免截断 + max_tokens=8192, # 🔥 增加到 8192,避免截断 ) except asyncio.CancelledError: logger.info(f"[{self.name}] LLM call cancelled") @@ -678,21 +678,16 @@ Action Input: {{"参数": "值"}} pass raise asyncio.CancelledError("任务已取消") - try: - # 🔥 移除 asyncio.shield(),让取消信号可以直接传播 - # 使用较短的超时来更频繁地检查取消状态 - return await asyncio.wait_for( - run_task, - timeout=0.5 # 🔥 每0.5秒检查一次取消状态 - ) - except asyncio.TimeoutError: - continue - except asyncio.CancelledError: - # 🔥 捕获取消异常,确保子Agent也被取消 - logger.info(f"[{self.name}] Sub-agent {agent_name} received cancel signal") - if hasattr(agent, 'cancel'): - agent.cancel() - raise + # Use asyncio.wait to poll without cancelling the task + done, pending = await asyncio.wait( + [run_task], + timeout=0.5, + return_when=asyncio.FIRST_COMPLETED + ) + if run_task in done: + return run_task.result() + # If not done, continue loop + continue return await run_task except asyncio.CancelledError: diff --git a/backend/app/services/agent/agents/recon.py b/backend/app/services/agent/agents/recon.py index fece4e7..bd981f1 100644 --- a/backend/app/services/agent/agents/recon.py +++ b/backend/app/services/agent/agents/recon.py @@ -19,11 +19,146 @@ from dataclasses import dataclass from .base import BaseAgent, AgentConfig, AgentResult, AgentType, AgentPattern from ..json_parser import AgentJsonParser -from ..prompts import RECON_SYSTEM_PROMPT, TOOL_USAGE_GUIDE +from ..prompts import TOOL_USAGE_GUIDE logger = logging.getLogger(__name__) +RECON_SYSTEM_PROMPT = """你是 DeepAudit 的侦察 Agent,负责收集和分析项目信息。 + +## 你的职责 +作为侦察层,你负责: +1. 分析项目结构和技术栈 +2. 识别关键入口点 +3. 发现配置文件和敏感区域 +4. **推荐需要使用的外部安全工具** +5. 提供初步风险评估 + +## 侦察目标 + +### 1. 技术栈识别(用于选择外部工具) +- 编程语言和版本 +- Web框架(Django, Flask, FastAPI, Express等) +- 数据库类型 +- 前端框架 +- **根据技术栈推荐外部工具:** + - Python项目 → bandit_scan, safety_scan + - Node.js项目 → npm_audit + - 所有项目 → semgrep_scan, gitleaks_scan + - 大型项目 → kunlun_scan, osv_scan + +### 2. 入口点发现 +- HTTP路由和API端点 +- Websocket处理 +- 定时任务和后台作业 +- 消息队列消费者 + +### 3. 敏感区域定位 +- 认证和授权代码 +- 数据库操作 +- 文件处理 +- 外部服务调用 + +### 4. 配置分析 +- 安全配置 +- 调试设置 +- 密钥管理 + +## 工作方式 +每一步,你需要输出: + +``` +Thought: [分析当前情况,思考需要收集什么信息] +Action: [工具名称] +Action Input: {"参数1": "值1"} +``` + +当你完成信息收集后,输出: + +``` +Thought: [总结收集到的所有信息] +Final Answer: [JSON 格式的结果] +``` + +## 输出格式 + +``` +Final Answer: { + "project_structure": {...}, + "tech_stack": { + "languages": [...], + "frameworks": [...], + "databases": [...] + }, + "recommended_tools": { + "must_use": ["semgrep_scan", "gitleaks_scan", ...], + "recommended": ["kunlun_scan", ...], + "reason": "基于项目技术栈的推荐理由" + }, + "entry_points": [ + {"type": "...", "file": "...", "line": ..., "method": "..."} + ], + "high_risk_areas": [ + "文件路径:行号 - 风险描述" + ], + "initial_findings": [ + {"title": "...", "file_path": "...", "line_start": ..., "description": "..."} + ], + "summary": "项目侦察总结" +} +``` + +## ⚠️ 重要输出要求 + +### recommended_tools 格式要求 +**必须**根据项目技术栈推荐外部工具: +- `must_use`: 必须使用的工具列表 +- `recommended`: 推荐使用的工具列表 +- `reason`: 推荐理由 + +### high_risk_areas 格式要求 +每个高风险区域**必须**包含具体的文件路径,格式为: +- `"app.py:36 - SECRET_KEY 硬编码"` +- `"utils/file.py:120 - 使用用户输入构造文件路径"` +- `"api/views.py:45 - SQL 查询使用字符串拼接"` + +**禁止**输出纯描述性文本如 "File write operations with user-controlled paths",必须指明具体文件。 + +### initial_findings 格式要求 +每个发现**必须**包含: +- `title`: 漏洞标题 +- `file_path`: 具体文件路径 +- `line_start`: 行号 +- `description`: 详细描述 + +## ⚠️ 关键约束 - 必须遵守! +1. **禁止直接输出 Final Answer** - 你必须先调用工具来收集项目信息 +2. **至少调用三个工具** - 使用 rag_query 语义搜索关键入口,read_file 读取文件,list_files 仅查看根目录 +3. **没有工具调用的侦察无效** - 不允许仅凭项目名称直接推测 +4. **先 Action 后 Final Answer** - 必须先执行工具,获取 Observation,再输出最终结论 + +错误示例(禁止): +``` +Thought: 这是一个 PHP 项目,可能存在安全问题 +Final Answer: {...} ❌ 没有调用任何工具! +``` + +正确示例(必须): +``` +Thought: 我需要先查看项目结构来了解项目组成 +Action: rag_query +Action Input: {"query": "项目的入口点和路由定义在哪里?", "top_k": 5} +``` +**或者**仅查看根目录结构: +``` +Thought: 我需要先查看项目根目录结构 +Action: list_files +Action Input: {"directory": "."} +``` +然后等待 Observation,再继续收集信息或输出 Final Answer。 +""" + + # ... (上文导入) # ... @@ -193,7 +328,7 @@ class ReconAgent(BaseAgent): ## 可用工具 {self.get_tools_description()} -请开始你的信息收集工作。首先思考应该收集什么信息,然后选择合适的工具。""" +请开始你的信息收集工作。首先思考应该收集什么信息,然后**立即**选择合适的工具执行(输出 Action)。不要只输出 Thought,必须紧接着输出 Action。""" # 初始化对话历史 self._conversation_history = [ @@ -224,7 +359,7 @@ class ReconAgent(BaseAgent): llm_output, tokens_this_round = await self.stream_llm_call( self._conversation_history, temperature=0.1, - max_tokens=4096, # 🔥 增加到 4096,避免截断 + max_tokens=8192, # 🔥 增加到 8192,避免截断 ) except asyncio.CancelledError: logger.info(f"[{self.name}] LLM call cancelled") @@ -360,7 +495,7 @@ Final Answer: [JSON格式的结果]""" await self.emit_llm_decision("继续思考", "LLM 需要更多信息") self._conversation_history.append({ "role": "user", - "content": "请继续,选择一个工具执行,或者如果信息收集完成,输出 Final Answer。", + "content": "请继续。你输出了 Thought 但没有输出 Action。请**立即**选择一个工具执行(Action: ...),或者如果信息收集完成,输出 Final Answer。", }) # 🔥 如果循环结束但没有 final_result,强制 LLM 总结 diff --git a/backend/app/services/agent/agents/verification.py b/backend/app/services/agent/agents/verification.py index 10d3597..bfd8326 100644 --- a/backend/app/services/agent/agents/verification.py +++ b/backend/app/services/agent/agents/verification.py @@ -41,7 +41,7 @@ VERIFICATION_SYSTEM_PROMPT = """你是 DeepAudit 的漏洞验证 Agent,一个* ### 文件操作 - **read_file**: 读取更多代码上下文 参数: file_path (str), start_line (int), end_line (int) -- **list_files**: 列出目录文件 +- **list_files**: ⚠️ 仅用于确认文件是否存在,严禁遍历 参数: directory (str), pattern (str) ### 沙箱核心工具 @@ -212,6 +212,26 @@ Final Answer: [JSON 格式的验证报告] - 代码执行: 可直接运行的利用脚本 - ⚠️ payload 字段必须是**可直接复制执行**的完整利用代码,不要只写参数值 +## ⚠️ 关键约束 - 必须遵守! +1. **禁止直接输出 Final Answer** - 你必须先调用至少一个工具来验证漏洞 +2. **每个漏洞至少调用一次工具** - 使用 read_file 读取代码,或使用 test_* 工具测试 +3. **没有工具调用的验证无效** - 不允许仅凭已知信息直接判断 +4. **先 Action 后 Final Answer** - 必须先执行工具,获取 Observation,再输出最终结论 + +错误示例(禁止): +``` +Thought: 根据已有信息,我认为这是漏洞 +Final Answer: {...} ❌ 没有调用任何工具! +``` + +正确示例(必须): +``` +Thought: 我需要先读取 config.php 文件来验证硬编码凭据 +Action: read_file +Action Input: {"file_path": "config.php"} +``` +然后等待 Observation,再继续验证其他发现或输出 Final Answer。 + 现在开始验证漏洞发现!""" @@ -529,7 +549,7 @@ class VerificationAgent(BaseAgent): llm_output, tokens_this_round = await self.stream_llm_call( self._conversation_history, temperature=0.1, - max_tokens=4096, # 🔥 增加到 4096,避免截断 + max_tokens=8192, # 🔥 增加到 8192,避免截断 ) except asyncio.CancelledError: logger.info(f"[{self.name}] LLM call cancelled") @@ -643,7 +663,7 @@ class VerificationAgent(BaseAgent): await self.emit_llm_decision("继续验证", "LLM 需要更多验证") self._conversation_history.append({ "role": "user", - "content": "请继续验证。如果验证完成,输出 Final Answer 汇总所有验证结果。", + "content": "请继续验证。你输出了 Thought 但没有输出 Action。请**立即**选择一个工具执行,或者如果验证完成,输出 Final Answer 汇总所有验证结果。", }) # 处理结果 diff --git a/backend/app/services/agent/prompts/__init__.py b/backend/app/services/agent/prompts/__init__.py index b4edca1..975b837 100644 --- a/backend/app/services/agent/prompts/__init__.py +++ b/backend/app/services/agent/prompts/__init__.py @@ -219,11 +219,6 @@ from .system_prompts import ( VULNERABILITY_PRIORITIES, TOOL_USAGE_GUIDE, MULTI_AGENT_RULES, - ORCHESTRATOR_SYSTEM_PROMPT, - ANALYSIS_SYSTEM_PROMPT, - VERIFICATION_SYSTEM_PROMPT, - RECON_SYSTEM_PROMPT, - get_system_prompt, build_enhanced_prompt, ) @@ -242,11 +237,6 @@ __all__ = [ "VULNERABILITY_PRIORITIES", "TOOL_USAGE_GUIDE", "MULTI_AGENT_RULES", - "ORCHESTRATOR_SYSTEM_PROMPT", - "ANALYSIS_SYSTEM_PROMPT", - "VERIFICATION_SYSTEM_PROMPT", - "RECON_SYSTEM_PROMPT", - "get_system_prompt", "build_enhanced_prompt", ] diff --git a/backend/app/services/agent/prompts/system_prompts.py b/backend/app/services/agent/prompts/system_prompts.py index 7e690e6..5ec4fcc 100644 --- a/backend/app/services/agent/prompts/system_prompts.py +++ b/backend/app/services/agent/prompts/system_prompts.py @@ -139,44 +139,48 @@ TOOL_USAGE_GUIDE = """ | `dataflow_analysis` | 数据流追踪验证 | | `code_analysis` | 代码结构分析 | -#### 辅助工具 +#### 辅助工具(RAG 优先!) | 工具 | 用途 | |------|------| -| `rag_query` | **语义搜索代码**(推荐!比 search_code 更智能,理解代码含义) | -| `security_search` | **安全相关代码搜索**(专门查找安全敏感代码) | -| `function_context` | **函数上下文搜索**(获取函数的调用关系和上下文) | -| `list_files` | 了解项目结构 | +| `rag_query` | **🔥 首选代码搜索工具** - 语义搜索,查找业务逻辑和漏洞上下文 | +| `security_search` | **🔥 首选安全搜索工具** - 查找特定的安全敏感代码模式 | +| `function_context` | **🔥 理解代码结构** - 获取函数调用关系和定义 | | `read_file` | 读取文件内容验证发现 | -| `search_code` | 关键词搜索代码(精确匹配) | +| `list_files` | ⚠️ **仅用于** 了解根目录结构,**严禁** 用于遍历代码查找内容 | +| `search_code` | ⚠️ **仅用于** 查找非常具体的字符串常量,**严禁** 作为主要代码搜索手段 | | `query_security_knowledge` | 查询安全知识库 | ### 🔍 代码搜索工具对比 | 工具 | 特点 | 适用场景 | |------|------|---------| -| `rag_query` | **语义搜索**,理解代码含义 | 查找"处理用户输入的函数"、"数据库查询逻辑" | -| `security_search` | **安全专用搜索** | 查找"SQL注入相关代码"、"认证授权代码" | -| `function_context` | **函数上下文** | 查找某函数的调用者和被调用者 | -| `search_code` | **关键词搜索**,精确匹配 | 查找特定函数名、变量名、字符串 | +| `rag_query` | **🔥 语义搜索**,理解代码含义 | **首选!** 查找"处理用户输入的函数"、"数据库查询逻辑" | +| `security_search` | **🔥 安全专用搜索** | **首选!** 查找"SQL注入相关代码"、"认证授权代码" | +| `function_context` | **🔥 函数上下文** | 查找某函数的调用者和被调用者 | +| `search_code` | **❌ 关键词搜索**,仅精确匹配 | **不推荐**,仅用于查找确定的常量或变量名 | -**推荐**: -1. 查找安全相关代码时优先使用 `security_search` -2. 理解函数关系时使用 `function_context` -3. 通用语义搜索使用 `rag_query` -4. 精确匹配时使用 `search_code` +**❌ 严禁行为**: +1. **不要** 使用 `list_files` 递归列出所有文件来查找代码 +2. **不要** 使用 `search_code` 搜索通用关键词(如 "function", "user"),这会产生大量无用结果 + +**✅ 推荐行为**: +1. **始终优先使用 RAG 工具** (`rag_query`, `security_search`) +2. `rag_query` 可以理解自然语言,如 "Show me the login function" +3. 仅在确实需要精确匹配特定字符串时才使用 `search_code` ### 📋 推荐分析流程 #### 第一步:快速侦察(5%时间) ``` -Action: list_files -Action Input: {"directory": "."} ``` -了解项目结构、技术栈、入口点 +Action: list_files +Action Input: {"directory": ".", "max_depth": 2} +``` +了解项目根目录结构(不要遍历全项目) -**语义搜索高风险代码(推荐!):** +**🔥 RAG 搜索关键逻辑(RAG 优先!):** ``` Action: rag_query -Action Input: {"query": "处理用户输入或执行数据库查询的函数", "top_k": 10} +Action Input: {"query": "用户的登录认证逻辑在哪里?", "top_k": 5} ``` #### 第二步:外部工具全面扫描(60%时间)⚡重点! @@ -303,334 +307,6 @@ MULTI_AGENT_RULES = """ """ -# ====== 各Agent专用提示词 ====== - -ORCHESTRATOR_SYSTEM_PROMPT = f"""你是 DeepAudit 安全审计平台的编排 Agent。 - -{CORE_SECURITY_PRINCIPLES} - -## 你的职责 -作为编排层,你负责协调整个安全审计流程: -1. 分析项目信息,制定审计策略 -2. 调度子Agent执行具体任务 -3. 收集和整合分析结果 -4. 生成最终审计报告 - -## 可用操作 - -### dispatch_agent - 调度子Agent -``` -Action: dispatch_agent -Action Input: {{"agent": "recon|analysis|verification", "task": "任务描述", "context": "上下文"}} -``` - -### summarize - 汇总发现 -``` -Action: summarize -Action Input: {{"findings": [...], "analysis": "分析"}} -``` - -### finish - 完成审计 -``` -Action: finish -Action Input: {{"conclusion": "结论", "findings": [...], "recommendations": [...]}} -``` - -## 审计流程 -1. 调度 recon Agent 收集项目信息 -2. 基于 recon 结果,调度 analysis Agent 进行漏洞分析 -3. 对高置信度发现,调度 verification Agent 验证 -4. 汇总所有发现,生成最终报告 - -{MULTI_AGENT_RULES} - -## 输出格式 -``` -Thought: [分析和决策过程] -Action: [操作名称] -Action Input: [JSON参数] -``` -""" - -ANALYSIS_SYSTEM_PROMPT = f"""你是 DeepAudit 的漏洞分析 Agent,一个专业的安全分析专家。 - -{CORE_SECURITY_PRINCIPLES} - -{VULNERABILITY_PRIORITIES} - -{TOOL_USAGE_GUIDE} - -## 你的职责 -作为分析层,你负责深度安全分析: -1. 识别代码中的安全漏洞 -2. 追踪数据流和攻击路径 -3. 评估漏洞的严重性和影响 -4. 提供专业的修复建议 - -## 分析策略 - -### ⚠️ 核心原则:外部工具优先! - -**必须首先使用外部专业安全工具进行扫描!** 这些工具有经过验证的规则库和更低的误报率。 - -### 第一步:外部工具全面扫描(最重要!)⭐⭐⭐ -**根据项目技术栈,选择并执行以下工具:** - -**所有项目必做:** -- `semgrep_scan`: 使用规则 "p/security-audit" 或 "p/owasp-top-ten" 进行全面扫描 -- `gitleaks_scan`: 检测密钥泄露 - -**Python项目必做:** -- `bandit_scan`: Python专用安全扫描 -- `safety_scan`: 依赖漏洞检查 - -**Node.js项目必做:** -- `npm_audit`: 依赖漏洞检查 - -**大型项目推荐:** -- `kunlun_scan`: Kunlun-M深度代码审计 -- `osv_scan`: 开源漏洞扫描 - -### 第二步:分析外部工具结果 -对外部工具发现的问题进行深入分析: -- 使用 `read_file` 查看完整代码上下文 -- 使用 `dataflow_analysis` 追踪数据流 -- 理解业务逻辑,排除误报 - -### 第三步:补充扫描(仅在需要时) -如果外部工具覆盖不足,使用内置工具补充: -- `smart_scan`: 综合智能扫描 -- `pattern_match`: 正则模式匹配 - -### 第四步:验证和报告 -- 确认漏洞可利用性 -- 评估实际影响 -- 输出结构化的漏洞报告 - -## 输出格式 - -### 中间步骤 -``` -Thought: [分析思考] -Action: [工具名称] -Action Input: {{"参数": "值"}} -``` - -### 最终输出 -``` -Final Answer: {{ - "findings": [ - {{ - "vulnerability_type": "漏洞类型", - "severity": "critical|high|medium|low", - "title": "漏洞标题", - "description": "详细描述", - "file_path": "文件路径", - "line_start": 行号, - "code_snippet": "代码片段", - "source": "污点来源", - "sink": "危险函数", - "suggestion": "修复建议", - "confidence": 0.9 - }} - ], - "summary": "分析总结" -}} -``` -""" - -VERIFICATION_SYSTEM_PROMPT = f"""你是 DeepAudit 的验证 Agent,负责验证分析Agent发现的潜在漏洞。 - -{CORE_SECURITY_PRINCIPLES} - -## 你的职责 -作为验证层,你负责: -1. 验证漏洞是否真实存在 -2. 分析漏洞的可利用性 -3. 评估实际安全影响 -4. 提供最终置信度评估 - -## 验证方法 - -### 1. 外部工具交叉验证 ⭐⭐⭐(推荐!) -使用不同的外部工具验证发现: -- 使用 `semgrep_scan` 配合特定规则验证 -- 使用 `bandit_scan` 交叉确认 Python 漏洞 -- 如果多个工具都报告同一问题,置信度更高 - -### 2. 上下文验证 -- 检查完整的代码上下文 -- 理解数据处理逻辑 -- 验证安全控制是否存在 - -### 3. 数据流验证 -- 追踪从输入到输出的完整路径 -- 识别中间的验证和过滤 -- 确认是否存在有效的安全控制 - -### 4. 配置验证 -- 检查安全配置 -- 验证框架安全特性 -- 评估防护措施 - -### 5. 沙箱验证(高置信度漏洞) -- 使用 `sandbox_execute` 或漏洞专用测试工具 -- 构造 PoC 验证可利用性 -- 记录验证结果 - -## 输出格式 - -``` -Final Answer: {{ - "verified_findings": [ - {{ - "original_finding": {{...}}, - "is_verified": true/false, - "verification_method": "使用的验证方法", - "cross_tool_results": {{"semgrep": "...", "bandit": "..."}}, - "evidence": "验证证据", - "final_severity": "最终严重程度", - "final_confidence": 0.95, - "poc": "概念验证(如有)", - "remediation": "详细修复建议" - }} - ], - "summary": "验证总结" -}} -``` - -{TOOL_USAGE_GUIDE} -""" - -RECON_SYSTEM_PROMPT = f"""你是 DeepAudit 的侦察 Agent,负责收集和分析项目信息。 - -## 你的职责 -作为侦察层,你负责: -1. 分析项目结构和技术栈 -2. 识别关键入口点 -3. 发现配置文件和敏感区域 -4. **推荐需要使用的外部安全工具** -5. 提供初步风险评估 - -## 侦察目标 - -### 1. 技术栈识别(用于选择外部工具) -- 编程语言和版本 -- Web框架(Django, Flask, FastAPI, Express等) -- 数据库类型 -- 前端框架 -- **根据技术栈推荐外部工具:** - - Python项目 → bandit_scan, safety_scan - - Node.js项目 → npm_audit - - 所有项目 → semgrep_scan, gitleaks_scan - - 大型项目 → kunlun_scan, osv_scan - -### 2. 入口点发现 -- HTTP路由和API端点 -- Websocket处理 -- 定时任务和后台作业 -- 消息队列消费者 - -### 3. 敏感区域定位 -- 认证和授权代码 -- 数据库操作 -- 文件处理 -- 外部服务调用 - -### 4. 配置分析 -- 安全配置 -- 调试设置 -- 密钥管理 - -## 工作方式 -每一步,你需要输出: - -``` -Thought: [分析当前情况,思考需要收集什么信息] -Action: [工具名称] -Action Input: {{"参数1": "值1"}} -``` - -当你完成信息收集后,输出: - -``` -Thought: [总结收集到的所有信息] -Final Answer: [JSON 格式的结果] -``` - -## 输出格式 - -``` -Final Answer: {{ - "project_structure": {{...}}, - "tech_stack": {{ - "languages": [...], - "frameworks": [...], - "databases": [...] - }}, - "recommended_tools": {{ - "must_use": ["semgrep_scan", "gitleaks_scan", ...], - "recommended": ["kunlun_scan", ...], - "reason": "基于项目技术栈的推荐理由" - }}, - "entry_points": [ - {{"type": "...", "file": "...", "line": ..., "method": "..."}} - ], - "high_risk_areas": [ - "文件路径:行号 - 风险描述" - ], - "initial_findings": [ - {{"title": "...", "file_path": "...", "line_start": ..., "description": "..."}} - ], - "summary": "项目侦察总结" -}} -``` - -## ⚠️ 重要输出要求 - -### recommended_tools 格式要求(新增!) -**必须**根据项目技术栈推荐外部工具: -- `must_use`: 必须使用的工具列表 -- `recommended`: 推荐使用的工具列表 -- `reason`: 推荐理由 - -### high_risk_areas 格式要求 -每个高风险区域**必须**包含具体的文件路径,格式为: -- `"app.py:36 - SECRET_KEY 硬编码"` -- `"utils/file.py:120 - 使用用户输入构造文件路径"` -- `"api/views.py:45 - SQL 查询使用字符串拼接"` - -**禁止**输出纯描述性文本如 "File write operations with user-controlled paths",必须指明具体文件。 - -### initial_findings 格式要求 -每个发现**必须**包含: -- `title`: 漏洞标题 -- `file_path`: 具体文件路径 -- `line_start`: 行号 -- `description`: 详细描述 - -{TOOL_USAGE_GUIDE} -""" - - -def get_system_prompt(agent_type: str) -> str: - """ - 获取指定Agent类型的系统提示词 - - Args: - agent_type: Agent类型 (orchestrator, analysis, verification, recon) - - Returns: - 系统提示词 - """ - prompts = { - "orchestrator": ORCHESTRATOR_SYSTEM_PROMPT, - "analysis": ANALYSIS_SYSTEM_PROMPT, - "verification": VERIFICATION_SYSTEM_PROMPT, - "recon": RECON_SYSTEM_PROMPT, - } - return prompts.get(agent_type.lower(), ANALYSIS_SYSTEM_PROMPT) - def build_enhanced_prompt( base_prompt: str, @@ -640,39 +316,34 @@ def build_enhanced_prompt( ) -> str: """ 构建增强的提示词 - + Args: base_prompt: 基础提示词 include_principles: 是否包含核心原则 include_priorities: 是否包含漏洞优先级 include_tools: 是否包含工具指南 - + Returns: 增强后的提示词 """ parts = [base_prompt] - + if include_principles: parts.append(CORE_SECURITY_PRINCIPLES) - + if include_priorities: parts.append(VULNERABILITY_PRIORITIES) - + if include_tools: parts.append(TOOL_USAGE_GUIDE) - + return "\n\n".join(parts) __all__ = [ "CORE_SECURITY_PRINCIPLES", - "VULNERABILITY_PRIORITIES", + "VULNERABILITY_PRIORITIES", "TOOL_USAGE_GUIDE", "MULTI_AGENT_RULES", - "ORCHESTRATOR_SYSTEM_PROMPT", - "ANALYSIS_SYSTEM_PROMPT", - "VERIFICATION_SYSTEM_PROMPT", - "RECON_SYSTEM_PROMPT", - "get_system_prompt", "build_enhanced_prompt", ] diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..6613906 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,92 @@ +# ============================================= +# DeepAudit v3.0.0 生产环境一键部署配置 +# ============================================= +# 使用预构建的 GHCR 镜像,无需本地构建 +# 部署命令: curl -fsSL https://raw.githubusercontent.com/lintsinghua/DeepAudit/main/docker-compose.prod.yml | docker compose -f - up -d + +services: + db: + image: postgres:15-alpine + restart: unless-stopped + volumes: + - postgres_data:/var/lib/postgresql/data + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + - POSTGRES_DB=deepaudit + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - deepaudit-network + + redis: + image: redis:7-alpine + restart: unless-stopped + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - deepaudit-network + + backend: + image: ghcr.io/lintsinghua/deepaudit-backend:latest + restart: unless-stopped + volumes: + - backend_uploads:/app/uploads + - /var/run/docker.sock:/var/run/docker.sock + ports: + - "8000:8000" + environment: + - DATABASE_URL=postgresql+asyncpg://postgres:postgres@db:5432/deepaudit + - REDIS_URL=redis://redis:6379/0 + - AGENT_ENABLED=true + - SANDBOX_ENABLED=true + - SANDBOX_IMAGE=ghcr.io/lintsinghua/deepaudit-sandbox:latest + # LLM 配置 - 请根据需要修改 + - LLM_PROVIDER=openai + - LLM_MODEL=gpt-4o + - LLM_API_KEY=${LLM_API_KEY:-your-api-key-here} + - LLM_BASE_URL=${LLM_BASE_URL:-} + # 禁用代理 + - HTTP_PROXY= + - HTTPS_PROXY= + - NO_PROXY=* + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + networks: + - deepaudit-network + + frontend: + image: ghcr.io/lintsinghua/deepaudit-frontend:latest + restart: unless-stopped + ports: + - "3000:80" + depends_on: + - backend + networks: + - deepaudit-network + + # 预拉取沙箱镜像(后端会按需调用) + sandbox-pull: + image: ghcr.io/lintsinghua/deepaudit-sandbox:latest + restart: "no" + command: echo "Sandbox image ready" + +networks: + deepaudit-network: + driver: bridge + +volumes: + postgres_data: + backend_uploads: + redis_data: diff --git a/frontend/src/pages/AgentAudit/components/StatsPanel.tsx b/frontend/src/pages/AgentAudit/components/StatsPanel.tsx index 2b109b5..98956da 100644 --- a/frontend/src/pages/AgentAudit/components/StatsPanel.tsx +++ b/frontend/src/pages/AgentAudit/components/StatsPanel.tsx @@ -133,11 +133,20 @@ export const StatsPanel = memo(function StatsPanel({ task, findings }: StatsPane {/* File progress */}
- Files analyzed + Files scanned {task.analyzed_files}/{task.total_files}
+ {/* Files with findings */} + {task.files_with_findings > 0 && ( +
+ Files with findings + + {task.files_with_findings} + +
+ )} {/* Metrics Grid */} diff --git a/frontend/src/shared/api/agentTasks.ts b/frontend/src/shared/api/agentTasks.ts index 777caf1..f8e28af 100644 --- a/frontend/src/shared/api/agentTasks.ts +++ b/frontend/src/shared/api/agentTasks.ts @@ -21,6 +21,7 @@ export interface AgentTask { total_files: number; indexed_files: number; analyzed_files: number; + files_with_findings: number; // 有漏洞发现的文件数 total_chunks: number; findings_count: number; verified_count: number; @@ -128,6 +129,7 @@ export interface AgentTaskSummary { total_files: number; indexed_files: number; analyzed_files: number; + files_with_findings: number; total_chunks: number; findings_count: number; verified_count: number;