feat: Introduce Kunlun agent tool, add Docker and sandbox environment checks, and update agent services and frontend dialogs.
This commit is contained in:
parent
39a57b9c58
commit
2df1b39e08
|
|
@ -234,9 +234,17 @@ async def _execute_agent_task(task_id: str):
|
||||||
from app.services.agent.event_manager import EventManager, AgentEventEmitter
|
from app.services.agent.event_manager import EventManager, AgentEventEmitter
|
||||||
from app.services.llm.service import LLMService
|
from app.services.llm.service import LLMService
|
||||||
from app.services.agent.core import agent_registry
|
from app.services.agent.core import agent_registry
|
||||||
|
from app.services.agent.tools import SandboxManager
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
# 🔥 在任务最开始就初始化 Docker 沙箱管理器
|
||||||
|
# 这样可以确保整个任务生命周期内使用同一个管理器,并且尽早发现 Docker 问题
|
||||||
|
logger.info(f"🚀 Starting execution for task {task_id}")
|
||||||
|
sandbox_manager = SandboxManager()
|
||||||
|
await sandbox_manager.initialize()
|
||||||
|
logger.info(f"🐳 Global Sandbox Manager initialized (Available: {sandbox_manager.is_available})")
|
||||||
|
|
||||||
async with async_session_factory() as db:
|
async with async_session_factory() as db:
|
||||||
orchestrator = None
|
orchestrator = None
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
@ -275,11 +283,12 @@ async def _execute_agent_task(task_id: str):
|
||||||
# 创建 LLM 服务
|
# 创建 LLM 服务
|
||||||
llm_service = LLMService(user_config=user_config)
|
llm_service = LLMService(user_config=user_config)
|
||||||
|
|
||||||
# 初始化工具集 - 传递排除模式和目标文件
|
# 初始化工具集 - 传递排除模式和目标文件以及预初始化的 sandbox_manager
|
||||||
tools = await _initialize_tools(
|
tools = await _initialize_tools(
|
||||||
project_root,
|
project_root,
|
||||||
llm_service,
|
llm_service,
|
||||||
user_config,
|
user_config,
|
||||||
|
sandbox_manager=sandbox_manager,
|
||||||
exclude_patterns=task.exclude_patterns,
|
exclude_patterns=task.exclude_patterns,
|
||||||
target_files=task.target_files,
|
target_files=task.target_files,
|
||||||
)
|
)
|
||||||
|
|
@ -535,6 +544,7 @@ async def _initialize_tools(
|
||||||
project_root: str,
|
project_root: str,
|
||||||
llm_service,
|
llm_service,
|
||||||
user_config: Optional[Dict[str, Any]],
|
user_config: Optional[Dict[str, Any]],
|
||||||
|
sandbox_manager: Any, # 传递预初始化的 SandboxManager
|
||||||
exclude_patterns: Optional[List[str]] = None,
|
exclude_patterns: Optional[List[str]] = None,
|
||||||
target_files: Optional[List[str]] = None,
|
target_files: Optional[List[str]] = None,
|
||||||
) -> Dict[str, Dict[str, Any]]:
|
) -> Dict[str, Dict[str, Any]]:
|
||||||
|
|
@ -544,6 +554,7 @@ async def _initialize_tools(
|
||||||
project_root: 项目根目录
|
project_root: 项目根目录
|
||||||
llm_service: LLM 服务
|
llm_service: LLM 服务
|
||||||
user_config: 用户配置
|
user_config: 用户配置
|
||||||
|
sandbox_manager: 沙箱管理器
|
||||||
exclude_patterns: 排除模式列表
|
exclude_patterns: 排除模式列表
|
||||||
target_files: 目标文件列表
|
target_files: 目标文件列表
|
||||||
"""
|
"""
|
||||||
|
|
@ -551,6 +562,7 @@ async def _initialize_tools(
|
||||||
FileReadTool, FileSearchTool, ListFilesTool,
|
FileReadTool, FileSearchTool, ListFilesTool,
|
||||||
PatternMatchTool, CodeAnalysisTool, DataFlowAnalysisTool,
|
PatternMatchTool, CodeAnalysisTool, DataFlowAnalysisTool,
|
||||||
SemgrepTool, BanditTool, GitleaksTool,
|
SemgrepTool, BanditTool, GitleaksTool,
|
||||||
|
NpmAuditTool, SafetyTool, TruffleHogTool, OSVScannerTool, # 🔥 Added missing tools
|
||||||
ThinkTool, ReflectTool,
|
ThinkTool, ReflectTool,
|
||||||
CreateVulnerabilityReportTool,
|
CreateVulnerabilityReportTool,
|
||||||
VulnerabilityValidationTool,
|
VulnerabilityValidationTool,
|
||||||
|
|
@ -572,6 +584,14 @@ async def _initialize_tools(
|
||||||
# Recon 工具
|
# Recon 工具
|
||||||
recon_tools = {
|
recon_tools = {
|
||||||
**base_tools,
|
**base_tools,
|
||||||
|
# 🔥 外部侦察工具 (Recon 阶段也需要使用这些工具来收集初步信息)
|
||||||
|
"semgrep_scan": SemgrepTool(project_root, sandbox_manager),
|
||||||
|
"bandit_scan": BanditTool(project_root, sandbox_manager),
|
||||||
|
"gitleaks_scan": GitleaksTool(project_root, sandbox_manager),
|
||||||
|
"npm_audit": NpmAuditTool(project_root, sandbox_manager),
|
||||||
|
"safety_scan": SafetyTool(project_root, sandbox_manager),
|
||||||
|
"trufflehog_scan": TruffleHogTool(project_root, sandbox_manager),
|
||||||
|
"osv_scan": OSVScannerTool(project_root, sandbox_manager),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Analysis 工具
|
# Analysis 工具
|
||||||
|
|
@ -587,10 +607,14 @@ async def _initialize_tools(
|
||||||
"pattern_match": PatternMatchTool(project_root),
|
"pattern_match": PatternMatchTool(project_root),
|
||||||
# 数据流分析
|
# 数据流分析
|
||||||
"dataflow_analysis": DataFlowAnalysisTool(llm_service),
|
"dataflow_analysis": DataFlowAnalysisTool(llm_service),
|
||||||
# 外部安全工具
|
# 外部安全工具 (传入共享的 sandbox_manager)
|
||||||
"semgrep_scan": SemgrepTool(project_root),
|
"semgrep_scan": SemgrepTool(project_root, sandbox_manager),
|
||||||
"bandit_scan": BanditTool(project_root),
|
"bandit_scan": BanditTool(project_root, sandbox_manager),
|
||||||
"gitleaks_scan": GitleaksTool(project_root),
|
"gitleaks_scan": GitleaksTool(project_root, sandbox_manager),
|
||||||
|
"npm_audit": NpmAuditTool(project_root, sandbox_manager),
|
||||||
|
"safety_scan": SafetyTool(project_root, sandbox_manager),
|
||||||
|
"trufflehog_scan": TruffleHogTool(project_root, sandbox_manager),
|
||||||
|
"osv_scan": OSVScannerTool(project_root, sandbox_manager),
|
||||||
# 安全知识查询
|
# 安全知识查询
|
||||||
"query_security_knowledge": SecurityKnowledgeQueryTool(),
|
"query_security_knowledge": SecurityKnowledgeQueryTool(),
|
||||||
"get_vulnerability_knowledge": GetVulnerabilityKnowledgeTool(),
|
"get_vulnerability_knowledge": GetVulnerabilityKnowledgeTool(),
|
||||||
|
|
@ -599,7 +623,7 @@ async def _initialize_tools(
|
||||||
# Verification 工具
|
# Verification 工具
|
||||||
# 🔥 导入沙箱工具
|
# 🔥 导入沙箱工具
|
||||||
from app.services.agent.tools import (
|
from app.services.agent.tools import (
|
||||||
SandboxTool, SandboxHttpTool, VulnerabilityVerifyTool, SandboxManager,
|
SandboxTool, SandboxHttpTool, VulnerabilityVerifyTool,
|
||||||
# 多语言代码测试工具
|
# 多语言代码测试工具
|
||||||
PhpTestTool, PythonTestTool, JavaScriptTestTool, JavaTestTool,
|
PhpTestTool, PythonTestTool, JavaScriptTestTool, JavaTestTool,
|
||||||
GoTestTool, RubyTestTool, ShellTestTool, UniversalCodeTestTool,
|
GoTestTool, RubyTestTool, ShellTestTool, UniversalCodeTestTool,
|
||||||
|
|
@ -609,11 +633,6 @@ async def _initialize_tools(
|
||||||
UniversalVulnTestTool,
|
UniversalVulnTestTool,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 🔥 初始化沙箱管理器
|
|
||||||
sandbox_manager = SandboxManager()
|
|
||||||
await sandbox_manager.initialize()
|
|
||||||
logger.info(f"✅ Sandbox initialized (available: {sandbox_manager.is_available})")
|
|
||||||
|
|
||||||
verification_tools = {
|
verification_tools = {
|
||||||
**base_tools,
|
**base_tools,
|
||||||
# 🔥 沙箱验证工具
|
# 🔥 沙箱验证工具
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,42 @@ ANALYSIS_SYSTEM_PROMPT = """你是 DeepAudit 的漏洞分析 Agent,一个**自
|
||||||
4. 判断是否是真实漏洞
|
4. 判断是否是真实漏洞
|
||||||
5. 动态调整分析方向
|
5. 动态调整分析方向
|
||||||
|
|
||||||
## 你可以使用的工具
|
## ⚠️ 核心原则:优先使用外部专业工具!
|
||||||
|
|
||||||
### 🚀 智能扫描工具(推荐优先使用)
|
**外部工具优先级最高!** 必须首先使用外部安全工具进行扫描,它们有:
|
||||||
- **smart_scan**: 智能批量安全扫描 ⭐ 首选工具!
|
- 经过验证的专业规则库
|
||||||
|
- 更低的误报率
|
||||||
|
- 更全面的漏洞检测能力
|
||||||
|
|
||||||
|
## 🔧 工具优先级(必须按此顺序使用)
|
||||||
|
|
||||||
|
### 第一优先级:外部专业安全工具 ⭐⭐⭐ 【必须首先使用!】
|
||||||
|
- **semgrep_scan**: 全语言静态分析 - **每次分析必用**
|
||||||
|
参数: target_path (str), rules (str: "auto" 或 "p/security-audit")
|
||||||
|
示例: {"target_path": ".", "rules": "auto"}
|
||||||
|
|
||||||
|
- **bandit_scan**: Python 安全扫描 - **Python项目必用**
|
||||||
|
参数: target_path (str), severity (str)
|
||||||
|
示例: {"target_path": ".", "severity": "medium"}
|
||||||
|
|
||||||
|
- **gitleaks_scan**: 密钥泄露检测 - **每次分析必用**
|
||||||
|
参数: target_path (str)
|
||||||
|
示例: {"target_path": "."}
|
||||||
|
|
||||||
|
- **safety_scan**: Python 依赖漏洞 - **有 requirements.txt 时必用**
|
||||||
|
参数: requirements_file (str)
|
||||||
|
示例: {"requirements_file": "requirements.txt"}
|
||||||
|
|
||||||
|
- **npm_audit**: Node.js 依赖漏洞 - **有 package.json 时必用**
|
||||||
|
参数: target_path (str)
|
||||||
|
示例: {"target_path": "."}
|
||||||
|
|
||||||
|
- **kunlun_scan**: 深度代码审计(Kunlun-M)
|
||||||
|
参数: target_path (str), language (str: "php"|"javascript")
|
||||||
|
示例: {"target_path": ".", "language": "php"}
|
||||||
|
|
||||||
|
### 第二优先级:智能扫描工具 ⭐⭐
|
||||||
|
- **smart_scan**: 智能批量安全扫描
|
||||||
参数: target (str), quick_mode (bool), focus_vulnerabilities (list)
|
参数: target (str), quick_mode (bool), focus_vulnerabilities (list)
|
||||||
示例: {"target": ".", "quick_mode": true}
|
示例: {"target": ".", "quick_mode": true}
|
||||||
|
|
||||||
|
|
@ -45,33 +77,68 @@ ANALYSIS_SYSTEM_PROMPT = """你是 DeepAudit 的漏洞分析 Agent,一个**自
|
||||||
参数: file_path (str), deep_analysis (bool)
|
参数: file_path (str), deep_analysis (bool)
|
||||||
示例: {"file_path": "app/views.py", "deep_analysis": true}
|
示例: {"file_path": "app/views.py", "deep_analysis": true}
|
||||||
|
|
||||||
### 文件操作
|
### 第三优先级:内置分析工具 ⭐
|
||||||
- **read_file**: 读取文件内容
|
- **pattern_match**: 危险模式匹配(外部工具不可用时的备选)
|
||||||
|
参数: scan_file (str) 或 code (str), pattern_types (list)
|
||||||
|
示例: {"scan_file": "app/models.py", "pattern_types": ["sql_injection"]}
|
||||||
|
|
||||||
|
- **dataflow_analysis**: 数据流追踪
|
||||||
|
参数: source_code (str), variable_name (str)
|
||||||
|
|
||||||
|
### 辅助工具
|
||||||
|
- **read_file**: 读取文件内容验证发现
|
||||||
参数: file_path (str), start_line (int), end_line (int)
|
参数: file_path (str), start_line (int), end_line (int)
|
||||||
- **list_files**: 列出目录文件
|
- **list_files**: 列出目录文件
|
||||||
参数: directory (str), pattern (str)
|
参数: directory (str), pattern (str)
|
||||||
- **search_code**: 代码关键字搜索
|
- **search_code**: 代码关键字搜索
|
||||||
参数: keyword (str), max_results (int)
|
参数: keyword (str), max_results (int)
|
||||||
|
|
||||||
### 深度分析
|
|
||||||
- **pattern_match**: 危险模式匹配(支持直接扫描文件)
|
|
||||||
参数: scan_file (str) 或 code (str), pattern_types (list)
|
|
||||||
示例: {"scan_file": "app/models.py", "pattern_types": ["sql_injection"]}
|
|
||||||
- **dataflow_analysis**: 数据流追踪
|
|
||||||
参数: source_code (str), variable_name (str)
|
|
||||||
|
|
||||||
### 外部静态分析工具
|
|
||||||
- **semgrep_scan**: Semgrep 静态分析
|
|
||||||
参数: target_path (str), rules (str)
|
|
||||||
- **bandit_scan**: Python 安全扫描
|
|
||||||
参数: target_path (str)
|
|
||||||
- **gitleaks_scan**: Git 密钥泄露扫描
|
|
||||||
参数: target_path (str)
|
|
||||||
|
|
||||||
### 安全知识查询
|
|
||||||
- **query_security_knowledge**: 查询安全知识库
|
- **query_security_knowledge**: 查询安全知识库
|
||||||
- **get_vulnerability_knowledge**: 获取漏洞知识
|
- **get_vulnerability_knowledge**: 获取漏洞知识
|
||||||
|
|
||||||
|
## 📋 推荐分析流程(严格按此执行!)
|
||||||
|
|
||||||
|
### 第一步:外部工具全面扫描(60%时间)⚡ 最重要!
|
||||||
|
根据项目技术栈,**必须首先**执行以下外部工具:
|
||||||
|
|
||||||
|
```
|
||||||
|
# 所有项目必做
|
||||||
|
Action: semgrep_scan
|
||||||
|
Action Input: {"target_path": ".", "rules": "auto"}
|
||||||
|
|
||||||
|
Action: gitleaks_scan
|
||||||
|
Action Input: {"target_path": "."}
|
||||||
|
|
||||||
|
# Python 项目必做
|
||||||
|
Action: bandit_scan
|
||||||
|
Action Input: {"target_path": ".", "severity": "medium"}
|
||||||
|
|
||||||
|
Action: safety_scan
|
||||||
|
Action Input: {"requirements_file": "requirements.txt"}
|
||||||
|
|
||||||
|
# Node.js 项目必做
|
||||||
|
Action: npm_audit
|
||||||
|
Action Input: {"target_path": "."}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 第二步:分析外部工具结果(25%时间)
|
||||||
|
对外部工具发现的问题进行深入分析:
|
||||||
|
- 使用 `read_file` 查看完整代码上下文
|
||||||
|
- 使用 `dataflow_analysis` 追踪数据流
|
||||||
|
- 验证是否为真实漏洞,排除误报
|
||||||
|
|
||||||
|
### 第三步:补充扫描(10%时间)
|
||||||
|
如果外部工具覆盖不足,使用内置工具补充:
|
||||||
|
- `smart_scan` 综合扫描
|
||||||
|
- `pattern_match` 模式匹配
|
||||||
|
|
||||||
|
### 第四步:汇总报告(5%时间)
|
||||||
|
整理所有发现,输出 Final Answer
|
||||||
|
|
||||||
|
## ⚠️ 重要提醒
|
||||||
|
1. **不要跳过外部工具!** 即使内置工具可能更快,外部工具的检测能力更强
|
||||||
|
2. **Docker依赖**:外部工具需要Docker环境,如果返回"Docker不可用",再使用内置工具
|
||||||
|
3. **并行执行**:可以连续调用多个外部工具
|
||||||
|
|
||||||
## 工作方式
|
## 工作方式
|
||||||
每一步,你需要输出:
|
每一步,你需要输出:
|
||||||
|
|
||||||
|
|
@ -111,21 +178,6 @@ Final Answer: [JSON 格式的漏洞报告]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## ⭐ 推荐分析策略
|
|
||||||
1. **第一步:智能扫描**
|
|
||||||
使用 smart_scan 快速获取项目安全概览
|
|
||||||
示例: {"target": ".", "quick_mode": true}
|
|
||||||
|
|
||||||
2. **第二步:重点审计**
|
|
||||||
对 smart_scan 发现的高风险文件使用 quick_audit 深入分析
|
|
||||||
示例: {"file_path": "发现的高风险文件", "deep_analysis": true}
|
|
||||||
|
|
||||||
3. **第三步:验证分析**
|
|
||||||
使用 read_file 查看完整上下文,用 dataflow_analysis 追踪数据流
|
|
||||||
|
|
||||||
4. **第四步:汇总报告**
|
|
||||||
整理所有发现,输出 Final Answer
|
|
||||||
|
|
||||||
## 重点关注的漏洞类型
|
## 重点关注的漏洞类型
|
||||||
- SQL 注入 (query, execute, raw SQL)
|
- SQL 注入 (query, execute, raw SQL)
|
||||||
- XSS (innerHTML, document.write, v-html)
|
- XSS (innerHTML, document.write, v-html)
|
||||||
|
|
@ -136,12 +188,12 @@ Final Answer: [JSON 格式的漏洞报告]
|
||||||
- 不安全的反序列化 (pickle, yaml.load, eval)
|
- 不安全的反序列化 (pickle, yaml.load, eval)
|
||||||
|
|
||||||
## 重要原则
|
## 重要原则
|
||||||
1. **质量优先** - 宁可深入分析几个真实漏洞,不要浅尝辄止报告大量误报
|
1. **外部工具优先** - 首先使用 semgrep、bandit 等专业工具
|
||||||
2. **上下文分析** - 看到可疑代码要读取上下文,理解完整逻辑
|
2. **质量优先** - 宁可深入分析几个真实漏洞,不要浅尝辄止报告大量误报
|
||||||
3. **自主判断** - 不要机械相信工具输出,要用你的专业知识判断
|
3. **上下文分析** - 看到可疑代码要读取上下文,理解完整逻辑
|
||||||
4. **持续探索** - 发现一个问题后,思考是否有相关问题
|
4. **自主判断** - 不要机械相信工具输出,要用你的专业知识判断
|
||||||
|
|
||||||
现在开始你的安全分析!"""
|
现在开始你的安全分析!首先使用外部工具进行全面扫描。"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,11 @@ class AgentConfig(BaseSettings):
|
||||||
default=True,
|
default=True,
|
||||||
description="Enable OSV scanner"
|
description="Enable OSV scanner"
|
||||||
)
|
)
|
||||||
|
# Kunlun-M (MIT License - https://github.com/LoRexxar/Kunlun-M)
|
||||||
|
kunlun_enabled: bool = Field(
|
||||||
|
default=True,
|
||||||
|
description="Enable Kunlun-M static code analyzer"
|
||||||
|
)
|
||||||
|
|
||||||
# External Tool Timeouts
|
# External Tool Timeouts
|
||||||
semgrep_timeout_seconds: int = Field(
|
semgrep_timeout_seconds: int = Field(
|
||||||
|
|
@ -139,6 +144,10 @@ class AgentConfig(BaseSettings):
|
||||||
default=60,
|
default=60,
|
||||||
description="Timeout for Gitleaks scanner"
|
description="Timeout for Gitleaks scanner"
|
||||||
)
|
)
|
||||||
|
kunlun_timeout_seconds: int = Field(
|
||||||
|
default=600,
|
||||||
|
description="Timeout for Kunlun-M scanner (10 minutes for deep analysis)"
|
||||||
|
)
|
||||||
|
|
||||||
# ============ Rate Limiting ============
|
# ============ Rate Limiting ============
|
||||||
rate_limit_enabled: bool = Field(
|
rate_limit_enabled: bool = Field(
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,8 @@ class AgentRunner:
|
||||||
CommandInjectionTestTool, SqlInjectionTestTool, XssTestTool,
|
CommandInjectionTestTool, SqlInjectionTestTool, XssTestTool,
|
||||||
PathTraversalTestTool, SstiTestTool, DeserializationTestTool,
|
PathTraversalTestTool, SstiTestTool, DeserializationTestTool,
|
||||||
UniversalVulnTestTool,
|
UniversalVulnTestTool,
|
||||||
|
# Kunlun-M 静态代码分析工具 (MIT License)
|
||||||
|
KunlunMTool, KunlunRuleListTool, KunlunPluginTool,
|
||||||
)
|
)
|
||||||
# 🔥 导入知识查询工具
|
# 🔥 导入知识查询工具
|
||||||
from app.services.agent.knowledge import (
|
from app.services.agent.knowledge import (
|
||||||
|
|
@ -215,6 +217,33 @@ class AgentRunner:
|
||||||
exclude_patterns = self.task.exclude_patterns or []
|
exclude_patterns = self.task.exclude_patterns or []
|
||||||
target_files = self.task.target_files or None
|
target_files = self.task.target_files or None
|
||||||
|
|
||||||
|
# ============ 🔥 提前初始化 SandboxManager(供所有外部工具共享)============
|
||||||
|
self.sandbox_manager = None
|
||||||
|
try:
|
||||||
|
from app.services.agent.tools.sandbox_tool import SandboxConfig
|
||||||
|
sandbox_config = SandboxConfig(
|
||||||
|
image=settings.SANDBOX_IMAGE,
|
||||||
|
memory_limit=settings.SANDBOX_MEMORY_LIMIT,
|
||||||
|
cpu_limit=settings.SANDBOX_CPU_LIMIT,
|
||||||
|
timeout=settings.SANDBOX_TIMEOUT,
|
||||||
|
network_mode=settings.SANDBOX_NETWORK_MODE,
|
||||||
|
)
|
||||||
|
self.sandbox_manager = SandboxManager(config=sandbox_config)
|
||||||
|
# 🔥 必须调用 initialize() 来连接 Docker
|
||||||
|
await self.sandbox_manager.initialize()
|
||||||
|
logger.info(f"✅ SandboxManager initialized early (Docker available: {self.sandbox_manager.is_available})")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"❌ Early Sandbox Manager initialization failed: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.warning(f"Traceback: {traceback.format_exc()}")
|
||||||
|
# 尝试创建默认管理器作为后备
|
||||||
|
try:
|
||||||
|
self.sandbox_manager = SandboxManager()
|
||||||
|
await self.sandbox_manager.initialize()
|
||||||
|
logger.info(f"⚠️ Created fallback SandboxManager (Docker available: {self.sandbox_manager.is_available})")
|
||||||
|
except Exception as e2:
|
||||||
|
logger.error(f"❌ Failed to create fallback SandboxManager: {e2}")
|
||||||
|
|
||||||
# ============ 基础工具(所有 Agent 共享)============
|
# ============ 基础工具(所有 Agent 共享)============
|
||||||
base_tools = {
|
base_tools = {
|
||||||
"read_file": FileReadTool(self.project_root, exclude_patterns, target_files),
|
"read_file": FileReadTool(self.project_root, exclude_patterns, target_files),
|
||||||
|
|
@ -225,11 +254,18 @@ class AgentRunner:
|
||||||
|
|
||||||
# ============ Recon Agent 专属工具 ============
|
# ============ Recon Agent 专属工具 ============
|
||||||
# 职责:信息收集、项目结构分析、技术栈识别
|
# 职责:信息收集、项目结构分析、技术栈识别
|
||||||
|
# 🔥 新增:外部工具也可用于Recon阶段的快速扫描
|
||||||
self.recon_tools = {
|
self.recon_tools = {
|
||||||
**base_tools,
|
**base_tools,
|
||||||
"search_code": FileSearchTool(self.project_root, exclude_patterns, target_files),
|
"search_code": FileSearchTool(self.project_root, exclude_patterns, target_files),
|
||||||
# 🔥 新增:反思工具
|
# 🔥 新增:反思工具
|
||||||
"reflect": ReflectTool(),
|
"reflect": ReflectTool(),
|
||||||
|
# 🔥 外部安全工具(共享 SandboxManager 实例)
|
||||||
|
"semgrep_scan": SemgrepTool(self.project_root, self.sandbox_manager),
|
||||||
|
"bandit_scan": BanditTool(self.project_root, self.sandbox_manager),
|
||||||
|
"gitleaks_scan": GitleaksTool(self.project_root, self.sandbox_manager),
|
||||||
|
"safety_scan": SafetyTool(self.project_root, self.sandbox_manager),
|
||||||
|
"npm_audit": NpmAuditTool(self.project_root, self.sandbox_manager),
|
||||||
}
|
}
|
||||||
|
|
||||||
# RAG 工具(Recon 用于语义搜索)
|
# RAG 工具(Recon 用于语义搜索)
|
||||||
|
|
@ -246,14 +282,18 @@ class AgentRunner:
|
||||||
# TODO: code_analysis 工具暂时禁用,因为 LLM 调用经常失败
|
# TODO: code_analysis 工具暂时禁用,因为 LLM 调用经常失败
|
||||||
# "code_analysis": CodeAnalysisTool(self.llm_service),
|
# "code_analysis": CodeAnalysisTool(self.llm_service),
|
||||||
"dataflow_analysis": DataFlowAnalysisTool(self.llm_service),
|
"dataflow_analysis": DataFlowAnalysisTool(self.llm_service),
|
||||||
# 外部静态分析工具
|
# 🔥 外部静态分析工具(共享 SandboxManager 实例)
|
||||||
"semgrep_scan": SemgrepTool(self.project_root),
|
"semgrep_scan": SemgrepTool(self.project_root, self.sandbox_manager),
|
||||||
"bandit_scan": BanditTool(self.project_root),
|
"bandit_scan": BanditTool(self.project_root, self.sandbox_manager),
|
||||||
"gitleaks_scan": GitleaksTool(self.project_root),
|
"gitleaks_scan": GitleaksTool(self.project_root, self.sandbox_manager),
|
||||||
"trufflehog_scan": TruffleHogTool(self.project_root),
|
"trufflehog_scan": TruffleHogTool(self.project_root, self.sandbox_manager),
|
||||||
"npm_audit": NpmAuditTool(self.project_root),
|
"npm_audit": NpmAuditTool(self.project_root, self.sandbox_manager),
|
||||||
"safety_scan": SafetyTool(self.project_root),
|
"safety_scan": SafetyTool(self.project_root, self.sandbox_manager),
|
||||||
"osv_scan": OSVScannerTool(self.project_root),
|
"osv_scan": OSVScannerTool(self.project_root, self.sandbox_manager),
|
||||||
|
# 🔥 Kunlun-M 静态代码分析工具 (MIT License - https://github.com/LoRexxar/Kunlun-M)
|
||||||
|
"kunlun_scan": KunlunMTool(self.project_root),
|
||||||
|
"kunlun_list_rules": KunlunRuleListTool(self.project_root),
|
||||||
|
"kunlun_plugin": KunlunPluginTool(self.project_root),
|
||||||
# 🔥 新增:反思工具
|
# 🔥 新增:反思工具
|
||||||
"reflect": ReflectTool(),
|
"reflect": ReflectTool(),
|
||||||
# 🔥 新增:安全知识查询工具(基于RAG)
|
# 🔥 新增:安全知识查询工具(基于RAG)
|
||||||
|
|
@ -277,34 +317,7 @@ class AgentRunner:
|
||||||
"reflect": ReflectTool(),
|
"reflect": ReflectTool(),
|
||||||
}
|
}
|
||||||
|
|
||||||
# 沙箱工具(仅 Verification Agent 可用)
|
# 🔥 注册沙箱工具(使用提前初始化的 SandboxManager)
|
||||||
self.sandbox_manager = None
|
|
||||||
try:
|
|
||||||
from app.services.agent.tools.sandbox_tool import SandboxConfig
|
|
||||||
sandbox_config = SandboxConfig(
|
|
||||||
image=settings.SANDBOX_IMAGE,
|
|
||||||
memory_limit=settings.SANDBOX_MEMORY_LIMIT,
|
|
||||||
cpu_limit=settings.SANDBOX_CPU_LIMIT,
|
|
||||||
timeout=settings.SANDBOX_TIMEOUT,
|
|
||||||
network_mode=settings.SANDBOX_NETWORK_MODE,
|
|
||||||
)
|
|
||||||
self.sandbox_manager = SandboxManager(config=sandbox_config)
|
|
||||||
# 🔥 必须调用 initialize() 来连接 Docker
|
|
||||||
await self.sandbox_manager.initialize()
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"❌ Sandbox Manager initialization failed: {e}")
|
|
||||||
import traceback
|
|
||||||
logger.warning(f"Traceback: {traceback.format_exc()}")
|
|
||||||
# 尝试创建默认管理器作为后备
|
|
||||||
try:
|
|
||||||
self.sandbox_manager = SandboxManager()
|
|
||||||
# 🔥 同样需要调用 initialize()
|
|
||||||
await self.sandbox_manager.initialize()
|
|
||||||
logger.info("⚠️ Created fallback SandboxManager (Docker might be unavailable)")
|
|
||||||
except Exception as e2:
|
|
||||||
logger.error(f"❌ Failed to create fallback SandboxManager: {e2}")
|
|
||||||
|
|
||||||
# 始终注册沙箱工具,即使 Docker 不可用(工具内部会检查)
|
|
||||||
if self.sandbox_manager:
|
if self.sandbox_manager:
|
||||||
# 🔥 沙箱核心工具
|
# 🔥 沙箱核心工具
|
||||||
self.verification_tools["sandbox_exec"] = SandboxTool(self.sandbox_manager)
|
self.verification_tools["sandbox_exec"] = SandboxTool(self.sandbox_manager)
|
||||||
|
|
|
||||||
|
|
@ -102,26 +102,104 @@ TOOL_USAGE_GUIDE = """
|
||||||
<tool_usage_guide>
|
<tool_usage_guide>
|
||||||
## 工具使用指南
|
## 工具使用指南
|
||||||
|
|
||||||
### 分析流程
|
### ⚠️ 核心原则:优先使用外部专业工具
|
||||||
|
|
||||||
1. **初始侦察** - 了解项目结构
|
**外部工具优先级最高!** 外部安全工具(Semgrep、Bandit、Gitleaks、Kunlun-M 等)是经过业界验证的专业工具,具有:
|
||||||
- 使用 list_files 了解目录布局
|
- 更全面的规则库和漏洞检测能力
|
||||||
- 识别主要入口点和配置文件
|
- 更低的误报率
|
||||||
- 确定技术栈和框架
|
- 更专业的安全分析算法
|
||||||
|
- 持续更新的安全规则
|
||||||
|
|
||||||
2. **智能扫描** - 快速发现热点
|
**必须优先调用外部工具,而非依赖内置的模式匹配!**
|
||||||
- smart_scan: 综合扫描发现高风险区域
|
|
||||||
- pattern_match: 识别危险代码模式
|
|
||||||
- semgrep_scan/bandit_scan: 静态分析
|
|
||||||
|
|
||||||
3. **深度分析** - 验证发现
|
### 🔧 工具优先级(从高到低)
|
||||||
- read_file: 读取完整上下文
|
|
||||||
- dataflow_analysis: 追踪数据流
|
|
||||||
- search_code: 搜索相关代码
|
|
||||||
|
|
||||||
4. **知识查询** - 获取专业知识
|
#### 第一优先级:外部专业安全工具 ⭐⭐⭐
|
||||||
- query_security_knowledge: 搜索安全知识库
|
| 工具 | 用途 | 何时使用 |
|
||||||
- get_vulnerability_knowledge: 获取特定漏洞信息
|
|------|------|---------|
|
||||||
|
| `semgrep_scan` | 多语言静态分析 | **每次分析必用**,支持30+语言,OWASP规则 |
|
||||||
|
| `bandit_scan` | Python安全扫描 | Python项目**必用**,检测注入/反序列化等 |
|
||||||
|
| `gitleaks_scan` | 密钥泄露检测 | **每次分析必用**,检测150+种密钥类型 |
|
||||||
|
| `kunlun_scan` | 深度代码审计 | 大型项目推荐,支持PHP/Java/JS深度分析 |
|
||||||
|
| `npm_audit` | Node.js依赖漏洞 | package.json项目**必用** |
|
||||||
|
| `safety_scan` | Python依赖漏洞 | requirements.txt项目**必用** |
|
||||||
|
| `osv_scan` | 开源漏洞扫描 | 多语言依赖检查 |
|
||||||
|
| `trufflehog_scan` | 深度密钥扫描 | 需要验证密钥有效性时使用 |
|
||||||
|
|
||||||
|
#### 第二优先级:智能扫描工具 ⭐⭐
|
||||||
|
| 工具 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `smart_scan` | 综合智能扫描,快速定位高风险区域 |
|
||||||
|
| `quick_audit` | 快速审计模式 |
|
||||||
|
|
||||||
|
#### 第三优先级:内置分析工具 ⭐
|
||||||
|
| 工具 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `pattern_match` | 正则模式匹配(外部工具不可用时的备选) |
|
||||||
|
| `dataflow_analysis` | 数据流追踪验证 |
|
||||||
|
| `code_analysis` | 代码结构分析 |
|
||||||
|
|
||||||
|
#### 辅助工具
|
||||||
|
| 工具 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `list_files` | 了解项目结构 |
|
||||||
|
| `read_file` | 读取文件内容验证发现 |
|
||||||
|
| `search_code` | 搜索相关代码 |
|
||||||
|
| `query_security_knowledge` | 查询安全知识库 |
|
||||||
|
|
||||||
|
### 📋 推荐分析流程
|
||||||
|
|
||||||
|
#### 第一步:快速侦察(5%时间)
|
||||||
|
```
|
||||||
|
Action: list_files
|
||||||
|
Action Input: {"path": "."}
|
||||||
|
```
|
||||||
|
了解项目结构、技术栈、入口点
|
||||||
|
|
||||||
|
#### 第二步:外部工具全面扫描(60%时间)⚡重点!
|
||||||
|
**根据技术栈选择对应工具,并行执行多个扫描:**
|
||||||
|
|
||||||
|
```
|
||||||
|
# 通用项目(必做)
|
||||||
|
Action: semgrep_scan
|
||||||
|
Action Input: {"target_path": ".", "rules": "p/security-audit"}
|
||||||
|
|
||||||
|
Action: gitleaks_scan
|
||||||
|
Action Input: {"target_path": "."}
|
||||||
|
|
||||||
|
# Python项目(必做)
|
||||||
|
Action: bandit_scan
|
||||||
|
Action Input: {"target_path": ".", "severity": "medium"}
|
||||||
|
|
||||||
|
Action: safety_scan
|
||||||
|
Action Input: {"requirements_file": "requirements.txt"}
|
||||||
|
|
||||||
|
# Node.js项目(必做)
|
||||||
|
Action: npm_audit
|
||||||
|
Action Input: {"target_path": "."}
|
||||||
|
|
||||||
|
# 大型项目(推荐)
|
||||||
|
Action: kunlun_scan
|
||||||
|
Action Input: {"target_path": ".", "rules": "all"}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 第三步:深度分析(25%时间)
|
||||||
|
对外部工具发现的问题进行深入分析:
|
||||||
|
- 使用 `read_file` 查看完整上下文
|
||||||
|
- 使用 `dataflow_analysis` 追踪数据流
|
||||||
|
- 验证是否为真实漏洞
|
||||||
|
|
||||||
|
#### 第四步:验证和报告(10%时间)
|
||||||
|
- 确认漏洞可利用性
|
||||||
|
- 评估影响范围
|
||||||
|
- 生成修复建议
|
||||||
|
|
||||||
|
### ⚠️ 重要提醒
|
||||||
|
|
||||||
|
1. **不要跳过外部工具!** 即使内置模式匹配可能更快,外部工具的检测能力更强
|
||||||
|
2. **并行执行**:可以同时调用多个不相关的外部工具以提高效率
|
||||||
|
3. **Docker依赖**:外部工具需要Docker环境,如果Docker不可用,再回退到内置工具
|
||||||
|
4. **结果整合**:综合多个工具的结果,交叉验证提高准确性
|
||||||
|
|
||||||
### 工具调用格式
|
### 工具调用格式
|
||||||
|
|
||||||
|
|
@ -268,22 +346,43 @@ ANALYSIS_SYSTEM_PROMPT = f"""你是 DeepAudit 的漏洞分析 Agent,一个专
|
||||||
|
|
||||||
## 分析策略
|
## 分析策略
|
||||||
|
|
||||||
### 第一步:快速扫描
|
### ⚠️ 核心原则:外部工具优先!
|
||||||
使用 smart_scan 或 pattern_match 快速识别高风险区域
|
|
||||||
|
|
||||||
### 第二步:深度分析
|
**必须首先使用外部专业安全工具进行扫描!** 这些工具有经过验证的规则库和更低的误报率。
|
||||||
对可疑代码进行上下文分析:
|
|
||||||
- 读取相关文件
|
|
||||||
- 追踪数据流
|
|
||||||
- 理解业务逻辑
|
|
||||||
|
|
||||||
### 第三步:验证判断
|
### 第一步:外部工具全面扫描(最重要!)⭐⭐⭐
|
||||||
- 确认是否为真实漏洞
|
**根据项目技术栈,选择并执行以下工具:**
|
||||||
- 评估可利用性
|
|
||||||
- 确定置信度
|
|
||||||
|
|
||||||
### 第四步:报告发现
|
**所有项目必做:**
|
||||||
输出结构化的漏洞报告
|
- `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`: 正则模式匹配
|
||||||
|
|
||||||
|
### 第四步:验证和报告
|
||||||
|
- 确认漏洞可利用性
|
||||||
|
- 评估实际影响
|
||||||
|
- 输出结构化的漏洞报告
|
||||||
|
|
||||||
## 输出格式
|
## 输出格式
|
||||||
|
|
||||||
|
|
@ -330,25 +429,31 @@ VERIFICATION_SYSTEM_PROMPT = f"""你是 DeepAudit 的验证 Agent,负责验证
|
||||||
|
|
||||||
## 验证方法
|
## 验证方法
|
||||||
|
|
||||||
### 1. 上下文验证
|
### 1. 外部工具交叉验证 ⭐⭐⭐(推荐!)
|
||||||
|
使用不同的外部工具验证发现:
|
||||||
|
- 使用 `semgrep_scan` 配合特定规则验证
|
||||||
|
- 使用 `bandit_scan` 交叉确认 Python 漏洞
|
||||||
|
- 如果多个工具都报告同一问题,置信度更高
|
||||||
|
|
||||||
|
### 2. 上下文验证
|
||||||
- 检查完整的代码上下文
|
- 检查完整的代码上下文
|
||||||
- 理解数据处理逻辑
|
- 理解数据处理逻辑
|
||||||
- 验证安全控制是否存在
|
- 验证安全控制是否存在
|
||||||
|
|
||||||
### 2. 数据流验证
|
### 3. 数据流验证
|
||||||
- 追踪从输入到输出的完整路径
|
- 追踪从输入到输出的完整路径
|
||||||
- 识别中间的验证和过滤
|
- 识别中间的验证和过滤
|
||||||
- 确认是否存在有效的安全控制
|
- 确认是否存在有效的安全控制
|
||||||
|
|
||||||
### 3. 配置验证
|
### 4. 配置验证
|
||||||
- 检查安全配置
|
- 检查安全配置
|
||||||
- 验证框架安全特性
|
- 验证框架安全特性
|
||||||
- 评估防护措施
|
- 评估防护措施
|
||||||
|
|
||||||
### 4. 模式验证
|
### 5. 沙箱验证(高置信度漏洞)
|
||||||
- 对比已知漏洞模式
|
- 使用 `sandbox_execute` 或漏洞专用测试工具
|
||||||
- 检查类似代码位置
|
- 构造 PoC 验证可利用性
|
||||||
- 评估误报可能性
|
- 记录验证结果
|
||||||
|
|
||||||
## 输出格式
|
## 输出格式
|
||||||
|
|
||||||
|
|
@ -359,6 +464,7 @@ Final Answer: {{
|
||||||
"original_finding": {{...}},
|
"original_finding": {{...}},
|
||||||
"is_verified": true/false,
|
"is_verified": true/false,
|
||||||
"verification_method": "使用的验证方法",
|
"verification_method": "使用的验证方法",
|
||||||
|
"cross_tool_results": {{"semgrep": "...", "bandit": "..."}},
|
||||||
"evidence": "验证证据",
|
"evidence": "验证证据",
|
||||||
"final_severity": "最终严重程度",
|
"final_severity": "最终严重程度",
|
||||||
"final_confidence": 0.95,
|
"final_confidence": 0.95,
|
||||||
|
|
@ -380,15 +486,21 @@ RECON_SYSTEM_PROMPT = f"""你是 DeepAudit 的侦察 Agent,负责收集和分
|
||||||
1. 分析项目结构和技术栈
|
1. 分析项目结构和技术栈
|
||||||
2. 识别关键入口点
|
2. 识别关键入口点
|
||||||
3. 发现配置文件和敏感区域
|
3. 发现配置文件和敏感区域
|
||||||
4. 提供初步风险评估
|
4. **推荐需要使用的外部安全工具**
|
||||||
|
5. 提供初步风险评估
|
||||||
|
|
||||||
## 侦察目标
|
## 侦察目标
|
||||||
|
|
||||||
### 1. 技术栈识别
|
### 1. 技术栈识别(用于选择外部工具)
|
||||||
- 编程语言和版本
|
- 编程语言和版本
|
||||||
- Web框架(Django, Flask, FastAPI, Express等)
|
- Web框架(Django, Flask, FastAPI, Express等)
|
||||||
- 数据库类型
|
- 数据库类型
|
||||||
- 前端框架
|
- 前端框架
|
||||||
|
- **根据技术栈推荐外部工具:**
|
||||||
|
- Python项目 → bandit_scan, safety_scan
|
||||||
|
- Node.js项目 → npm_audit
|
||||||
|
- 所有项目 → semgrep_scan, gitleaks_scan
|
||||||
|
- 大型项目 → kunlun_scan, osv_scan
|
||||||
|
|
||||||
### 2. 入口点发现
|
### 2. 入口点发现
|
||||||
- HTTP路由和API端点
|
- HTTP路由和API端点
|
||||||
|
|
@ -433,6 +545,11 @@ Final Answer: {{
|
||||||
"frameworks": [...],
|
"frameworks": [...],
|
||||||
"databases": [...]
|
"databases": [...]
|
||||||
}},
|
}},
|
||||||
|
"recommended_tools": {{
|
||||||
|
"must_use": ["semgrep_scan", "gitleaks_scan", ...],
|
||||||
|
"recommended": ["kunlun_scan", ...],
|
||||||
|
"reason": "基于项目技术栈的推荐理由"
|
||||||
|
}},
|
||||||
"entry_points": [
|
"entry_points": [
|
||||||
{{"type": "...", "file": "...", "line": ..., "method": "..."}}
|
{{"type": "...", "file": "...", "line": ..., "method": "..."}}
|
||||||
],
|
],
|
||||||
|
|
@ -448,6 +565,12 @@ Final Answer: {{
|
||||||
|
|
||||||
## ⚠️ 重要输出要求
|
## ⚠️ 重要输出要求
|
||||||
|
|
||||||
|
### recommended_tools 格式要求(新增!)
|
||||||
|
**必须**根据项目技术栈推荐外部工具:
|
||||||
|
- `must_use`: 必须使用的工具列表
|
||||||
|
- `recommended`: 推荐使用的工具列表
|
||||||
|
- `reason`: 推荐理由
|
||||||
|
|
||||||
### high_risk_areas 格式要求
|
### high_risk_areas 格式要求
|
||||||
每个高风险区域**必须**包含具体的文件路径,格式为:
|
每个高风险区域**必须**包含具体的文件路径,格式为:
|
||||||
- `"app.py:36 - SECRET_KEY 硬编码"`
|
- `"app.py:36 - SECRET_KEY 硬编码"`
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,9 @@ from .agent_tools import (
|
||||||
# 🔥 新增:智能扫描工具
|
# 🔥 新增:智能扫描工具
|
||||||
from .smart_scan_tool import SmartScanTool, QuickAuditTool
|
from .smart_scan_tool import SmartScanTool, QuickAuditTool
|
||||||
|
|
||||||
|
# 🔥 新增:Kunlun-M 静态代码分析工具 (MIT License)
|
||||||
|
from .kunlun_tool import KunlunMTool, KunlunRuleListTool, KunlunPluginTool
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# 基础
|
# 基础
|
||||||
"AgentTool",
|
"AgentTool",
|
||||||
|
|
@ -156,4 +159,9 @@ __all__ = [
|
||||||
# 🔥 智能扫描工具
|
# 🔥 智能扫描工具
|
||||||
"SmartScanTool",
|
"SmartScanTool",
|
||||||
"QuickAuditTool",
|
"QuickAuditTool",
|
||||||
|
|
||||||
|
# 🔥 Kunlun-M 工具 (MIT License - https://github.com/LoRexxar/Kunlun-M)
|
||||||
|
"KunlunMTool",
|
||||||
|
"KunlunRuleListTool",
|
||||||
|
"KunlunPluginTool",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ class SemgrepInput(BaseModel):
|
||||||
"""Semgrep 扫描输入"""
|
"""Semgrep 扫描输入"""
|
||||||
target_path: str = Field(description="要扫描的目录或文件路径(相对于项目根目录)")
|
target_path: str = Field(description="要扫描的目录或文件路径(相对于项目根目录)")
|
||||||
rules: Optional[str] = Field(
|
rules: Optional[str] = Field(
|
||||||
default="auto",
|
default="p/security-audit",
|
||||||
description="规则集: auto, p/security-audit, p/owasp-top-ten, p/r2c-security-audit, 或自定义规则文件路径"
|
description="规则集: p/security-audit, p/owasp-top-ten, p/r2c-security-audit, 或自定义规则文件路径"
|
||||||
)
|
)
|
||||||
severity: Optional[str] = Field(
|
severity: Optional[str] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
|
|
@ -51,7 +51,6 @@ class SemgrepTool(AgentTool):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
AVAILABLE_RULESETS = [
|
AVAILABLE_RULESETS = [
|
||||||
"auto",
|
|
||||||
"p/security-audit",
|
"p/security-audit",
|
||||||
"p/owasp-top-ten",
|
"p/owasp-top-ten",
|
||||||
"p/r2c-security-audit",
|
"p/r2c-security-audit",
|
||||||
|
|
@ -68,10 +67,12 @@ class SemgrepTool(AgentTool):
|
||||||
"p/command-injection",
|
"p/command-injection",
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, project_root: str):
|
def __init__(self, project_root: str, sandbox_manager: Optional["SandboxManager"] = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.project_root = project_root
|
# 🔥 将相对路径转换为绝对路径,Docker 需要绝对路径
|
||||||
self.sandbox_manager = SandboxManager()
|
self.project_root = os.path.abspath(project_root)
|
||||||
|
# 🔥 使用共享的 SandboxManager 实例,避免重复初始化
|
||||||
|
self.sandbox_manager = sandbox_manager or SandboxManager()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
|
@ -103,7 +104,7 @@ Semgrep 是业界领先的静态分析工具,支持 30+ 种编程语言。
|
||||||
async def _execute(
|
async def _execute(
|
||||||
self,
|
self,
|
||||||
target_path: str = ".",
|
target_path: str = ".",
|
||||||
rules: str = "auto",
|
rules: str = "p/security-audit",
|
||||||
severity: Optional[str] = None,
|
severity: Optional[str] = None,
|
||||||
max_results: int = 50,
|
max_results: int = 50,
|
||||||
**kwargs
|
**kwargs
|
||||||
|
|
@ -112,7 +113,7 @@ Semgrep 是业界领先的静态分析工具,支持 30+ 种编程语言。
|
||||||
# 确保 Docker 可用
|
# 确保 Docker 可用
|
||||||
await self.sandbox_manager.initialize()
|
await self.sandbox_manager.initialize()
|
||||||
if not self.sandbox_manager.is_available:
|
if not self.sandbox_manager.is_available:
|
||||||
return ToolResult(success=False, error="Docker 沙箱不可用,无法执行 Semgrep")
|
return ToolResult(success=False, error=f"Semgrep unavailable: {self.sandbox_manager.get_diagnosis()}")
|
||||||
|
|
||||||
# 构建命令 (相对于 /workspace)
|
# 构建命令 (相对于 /workspace)
|
||||||
# 注意: target_path 是相对于 project_root 的
|
# 注意: target_path 是相对于 project_root 的
|
||||||
|
|
@ -121,7 +122,8 @@ Semgrep 是业界领先的静态分析工具,支持 30+ 种编程语言。
|
||||||
cmd = ["semgrep", "--json", "--quiet"]
|
cmd = ["semgrep", "--json", "--quiet"]
|
||||||
|
|
||||||
if rules == "auto":
|
if rules == "auto":
|
||||||
cmd.extend(["--config", "auto"])
|
# 🔥 Fallback if user explicitly requests 'auto', but prefer security-audit
|
||||||
|
cmd.extend(["--config", "p/security-audit"])
|
||||||
elif rules.startswith("p/"):
|
elif rules.startswith("p/"):
|
||||||
cmd.extend(["--config", rules])
|
cmd.extend(["--config", rules])
|
||||||
else:
|
else:
|
||||||
|
|
@ -139,7 +141,8 @@ Semgrep 是业界领先的静态分析工具,支持 30+ 种编程语言。
|
||||||
result = await self.sandbox_manager.execute_tool_command(
|
result = await self.sandbox_manager.execute_tool_command(
|
||||||
command=cmd_str,
|
command=cmd_str,
|
||||||
host_workdir=self.project_root,
|
host_workdir=self.project_root,
|
||||||
timeout=300
|
timeout=300,
|
||||||
|
network_mode="bridge" # 🔥 Semgrep 需要网络来下载规则
|
||||||
)
|
)
|
||||||
|
|
||||||
if not result["success"] and result["exit_code"] != 1: # 1 means findings were found
|
if not result["success"] and result["exit_code"] != 1: # 1 means findings were found
|
||||||
|
|
@ -230,10 +233,12 @@ class BanditTool(AgentTool):
|
||||||
- 不安全的反序列化
|
- 不安全的反序列化
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project_root: str):
|
def __init__(self, project_root: str, sandbox_manager: Optional["SandboxManager"] = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.project_root = project_root
|
# 🔥 将相对路径转换为绝对路径,Docker 需要绝对路径
|
||||||
self.sandbox_manager = SandboxManager()
|
self.project_root = os.path.abspath(project_root)
|
||||||
|
# 🔥 使用共享的 SandboxManager 实例,避免重复初始化
|
||||||
|
self.sandbox_manager = sandbox_manager or SandboxManager()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
|
@ -271,7 +276,7 @@ Bandit 是 Python 专用的安全分析工具,由 OpenStack 安全团队开发
|
||||||
# 确保 Docker 可用
|
# 确保 Docker 可用
|
||||||
await self.sandbox_manager.initialize()
|
await self.sandbox_manager.initialize()
|
||||||
if not self.sandbox_manager.is_available:
|
if not self.sandbox_manager.is_available:
|
||||||
return ToolResult(success=False, error="Docker 沙箱不可用")
|
return ToolResult(success=False, error=f"Bandit unavailable: {self.sandbox_manager.get_diagnosis()}")
|
||||||
|
|
||||||
safe_target_path = target_path if not target_path.startswith("/") else target_path.lstrip("/")
|
safe_target_path = target_path if not target_path.startswith("/") else target_path.lstrip("/")
|
||||||
|
|
||||||
|
|
@ -360,10 +365,12 @@ class GitleaksTool(AgentTool):
|
||||||
- JWT secrets
|
- JWT secrets
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project_root: str):
|
def __init__(self, project_root: str, sandbox_manager: Optional["SandboxManager"] = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.project_root = project_root
|
# 🔥 将相对路径转换为绝对路径,Docker 需要绝对路径
|
||||||
self.sandbox_manager = SandboxManager()
|
self.project_root = os.path.abspath(project_root)
|
||||||
|
# 🔥 使用共享的 SandboxManager 实例,避免重复初始化
|
||||||
|
self.sandbox_manager = sandbox_manager or SandboxManager()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
|
@ -402,30 +409,36 @@ Gitleaks 是专业的密钥检测工具,支持 150+ 种密钥类型。
|
||||||
# 确保 Docker 可用
|
# 确保 Docker 可用
|
||||||
await self.sandbox_manager.initialize()
|
await self.sandbox_manager.initialize()
|
||||||
if not self.sandbox_manager.is_available:
|
if not self.sandbox_manager.is_available:
|
||||||
return ToolResult(success=False, error="Docker 沙箱不可用")
|
return ToolResult(success=False, error=f"Gitleaks unavailable: {self.sandbox_manager.get_diagnosis()}")
|
||||||
|
|
||||||
safe_target_path = target_path if not target_path.startswith("/") else target_path.lstrip("/")
|
safe_target_path = target_path if not target_path.startswith("/") else target_path.lstrip("/")
|
||||||
|
|
||||||
# 构建命令 using . as source because we are mounted to /workspace
|
# 🔥 修复:新版 gitleaks 需要使用 --report-path 输出到文件
|
||||||
# But if user specified a subdirectory, we append it.
|
# 使用 /tmp 目录(tmpfs 可写)
|
||||||
# Actually gitleaks detects pwd by default if source is .
|
cmd = [
|
||||||
|
"gitleaks", "detect",
|
||||||
cmd = ["gitleaks", "detect", "--source", safe_target_path, "-f", "json"]
|
"--source", safe_target_path,
|
||||||
|
"--report-format", "json",
|
||||||
|
"--report-path", "/tmp/gitleaks-report.json",
|
||||||
|
"--exit-code", "0" # 🔥 不要因为发现密钥而返回非零退出码
|
||||||
|
]
|
||||||
if no_git:
|
if no_git:
|
||||||
cmd.append("--no-git")
|
cmd.append("--no-git")
|
||||||
|
|
||||||
cmd_str = " ".join(cmd)
|
# 执行 gitleaks 并读取报告文件
|
||||||
|
cmd_str = " ".join(cmd) + " && cat /tmp/gitleaks-report.json"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = await self.sandbox_manager.execute_tool_command(
|
result = await self.sandbox_manager.execute_tool_command(
|
||||||
command=cmd_str,
|
command=cmd_str,
|
||||||
host_workdir=self.project_root,
|
host_workdir=self.project_root,
|
||||||
timeout=120
|
timeout=180 # 🔥 增加超时时间
|
||||||
)
|
)
|
||||||
|
|
||||||
# Gitleaks returns 1 if secrets found
|
if result['exit_code'] != 0:
|
||||||
if result['exit_code'] not in [0, 1]:
|
# 🔥 修复:错误信息可能在 error 或 stderr 中
|
||||||
return ToolResult(success=False, error=f"Gitleaks 执行失败: {result['stderr'][:300]}")
|
error_msg = result.get('error') or result.get('stderr', '')[:300] or '未知错误'
|
||||||
|
return ToolResult(success=False, error=f"Gitleaks 执行失败: {error_msg}")
|
||||||
|
|
||||||
stdout = result['stdout']
|
stdout = result['stdout']
|
||||||
|
|
||||||
|
|
@ -502,10 +515,12 @@ class NpmAuditTool(AgentTool):
|
||||||
扫描 Node.js 项目的依赖漏洞,基于 npm 官方漏洞数据库。
|
扫描 Node.js 项目的依赖漏洞,基于 npm 官方漏洞数据库。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project_root: str):
|
def __init__(self, project_root: str, sandbox_manager: Optional["SandboxManager"] = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.project_root = project_root
|
# 🔥 将相对路径转换为绝对路径,Docker 需要绝对路径
|
||||||
self.sandbox_manager = SandboxManager()
|
self.project_root = os.path.abspath(project_root)
|
||||||
|
# 🔥 使用共享的 SandboxManager 实例,避免重复初始化
|
||||||
|
self.sandbox_manager = sandbox_manager or SandboxManager()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
|
@ -536,7 +551,7 @@ class NpmAuditTool(AgentTool):
|
||||||
# 确保 Docker 可用
|
# 确保 Docker 可用
|
||||||
await self.sandbox_manager.initialize()
|
await self.sandbox_manager.initialize()
|
||||||
if not self.sandbox_manager.is_available:
|
if not self.sandbox_manager.is_available:
|
||||||
return ToolResult(success=False, error="Docker 沙箱不可用")
|
return ToolResult(success=False, error=f"npm audit unavailable: {self.sandbox_manager.get_diagnosis()}")
|
||||||
|
|
||||||
# 这里的 target_path 是相对于 project_root 的
|
# 这里的 target_path 是相对于 project_root 的
|
||||||
# 防止空路径
|
# 防止空路径
|
||||||
|
|
@ -645,10 +660,12 @@ class SafetyTool(AgentTool):
|
||||||
检查 Python 依赖中的已知安全漏洞。
|
检查 Python 依赖中的已知安全漏洞。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project_root: str):
|
def __init__(self, project_root: str, sandbox_manager: Optional["SandboxManager"] = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.project_root = project_root
|
# 🔥 将相对路径转换为绝对路径,Docker 需要绝对路径
|
||||||
self.sandbox_manager = SandboxManager()
|
self.project_root = os.path.abspath(project_root)
|
||||||
|
# 🔥 使用共享的 SandboxManager 实例,避免重复初始化
|
||||||
|
self.sandbox_manager = sandbox_manager or SandboxManager()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
|
@ -677,7 +694,7 @@ class SafetyTool(AgentTool):
|
||||||
# 确保 Docker 可用
|
# 确保 Docker 可用
|
||||||
await self.sandbox_manager.initialize()
|
await self.sandbox_manager.initialize()
|
||||||
if not self.sandbox_manager.is_available:
|
if not self.sandbox_manager.is_available:
|
||||||
return ToolResult(success=False, error="Docker 沙箱不可用")
|
return ToolResult(success=False, error=f"Safety unavailable: {self.sandbox_manager.get_diagnosis()}")
|
||||||
|
|
||||||
full_path = os.path.join(self.project_root, requirements_file)
|
full_path = os.path.join(self.project_root, requirements_file)
|
||||||
if not os.path.exists(full_path):
|
if not os.path.exists(full_path):
|
||||||
|
|
@ -768,10 +785,12 @@ class TruffleHogTool(AgentTool):
|
||||||
并可以验证密钥是否仍然有效。
|
并可以验证密钥是否仍然有效。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project_root: str):
|
def __init__(self, project_root: str, sandbox_manager: Optional["SandboxManager"] = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.project_root = project_root
|
# 🔥 将相对路径转换为绝对路径,Docker 需要绝对路径
|
||||||
self.sandbox_manager = SandboxManager()
|
self.project_root = os.path.abspath(project_root)
|
||||||
|
# 🔥 使用共享的 SandboxManager 实例,避免重复初始化
|
||||||
|
self.sandbox_manager = sandbox_manager or SandboxManager()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
|
@ -804,7 +823,7 @@ TruffleHog 可以扫描代码和 Git 历史,并验证密钥是否有效。
|
||||||
# 确保 Docker 可用
|
# 确保 Docker 可用
|
||||||
await self.sandbox_manager.initialize()
|
await self.sandbox_manager.initialize()
|
||||||
if not self.sandbox_manager.is_available:
|
if not self.sandbox_manager.is_available:
|
||||||
return ToolResult(success=False, error="Docker 沙箱不可用")
|
return ToolResult(success=False, error=f"TruffleHog unavailable: {self.sandbox_manager.get_diagnosis()}")
|
||||||
|
|
||||||
safe_target_path = target_path if not target_path.startswith("/") else target_path.lstrip("/")
|
safe_target_path = target_path if not target_path.startswith("/") else target_path.lstrip("/")
|
||||||
|
|
||||||
|
|
@ -879,10 +898,12 @@ class OSVScannerTool(AgentTool):
|
||||||
支持多种包管理器和锁文件。
|
支持多种包管理器和锁文件。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project_root: str):
|
def __init__(self, project_root: str, sandbox_manager: Optional["SandboxManager"] = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.project_root = project_root
|
# 🔥 将相对路径转换为绝对路径,Docker 需要绝对路径
|
||||||
self.sandbox_manager = SandboxManager()
|
self.project_root = os.path.abspath(project_root)
|
||||||
|
# 🔥 使用共享的 SandboxManager 实例,避免重复初始化
|
||||||
|
self.sandbox_manager = sandbox_manager or SandboxManager()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
|
@ -920,7 +941,7 @@ Google 开源的漏洞扫描工具,使用 OSV (Open Source Vulnerabilities)
|
||||||
# 确保 Docker 可用
|
# 确保 Docker 可用
|
||||||
await self.sandbox_manager.initialize()
|
await self.sandbox_manager.initialize()
|
||||||
if not self.sandbox_manager.is_available:
|
if not self.sandbox_manager.is_available:
|
||||||
return ToolResult(success=False, error="Docker 沙箱不可用")
|
return ToolResult(success=False, error=f"OSV-Scanner unavailable: {self.sandbox_manager.get_diagnosis()}")
|
||||||
|
|
||||||
safe_target_path = target_path if not target_path.startswith("/") else target_path.lstrip("/")
|
safe_target_path = target_path if not target_path.startswith("/") else target_path.lstrip("/")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,723 @@
|
||||||
|
"""
|
||||||
|
Kunlun-M 静态代码分析工具集成
|
||||||
|
|
||||||
|
Kunlun-M (昆仑镜) 是一款开源的静态代码安全审计工具,
|
||||||
|
支持 PHP、JavaScript 等语言的语义分析和漏洞检测。
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
Copyright (c) 2017 Feei. <feei@feei.cn> All rights reserved
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
原始项目: https://github.com/LoRexxar/Kunlun-M
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import subprocess
|
||||||
|
from typing import Optional, List, Dict, Any
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .base import AgentTool, ToolResult
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Kunlun-M 安装路径(相对于项目根目录)
|
||||||
|
KUNLUN_M_PATH = os.path.join(
|
||||||
|
os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))),
|
||||||
|
"Kunlun-M-master"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class KunlunScanInput(BaseModel):
|
||||||
|
"""Kunlun-M 扫描输入"""
|
||||||
|
target_path: str = Field(
|
||||||
|
description="要扫描的目录或文件路径(相对于项目根目录)"
|
||||||
|
)
|
||||||
|
language: Optional[str] = Field(
|
||||||
|
default=None,
|
||||||
|
description="指定扫描语言: php, javascript, solidity, chromeext。不指定则自动检测"
|
||||||
|
)
|
||||||
|
rules: Optional[str] = Field(
|
||||||
|
default=None,
|
||||||
|
description="指定规则ID,多个规则用逗号分隔,如: 1000,1001,1002"
|
||||||
|
)
|
||||||
|
tamper: Optional[str] = Field(
|
||||||
|
default=None,
|
||||||
|
description="指定 tamper 名称,用于自定义修复函数检测"
|
||||||
|
)
|
||||||
|
include_unconfirmed: bool = Field(
|
||||||
|
default=False,
|
||||||
|
description="是否包含未确认的漏洞(疑似漏洞)"
|
||||||
|
)
|
||||||
|
max_results: int = Field(
|
||||||
|
default=50,
|
||||||
|
description="最大返回结果数"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class KunlunRuleListInput(BaseModel):
|
||||||
|
"""Kunlun-M 规则列表输入"""
|
||||||
|
language: Optional[str] = Field(
|
||||||
|
default=None,
|
||||||
|
description="按语言过滤规则: php, javascript, solidity, chromeext"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class KunlunMTool(AgentTool):
|
||||||
|
"""
|
||||||
|
Kunlun-M (昆仑镜) 静态代码安全审计工具
|
||||||
|
|
||||||
|
特点:
|
||||||
|
- 语义分析:深度AST分析,减少误报
|
||||||
|
- 多语言支持:PHP、JavaScript 语义分析,Solidity、Chrome Extension 基础扫描
|
||||||
|
- 函数回溯:支持污点追踪和数据流分析
|
||||||
|
- 丰富的规则库:覆盖 OWASP Top 10 等常见漏洞
|
||||||
|
|
||||||
|
支持的漏洞类型:
|
||||||
|
- SQL 注入
|
||||||
|
- XSS 跨站脚本
|
||||||
|
- 命令注入
|
||||||
|
- 代码执行
|
||||||
|
- 文件包含
|
||||||
|
- 文件上传
|
||||||
|
- 反序列化
|
||||||
|
- SSRF
|
||||||
|
- XXE
|
||||||
|
- 等等...
|
||||||
|
|
||||||
|
使用场景:
|
||||||
|
- PHP 代码深度安全审计
|
||||||
|
- JavaScript 代码安全扫描
|
||||||
|
- 智能合约安全检查
|
||||||
|
- Chrome 扩展安全审计
|
||||||
|
|
||||||
|
原始项目: https://github.com/LoRexxar/Kunlun-M
|
||||||
|
License: MIT
|
||||||
|
"""
|
||||||
|
|
||||||
|
SUPPORTED_LANGUAGES = ["php", "javascript", "solidity", "chromeext"]
|
||||||
|
|
||||||
|
def __init__(self, project_root: str):
|
||||||
|
super().__init__()
|
||||||
|
self.project_root = project_root
|
||||||
|
self.kunlun_path = KUNLUN_M_PATH
|
||||||
|
self._initialized = False
|
||||||
|
self._db_initialized = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return "kunlun_scan"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self) -> str:
|
||||||
|
return """使用 Kunlun-M (昆仑镜) 进行静态代码安全审计。
|
||||||
|
Kunlun-M 是一款专注于代码安全审计的工具,特别擅长 PHP 和 JavaScript 的语义分析。
|
||||||
|
|
||||||
|
支持的语言:
|
||||||
|
- php: PHP 语义分析(最完善)
|
||||||
|
- javascript: JavaScript 语义分析
|
||||||
|
- solidity: 智能合约基础扫描
|
||||||
|
- chromeext: Chrome 扩展安全检查
|
||||||
|
|
||||||
|
主要功能:
|
||||||
|
- 深度 AST 语义分析
|
||||||
|
- 污点追踪和函数回溯
|
||||||
|
- 自定义规则和 tamper 支持
|
||||||
|
- 支持识别常见安全漏洞
|
||||||
|
|
||||||
|
使用场景:
|
||||||
|
- 对 PHP/JS 代码进行深度安全审计
|
||||||
|
- 检测 SQL 注入、XSS、命令注入等漏洞
|
||||||
|
- 分析代码中的危险函数调用
|
||||||
|
- 追踪用户输入的传播路径"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def args_schema(self):
|
||||||
|
return KunlunScanInput
|
||||||
|
|
||||||
|
async def _ensure_initialized(self) -> bool:
|
||||||
|
"""确保 Kunlun-M 已初始化"""
|
||||||
|
if self._initialized:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 检查 Kunlun-M 是否存在
|
||||||
|
if not os.path.exists(self.kunlun_path):
|
||||||
|
logger.error(f"Kunlun-M not found at {self.kunlun_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
kunlun_py = os.path.join(self.kunlun_path, "kunlun.py")
|
||||||
|
if not os.path.exists(kunlun_py):
|
||||||
|
logger.error(f"kunlun.py not found at {kunlun_py}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 检查数据库是否已初始化
|
||||||
|
db_path = os.path.join(self.kunlun_path, "db.sqlite3")
|
||||||
|
if not os.path.exists(db_path):
|
||||||
|
logger.info("Kunlun-M database not found, initializing...")
|
||||||
|
try:
|
||||||
|
await self._initialize_database()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to initialize Kunlun-M database: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._initialized = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def _initialize_database(self):
|
||||||
|
"""初始化 Kunlun-M 数据库"""
|
||||||
|
# 复制 settings.py
|
||||||
|
settings_bak = os.path.join(self.kunlun_path, "Kunlun_M", "settings.py.bak")
|
||||||
|
settings_py = os.path.join(self.kunlun_path, "Kunlun_M", "settings.py")
|
||||||
|
|
||||||
|
if os.path.exists(settings_bak) and not os.path.exists(settings_py):
|
||||||
|
import shutil
|
||||||
|
shutil.copy(settings_bak, settings_py)
|
||||||
|
|
||||||
|
# 运行初始化命令
|
||||||
|
init_cmd = [
|
||||||
|
sys.executable,
|
||||||
|
os.path.join(self.kunlun_path, "kunlun.py"),
|
||||||
|
"init", "initialize"
|
||||||
|
]
|
||||||
|
|
||||||
|
process = await asyncio.create_subprocess_exec(
|
||||||
|
*init_cmd,
|
||||||
|
cwd=self.kunlun_path,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE,
|
||||||
|
env={**os.environ, "DJANGO_SETTINGS_MODULE": "Kunlun_M.settings"}
|
||||||
|
)
|
||||||
|
|
||||||
|
stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=120)
|
||||||
|
|
||||||
|
if process.returncode != 0:
|
||||||
|
raise Exception(f"Database init failed: {stderr.decode()}")
|
||||||
|
|
||||||
|
# 加载规则
|
||||||
|
load_cmd = [
|
||||||
|
sys.executable,
|
||||||
|
os.path.join(self.kunlun_path, "kunlun.py"),
|
||||||
|
"config", "load"
|
||||||
|
]
|
||||||
|
|
||||||
|
process = await asyncio.create_subprocess_exec(
|
||||||
|
*load_cmd,
|
||||||
|
cwd=self.kunlun_path,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE,
|
||||||
|
env={**os.environ, "DJANGO_SETTINGS_MODULE": "Kunlun_M.settings"}
|
||||||
|
)
|
||||||
|
|
||||||
|
stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=120)
|
||||||
|
|
||||||
|
self._db_initialized = True
|
||||||
|
logger.info("Kunlun-M database initialized successfully")
|
||||||
|
|
||||||
|
async def _execute(
|
||||||
|
self,
|
||||||
|
target_path: str = ".",
|
||||||
|
language: Optional[str] = None,
|
||||||
|
rules: Optional[str] = None,
|
||||||
|
tamper: Optional[str] = None,
|
||||||
|
include_unconfirmed: bool = False,
|
||||||
|
max_results: int = 50,
|
||||||
|
**kwargs
|
||||||
|
) -> ToolResult:
|
||||||
|
"""执行 Kunlun-M 扫描"""
|
||||||
|
|
||||||
|
# 确保初始化
|
||||||
|
if not await self._ensure_initialized():
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error="Kunlun-M 未正确安装或初始化失败。请确保 Kunlun-M-master 目录存在且依赖已安装。"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 构建完整目标路径
|
||||||
|
if target_path.startswith("/"):
|
||||||
|
full_target = target_path
|
||||||
|
else:
|
||||||
|
full_target = os.path.join(self.project_root, target_path)
|
||||||
|
|
||||||
|
if not os.path.exists(full_target):
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error=f"目标路径不存在: {target_path}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 构建扫描命令
|
||||||
|
cmd = [
|
||||||
|
sys.executable,
|
||||||
|
os.path.join(self.kunlun_path, "kunlun.py"),
|
||||||
|
"scan",
|
||||||
|
"-t", full_target,
|
||||||
|
"-o", "json" # JSON 输出格式
|
||||||
|
]
|
||||||
|
|
||||||
|
# 添加语言参数
|
||||||
|
if language:
|
||||||
|
if language.lower() not in self.SUPPORTED_LANGUAGES:
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error=f"不支持的语言: {language}。支持: {', '.join(self.SUPPORTED_LANGUAGES)}"
|
||||||
|
)
|
||||||
|
cmd.extend(["-l", language.lower()])
|
||||||
|
|
||||||
|
# 添加规则参数
|
||||||
|
if rules:
|
||||||
|
cmd.extend(["-r", rules])
|
||||||
|
|
||||||
|
# 添加 tamper 参数
|
||||||
|
if tamper:
|
||||||
|
cmd.extend(["-tp", tamper])
|
||||||
|
|
||||||
|
# 包含未确认漏洞
|
||||||
|
if include_unconfirmed:
|
||||||
|
cmd.append("-uc")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 创建临时输出文件
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
||||||
|
output_file = f.name
|
||||||
|
|
||||||
|
# 修改命令使用输出文件
|
||||||
|
cmd.extend(["-o", output_file])
|
||||||
|
|
||||||
|
logger.debug(f"Running Kunlun-M: {' '.join(cmd)}")
|
||||||
|
|
||||||
|
# 执行扫描
|
||||||
|
process = await asyncio.create_subprocess_exec(
|
||||||
|
*cmd,
|
||||||
|
cwd=self.kunlun_path,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE,
|
||||||
|
env={**os.environ, "DJANGO_SETTINGS_MODULE": "Kunlun_M.settings"}
|
||||||
|
)
|
||||||
|
|
||||||
|
stdout, stderr = await asyncio.wait_for(
|
||||||
|
process.communicate(),
|
||||||
|
timeout=600 # 10 分钟超时
|
||||||
|
)
|
||||||
|
|
||||||
|
stdout_text = stdout.decode('utf-8', errors='ignore')
|
||||||
|
stderr_text = stderr.decode('utf-8', errors='ignore')
|
||||||
|
|
||||||
|
# 解析结果
|
||||||
|
findings = await self._parse_results(stdout_text, stderr_text, output_file)
|
||||||
|
|
||||||
|
# 清理临时文件
|
||||||
|
try:
|
||||||
|
os.unlink(output_file)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not findings:
|
||||||
|
return ToolResult(
|
||||||
|
success=True,
|
||||||
|
data="🛡️ Kunlun-M 扫描完成,未发现安全问题",
|
||||||
|
metadata={
|
||||||
|
"findings_count": 0,
|
||||||
|
"target": target_path,
|
||||||
|
"language": language
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 格式化输出
|
||||||
|
output = self._format_findings(findings[:max_results], target_path)
|
||||||
|
|
||||||
|
return ToolResult(
|
||||||
|
success=True,
|
||||||
|
data=output,
|
||||||
|
metadata={
|
||||||
|
"findings_count": len(findings),
|
||||||
|
"target": target_path,
|
||||||
|
"language": language,
|
||||||
|
"findings": findings[:10] # 只在 metadata 中保存前10个
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error="Kunlun-M 扫描超时(10分钟)"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Kunlun-M scan error: {e}", exc_info=True)
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error=f"扫描执行失败: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _parse_results(
|
||||||
|
self,
|
||||||
|
stdout: str,
|
||||||
|
stderr: str,
|
||||||
|
output_file: str
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
"""解析 Kunlun-M 扫描结果"""
|
||||||
|
findings = []
|
||||||
|
|
||||||
|
# 尝试从输出文件读取 JSON
|
||||||
|
try:
|
||||||
|
if os.path.exists(output_file):
|
||||||
|
with open(output_file, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
if isinstance(data, list):
|
||||||
|
findings.extend(data)
|
||||||
|
elif isinstance(data, dict) and 'vulnerabilities' in data:
|
||||||
|
findings.extend(data['vulnerabilities'])
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Failed to parse output file: {e}")
|
||||||
|
|
||||||
|
# 如果没有 JSON 输出,尝试从 stdout 解析
|
||||||
|
if not findings and stdout:
|
||||||
|
# 尝试提取 JSON 部分
|
||||||
|
try:
|
||||||
|
json_start = stdout.find('[')
|
||||||
|
json_end = stdout.rfind(']') + 1
|
||||||
|
if json_start >= 0 and json_end > json_start:
|
||||||
|
json_str = stdout[json_start:json_end]
|
||||||
|
findings = json.loads(json_str)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 尝试解析表格格式输出
|
||||||
|
if not findings:
|
||||||
|
findings = self._parse_table_output(stdout)
|
||||||
|
|
||||||
|
return findings
|
||||||
|
|
||||||
|
def _parse_table_output(self, output: str) -> List[Dict[str, Any]]:
|
||||||
|
"""解析 Kunlun-M 表格格式输出"""
|
||||||
|
findings = []
|
||||||
|
lines = output.split('\n')
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
# 匹配漏洞行格式: | index | CVI-xxxx | rule_name | language | file:line | ...
|
||||||
|
if '|' in line and 'CVI' in line:
|
||||||
|
parts = [p.strip() for p in line.split('|') if p.strip()]
|
||||||
|
if len(parts) >= 6:
|
||||||
|
try:
|
||||||
|
finding = {
|
||||||
|
"id": parts[1], # CVI-xxxx
|
||||||
|
"rule_name": parts[2],
|
||||||
|
"language": parts[3],
|
||||||
|
"location": parts[4],
|
||||||
|
"author": parts[5] if len(parts) > 5 else "",
|
||||||
|
"code": parts[6] if len(parts) > 6 else "",
|
||||||
|
"analysis": parts[7] if len(parts) > 7 else "",
|
||||||
|
}
|
||||||
|
findings.append(finding)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return findings
|
||||||
|
|
||||||
|
def _format_findings(self, findings: List[Dict[str, Any]], target: str) -> str:
|
||||||
|
"""格式化漏洞发现"""
|
||||||
|
output_parts = [
|
||||||
|
f"🔍 Kunlun-M 扫描结果",
|
||||||
|
f"目标: {target}",
|
||||||
|
f"发现 {len(findings)} 个潜在安全问题:\n"
|
||||||
|
]
|
||||||
|
|
||||||
|
severity_icons = {
|
||||||
|
"CRITICAL": "🔴",
|
||||||
|
"HIGH": "🟠",
|
||||||
|
"MEDIUM": "🟡",
|
||||||
|
"LOW": "🟢",
|
||||||
|
"INFO": "⚪"
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, finding in enumerate(findings, 1):
|
||||||
|
# 获取严重程度
|
||||||
|
severity = finding.get("severity", "MEDIUM")
|
||||||
|
if isinstance(severity, int):
|
||||||
|
if severity >= 9:
|
||||||
|
severity = "CRITICAL"
|
||||||
|
elif severity >= 6:
|
||||||
|
severity = "HIGH"
|
||||||
|
elif severity >= 3:
|
||||||
|
severity = "MEDIUM"
|
||||||
|
else:
|
||||||
|
severity = "LOW"
|
||||||
|
|
||||||
|
icon = severity_icons.get(severity.upper(), "⚪")
|
||||||
|
|
||||||
|
output_parts.append(f"\n{icon} [{i}] {finding.get('rule_name', 'Unknown')}")
|
||||||
|
output_parts.append(f" ID: {finding.get('id', 'N/A')}")
|
||||||
|
output_parts.append(f" 语言: {finding.get('language', 'N/A')}")
|
||||||
|
|
||||||
|
location = finding.get('location') or finding.get('file_path', '')
|
||||||
|
line_number = finding.get('line_number', '')
|
||||||
|
if location:
|
||||||
|
if line_number:
|
||||||
|
output_parts.append(f" 位置: {location}:{line_number}")
|
||||||
|
else:
|
||||||
|
output_parts.append(f" 位置: {location}")
|
||||||
|
|
||||||
|
code = finding.get('code') or finding.get('code_content', '')
|
||||||
|
if code:
|
||||||
|
code_preview = code[:100].strip().replace('\n', ' ')
|
||||||
|
output_parts.append(f" 代码: {code_preview}")
|
||||||
|
|
||||||
|
analysis = finding.get('analysis', '')
|
||||||
|
if analysis:
|
||||||
|
output_parts.append(f" 分析: {analysis}")
|
||||||
|
|
||||||
|
return "\n".join(output_parts)
|
||||||
|
|
||||||
|
|
||||||
|
class KunlunRuleListTool(AgentTool):
|
||||||
|
"""
|
||||||
|
查看 Kunlun-M 可用的扫描规则
|
||||||
|
|
||||||
|
可以按语言过滤规则,了解支持检测的漏洞类型。
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, project_root: str):
|
||||||
|
super().__init__()
|
||||||
|
self.project_root = project_root
|
||||||
|
self.kunlun_path = KUNLUN_M_PATH
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return "kunlun_list_rules"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self) -> str:
|
||||||
|
return """查看 Kunlun-M 可用的扫描规则。
|
||||||
|
|
||||||
|
可以按语言过滤:
|
||||||
|
- php: PHP 规则
|
||||||
|
- javascript: JavaScript 规则
|
||||||
|
- solidity: 智能合约规则
|
||||||
|
- chromeext: Chrome 扩展规则
|
||||||
|
|
||||||
|
返回规则ID、名称、描述等信息,帮助选择合适的规则进行扫描。"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def args_schema(self):
|
||||||
|
return KunlunRuleListInput
|
||||||
|
|
||||||
|
async def _execute(
|
||||||
|
self,
|
||||||
|
language: Optional[str] = None,
|
||||||
|
**kwargs
|
||||||
|
) -> ToolResult:
|
||||||
|
"""列出可用规则"""
|
||||||
|
|
||||||
|
if not os.path.exists(self.kunlun_path):
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error="Kunlun-M 未安装"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 构建命令
|
||||||
|
cmd = [
|
||||||
|
sys.executable,
|
||||||
|
os.path.join(self.kunlun_path, "kunlun.py"),
|
||||||
|
"show", "rule"
|
||||||
|
]
|
||||||
|
|
||||||
|
if language:
|
||||||
|
cmd.extend(["-k", language.lower()])
|
||||||
|
|
||||||
|
try:
|
||||||
|
process = await asyncio.create_subprocess_exec(
|
||||||
|
*cmd,
|
||||||
|
cwd=self.kunlun_path,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE,
|
||||||
|
env={**os.environ, "DJANGO_SETTINGS_MODULE": "Kunlun_M.settings"}
|
||||||
|
)
|
||||||
|
|
||||||
|
stdout, stderr = await asyncio.wait_for(
|
||||||
|
process.communicate(),
|
||||||
|
timeout=60
|
||||||
|
)
|
||||||
|
|
||||||
|
output = stdout.decode('utf-8', errors='ignore')
|
||||||
|
|
||||||
|
if not output.strip():
|
||||||
|
return ToolResult(
|
||||||
|
success=True,
|
||||||
|
data="未找到匹配的规则" if language else "规则列表为空,请先运行初始化",
|
||||||
|
metadata={"language": language}
|
||||||
|
)
|
||||||
|
|
||||||
|
return ToolResult(
|
||||||
|
success=True,
|
||||||
|
data=f"📋 Kunlun-M 规则列表{f' ({language})' if language else ''}:\n\n{output}",
|
||||||
|
metadata={"language": language}
|
||||||
|
)
|
||||||
|
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error="获取规则列表超时"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error=f"获取规则列表失败: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class KunlunPluginInput(BaseModel):
|
||||||
|
"""Kunlun-M 插件输入"""
|
||||||
|
plugin_name: str = Field(
|
||||||
|
description="插件名称: php_unserialize_chain_tools (PHP反序列化链分析), entrance_finder (入口点发现)"
|
||||||
|
)
|
||||||
|
target_path: str = Field(
|
||||||
|
description="要分析的目标路径(相对于项目根目录)"
|
||||||
|
)
|
||||||
|
depth: int = Field(
|
||||||
|
default=3,
|
||||||
|
description="分析深度(仅对 entrance_finder 有效)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class KunlunPluginTool(AgentTool):
|
||||||
|
"""
|
||||||
|
Kunlun-M 插件工具
|
||||||
|
|
||||||
|
提供额外的分析功能:
|
||||||
|
- php_unserialize_chain_tools: 自动化寻找 PHP 反序列化链
|
||||||
|
- entrance_finder: 发现 PHP 代码中的入口点/路由
|
||||||
|
"""
|
||||||
|
|
||||||
|
AVAILABLE_PLUGINS = {
|
||||||
|
"php_unserialize_chain_tools": "PHP 反序列化链分析工具,用于发现潜在的反序列化攻击链",
|
||||||
|
"entrance_finder": "入口点发现工具,帮助找到 PHP 代码中的入口页面和路由",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, project_root: str):
|
||||||
|
super().__init__()
|
||||||
|
self.project_root = project_root
|
||||||
|
self.kunlun_path = KUNLUN_M_PATH
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return "kunlun_plugin"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self) -> str:
|
||||||
|
return """运行 Kunlun-M 插件进行专项分析。
|
||||||
|
|
||||||
|
可用插件:
|
||||||
|
- php_unserialize_chain_tools: 自动分析 PHP 反序列化链,寻找 POP 链
|
||||||
|
- entrance_finder: 发现 PHP 入口点和路由
|
||||||
|
|
||||||
|
使用场景:
|
||||||
|
- 分析 PHP 框架的反序列化漏洞利用链
|
||||||
|
- 快速定位大型 PHP 项目的入口文件"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def args_schema(self):
|
||||||
|
return KunlunPluginInput
|
||||||
|
|
||||||
|
async def _execute(
|
||||||
|
self,
|
||||||
|
plugin_name: str,
|
||||||
|
target_path: str = ".",
|
||||||
|
depth: int = 3,
|
||||||
|
**kwargs
|
||||||
|
) -> ToolResult:
|
||||||
|
"""执行插件"""
|
||||||
|
|
||||||
|
if plugin_name not in self.AVAILABLE_PLUGINS:
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error=f"未知插件: {plugin_name}。可用插件: {', '.join(self.AVAILABLE_PLUGINS.keys())}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not os.path.exists(self.kunlun_path):
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error="Kunlun-M 未安装"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 构建完整目标路径
|
||||||
|
if target_path.startswith("/"):
|
||||||
|
full_target = target_path
|
||||||
|
else:
|
||||||
|
full_target = os.path.join(self.project_root, target_path)
|
||||||
|
|
||||||
|
if not os.path.exists(full_target):
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error=f"目标路径不存在: {target_path}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 构建命令
|
||||||
|
cmd = [
|
||||||
|
sys.executable,
|
||||||
|
os.path.join(self.kunlun_path, "kunlun.py"),
|
||||||
|
"plugin", plugin_name,
|
||||||
|
"-t", full_target
|
||||||
|
]
|
||||||
|
|
||||||
|
if plugin_name == "entrance_finder":
|
||||||
|
cmd.extend(["-l", str(depth)])
|
||||||
|
|
||||||
|
try:
|
||||||
|
process = await asyncio.create_subprocess_exec(
|
||||||
|
*cmd,
|
||||||
|
cwd=self.kunlun_path,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE,
|
||||||
|
env={**os.environ, "DJANGO_SETTINGS_MODULE": "Kunlun_M.settings"}
|
||||||
|
)
|
||||||
|
|
||||||
|
stdout, stderr = await asyncio.wait_for(
|
||||||
|
process.communicate(),
|
||||||
|
timeout=300 # 5 分钟超时
|
||||||
|
)
|
||||||
|
|
||||||
|
output = stdout.decode('utf-8', errors='ignore')
|
||||||
|
|
||||||
|
if not output.strip():
|
||||||
|
return ToolResult(
|
||||||
|
success=True,
|
||||||
|
data=f"插件 {plugin_name} 执行完成,未发现结果",
|
||||||
|
metadata={"plugin": plugin_name, "target": target_path}
|
||||||
|
)
|
||||||
|
|
||||||
|
return ToolResult(
|
||||||
|
success=True,
|
||||||
|
data=f"🔌 Kunlun-M 插件 [{plugin_name}] 分析结果:\n\n{output}",
|
||||||
|
metadata={"plugin": plugin_name, "target": target_path}
|
||||||
|
)
|
||||||
|
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error=f"插件 {plugin_name} 执行超时"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return ToolResult(
|
||||||
|
success=False,
|
||||||
|
error=f"插件执行失败: {str(e)}"
|
||||||
|
)
|
||||||
|
|
@ -40,6 +40,7 @@ class SandboxManager:
|
||||||
self.config = config or SandboxConfig()
|
self.config = config or SandboxConfig()
|
||||||
self._docker_client = None
|
self._docker_client = None
|
||||||
self._initialized = False
|
self._initialized = False
|
||||||
|
self._init_error = None
|
||||||
|
|
||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
"""初始化 Docker 客户端"""
|
"""初始化 Docker 客户端"""
|
||||||
|
|
@ -49,26 +50,35 @@ class SandboxManager:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import docker
|
import docker
|
||||||
logger.info("🔄 Attempting to connect to Docker...")
|
logger.info(f"🔄 Attempting to connect to Docker... (lib: {docker.__file__})")
|
||||||
self._docker_client = docker.from_env()
|
self._docker_client = docker.from_env()
|
||||||
# 测试连接
|
# 测试连接
|
||||||
self._docker_client.ping()
|
self._docker_client.ping()
|
||||||
self._initialized = True
|
self._initialized = True
|
||||||
|
self._init_error = None
|
||||||
logger.info("✅ Docker sandbox manager initialized successfully")
|
logger.info("✅ Docker sandbox manager initialized successfully")
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
logger.error(f"❌ Docker library not installed: {e}")
|
logger.error(f"❌ Docker library not installed: {e}")
|
||||||
self._docker_client = None
|
self._docker_client = None
|
||||||
|
self._init_error = f"ImportError: {e}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"❌ Docker not available: {e}")
|
logger.warning(f"❌ Docker not available: {e}")
|
||||||
import traceback
|
import traceback
|
||||||
logger.warning(f"Docker connection traceback: {traceback.format_exc()}")
|
logger.warning(f"Docker connection traceback: {traceback.format_exc()}")
|
||||||
self._docker_client = None
|
self._docker_client = None
|
||||||
|
self._init_error = f"{type(e).__name__}: {str(e)}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_available(self) -> bool:
|
def is_available(self) -> bool:
|
||||||
"""检查 Docker 是否可用"""
|
"""检查 Docker 是否可用"""
|
||||||
return self._docker_client is not None
|
return self._docker_client is not None
|
||||||
|
|
||||||
|
def get_diagnosis(self) -> str:
|
||||||
|
"""获取诊断信息"""
|
||||||
|
if self.is_available:
|
||||||
|
return "Docker Service Available"
|
||||||
|
return f"Docker Service Unavailable. Error: {self._init_error or 'Not initialized'}"
|
||||||
|
|
||||||
async def execute_command(
|
async def execute_command(
|
||||||
self,
|
self,
|
||||||
command: str,
|
command: str,
|
||||||
|
|
@ -213,10 +223,18 @@ class SandboxManager:
|
||||||
timeout = timeout or self.config.timeout
|
timeout = timeout or self.config.timeout
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# 🔥 清除代理环境变量的方式:在命令前添加 unset
|
||||||
|
# 因为设置空字符串会导致工具尝试解析空 URI 而出错
|
||||||
|
unset_proxy_prefix = "unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy; "
|
||||||
|
wrapped_command = unset_proxy_prefix + command
|
||||||
|
|
||||||
|
# 用户传入的环境变量
|
||||||
|
container_env = env or {}
|
||||||
|
|
||||||
# 准备容器配置
|
# 准备容器配置
|
||||||
container_config = {
|
container_config = {
|
||||||
"image": self.config.image,
|
"image": self.config.image,
|
||||||
"command": ["sh", "-c", command],
|
"command": ["sh", "-c", wrapped_command],
|
||||||
"detach": True,
|
"detach": True,
|
||||||
"mem_limit": self.config.memory_limit,
|
"mem_limit": self.config.memory_limit,
|
||||||
"cpu_period": 100000,
|
"cpu_period": 100000,
|
||||||
|
|
@ -228,10 +246,11 @@ class SandboxManager:
|
||||||
host_workdir: {"bind": "/workspace", "mode": "ro"}, # 只读挂载项目代码
|
host_workdir: {"bind": "/workspace", "mode": "ro"}, # 只读挂载项目代码
|
||||||
},
|
},
|
||||||
"tmpfs": {
|
"tmpfs": {
|
||||||
"/home/sandbox": "rw,size=100m,mode=1777"
|
"/home/sandbox": "rw,size=100m,mode=1777",
|
||||||
|
"/tmp": "rw,size=100m,mode=1777" # 🔥 添加 /tmp 目录供工具写入临时文件
|
||||||
},
|
},
|
||||||
"working_dir": "/workspace",
|
"working_dir": "/workspace",
|
||||||
"environment": env or {},
|
"environment": container_env, # 🔥 用户传入的环境变量
|
||||||
"cap_drop": ["ALL"],
|
"cap_drop": ["ALL"],
|
||||||
"security_opt": ["no-new-privileges:true"],
|
"security_opt": ["no-new-privileges:true"],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
import sys
|
||||||
|
try:
|
||||||
|
import docker
|
||||||
|
print(f"Docker module: {docker.__file__}")
|
||||||
|
client = docker.from_env()
|
||||||
|
print("Client created")
|
||||||
|
client.ping()
|
||||||
|
print("Docker is available and ping successful")
|
||||||
|
print(f"Docker version: {client.version()}")
|
||||||
|
except ImportError:
|
||||||
|
print("Docker library not installed")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Docker unavailable: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Add backend to path
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
from app.services.agent.tools.sandbox_tool import SandboxManager
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
print("Checking SandboxManager...")
|
||||||
|
mgr = SandboxManager()
|
||||||
|
await mgr.initialize()
|
||||||
|
print(f"Is available: {mgr.is_available}")
|
||||||
|
print(f"Diagnosis: {mgr.get_diagnosis()}")
|
||||||
|
|
||||||
|
if mgr.is_available:
|
||||||
|
print("Docker client created successfully.")
|
||||||
|
try:
|
||||||
|
ver = mgr._docker_client.version()
|
||||||
|
print(f"Docker version: {ver}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting version: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
|
|
@ -1,28 +1,211 @@
|
||||||
[project]
|
[project]
|
||||||
name = "deepaudit-backend"
|
name = "deepaudit-backend"
|
||||||
version = "2.0.0-beta.7"
|
version = "3.0.0"
|
||||||
description = "DeepAudit Backend API"
|
description = "DeepAudit Backend API - AI-Powered Code Security Audit Platform"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.11,<3.13"
|
||||||
dependencies = [
|
readme = "README.md"
|
||||||
"fastapi>=0.100.0",
|
license = { text = "MIT" }
|
||||||
"uvicorn[standard]",
|
authors = [
|
||||||
"sqlalchemy>=2.0.0",
|
{ name = "DeepAudit Team" }
|
||||||
"asyncpg",
|
]
|
||||||
"alembic",
|
keywords = ["security", "audit", "code-review", "vulnerability", "ai", "llm"]
|
||||||
"pydantic>=2.0.0",
|
|
||||||
"pydantic-settings",
|
dependencies = [
|
||||||
"passlib[bcrypt]",
|
# ============ Web Framework ============
|
||||||
"python-jose[cryptography]",
|
"fastapi>=0.100.0",
|
||||||
"python-multipart",
|
"uvicorn[standard]>=0.23.0",
|
||||||
"httpx",
|
"sse-starlette>=1.8.2",
|
||||||
"email-validator",
|
|
||||||
"greenlet",
|
# ============ Database ============
|
||||||
"bcrypt<5.0.0",
|
"sqlalchemy>=2.0.0",
|
||||||
"litellm>=1.0.0",
|
"asyncpg>=0.29.0",
|
||||||
"reportlab>=4.0.0",
|
"alembic>=1.13.0",
|
||||||
"weasyprint>=66.0",
|
"greenlet>=3.0.0",
|
||||||
"jinja2>=3.1.6",
|
|
||||||
"json-repair>=0.30.0",
|
# ============ Data Validation ============
|
||||||
"bandit>=1.9.2",
|
"pydantic>=2.0.0",
|
||||||
"safety>=3.7.0",
|
"pydantic-settings>=2.0.0",
|
||||||
|
"email-validator>=2.1.0",
|
||||||
|
|
||||||
|
# ============ Authentication ============
|
||||||
|
"passlib[bcrypt]>=1.7.4",
|
||||||
|
"python-jose[cryptography]>=3.3.0",
|
||||||
|
"python-multipart>=0.0.6",
|
||||||
|
"bcrypt>=4.0.0,<5.0.0",
|
||||||
|
|
||||||
|
# ============ HTTP Client ============
|
||||||
|
"httpx>=0.25.0",
|
||||||
|
|
||||||
|
# ============ LLM Integration ============
|
||||||
|
"litellm>=1.0.0",
|
||||||
|
"tiktoken>=0.5.2",
|
||||||
|
|
||||||
|
# ============ Report Generation ============
|
||||||
|
"reportlab>=4.0.0",
|
||||||
|
"weasyprint>=60.0",
|
||||||
|
"jinja2>=3.1.6",
|
||||||
|
|
||||||
|
# ============ Utilities ============
|
||||||
|
"json-repair>=0.30.0",
|
||||||
|
"aiofiles>=23.2.1",
|
||||||
|
|
||||||
|
# ============ LangChain & LangGraph ============
|
||||||
|
"langchain>=0.1.0",
|
||||||
|
"langchain-community>=0.0.20",
|
||||||
|
"langchain-openai>=0.0.5",
|
||||||
|
"langgraph>=0.0.40",
|
||||||
|
|
||||||
|
# ============ Vector Database ============
|
||||||
|
"chromadb>=0.4.22",
|
||||||
|
|
||||||
|
# ============ Code Parsing ============
|
||||||
|
"tree-sitter>=0.21.0",
|
||||||
|
"tree-sitter-languages>=1.10.0",
|
||||||
|
"pygments>=2.17.0",
|
||||||
|
|
||||||
|
# ============ Docker Sandbox ============
|
||||||
|
"docker>=7.0.0",
|
||||||
|
|
||||||
|
# ============ Security Tools ============
|
||||||
|
"bandit>=1.7.0",
|
||||||
|
"safety>=2.3.0",
|
||||||
|
"pip-audit>=2.6.0",
|
||||||
|
|
||||||
|
# ============ Kunlun-M Dependencies (MIT License) ============
|
||||||
|
# https://github.com/LoRexxar/Kunlun-M
|
||||||
|
"pyjsparser>=2.7.1",
|
||||||
|
"phply>=1.2.6",
|
||||||
|
"esprima>=4.0.1",
|
||||||
|
"jsbeautifier>=1.14.0",
|
||||||
|
"colorlog>=6.0.0",
|
||||||
|
"portalocker>=2.0.0",
|
||||||
|
"prettytable>=3.0.0",
|
||||||
|
"rarfile>=4.0",
|
||||||
|
"beautifulsoup4>=4.12.0",
|
||||||
|
"django>=4.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
# MySQL support for Kunlun-M web mode
|
||||||
|
mysql = ["mysqlclient>=2.2.0"]
|
||||||
|
|
||||||
|
# Development tools
|
||||||
|
dev = [
|
||||||
|
"pytest>=7.4.0",
|
||||||
|
"pytest-asyncio>=0.21.0",
|
||||||
|
"pytest-cov>=4.1.0",
|
||||||
|
"black>=23.0.0",
|
||||||
|
"ruff>=0.1.0",
|
||||||
|
"mypy>=1.5.0",
|
||||||
|
"pre-commit>=3.5.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
docs = [
|
||||||
|
"mkdocs>=1.5.0",
|
||||||
|
"mkdocs-material>=9.4.0",
|
||||||
|
"mkdocstrings[python]>=0.23.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Homepage = "https://github.com/your-org/deepaudit"
|
||||||
|
Documentation = "https://docs.deepaudit.io"
|
||||||
|
Repository = "https://github.com/your-org/deepaudit"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["app"]
|
||||||
|
|
||||||
|
# ============ Tool Configurations ============
|
||||||
|
|
||||||
|
[tool.black]
|
||||||
|
line-length = 100
|
||||||
|
target-version = ["py311", "py312", "py313"]
|
||||||
|
exclude = '''
|
||||||
|
/(
|
||||||
|
\.git
|
||||||
|
| \.hg
|
||||||
|
| \.mypy_cache
|
||||||
|
| \.tox
|
||||||
|
| \.venv
|
||||||
|
| _build
|
||||||
|
| buck-out
|
||||||
|
| build
|
||||||
|
| dist
|
||||||
|
| migrations
|
||||||
|
)/
|
||||||
|
'''
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
line-length = 100
|
||||||
|
target-version = "py311"
|
||||||
|
exclude = [
|
||||||
|
".git",
|
||||||
|
".hg",
|
||||||
|
".mypy_cache",
|
||||||
|
".tox",
|
||||||
|
".venv",
|
||||||
|
"_build",
|
||||||
|
"buck-out",
|
||||||
|
"build",
|
||||||
|
"dist",
|
||||||
|
"migrations",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = [
|
||||||
|
"E", # pycodestyle errors
|
||||||
|
"W", # pycodestyle warnings
|
||||||
|
"F", # Pyflakes
|
||||||
|
"I", # isort
|
||||||
|
"B", # flake8-bugbear
|
||||||
|
"C4", # flake8-comprehensions
|
||||||
|
"UP", # pyupgrade
|
||||||
|
]
|
||||||
|
ignore = [
|
||||||
|
"E501", # line too long (handled by black)
|
||||||
|
"B008", # do not perform function calls in argument defaults
|
||||||
|
"C901", # too complex
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
python_version = "3.11"
|
||||||
|
warn_return_any = true
|
||||||
|
warn_unused_ignores = true
|
||||||
|
disallow_untyped_defs = true
|
||||||
|
exclude = [
|
||||||
|
"migrations/",
|
||||||
|
".venv/",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
testpaths = ["tests"]
|
||||||
|
asyncio_mode = "auto"
|
||||||
|
addopts = "-v --tb=short"
|
||||||
|
|
||||||
|
[tool.coverage.run]
|
||||||
|
source = ["app"]
|
||||||
|
omit = ["*/migrations/*", "*/tests/*"]
|
||||||
|
|
||||||
|
[tool.coverage.report]
|
||||||
|
exclude_lines = [
|
||||||
|
"pragma: no cover",
|
||||||
|
"def __repr__",
|
||||||
|
"raise NotImplementedError",
|
||||||
|
"if TYPE_CHECKING:",
|
||||||
|
]
|
||||||
|
|
||||||
|
# ============ UV Configuration ============
|
||||||
|
|
||||||
|
[tool.uv]
|
||||||
|
dev-dependencies = [
|
||||||
|
"pytest>=7.4.0",
|
||||||
|
"pytest-asyncio>=0.21.0",
|
||||||
|
"pytest-cov>=4.1.0",
|
||||||
|
"black>=23.0.0",
|
||||||
|
"ruff>=0.1.0",
|
||||||
|
"mypy>=1.5.0",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
# This file was autogenerated by uv via the following command:
|
# This file was autogenerated by uv via the following command:
|
||||||
# uv pip compile requirements.txt -o requirements-lock.txt
|
# uv pip compile requirements.txt -o requirements-lock.txt --python-version 3.12
|
||||||
|
aiofiles==25.1.0
|
||||||
|
# via -r requirements.txt
|
||||||
aiohappyeyeballs==2.6.1
|
aiohappyeyeballs==2.6.1
|
||||||
# via aiohttp
|
# via aiohttp
|
||||||
aiohttp==3.13.2
|
aiohttp==3.13.2
|
||||||
# via litellm
|
# via
|
||||||
|
# langchain-community
|
||||||
|
# litellm
|
||||||
aiosignal==1.4.0
|
aiosignal==1.4.0
|
||||||
# via aiohttp
|
# via aiohttp
|
||||||
alembic==1.17.2
|
alembic==1.17.2
|
||||||
|
|
@ -16,8 +20,11 @@ anyio==4.11.0
|
||||||
# via
|
# via
|
||||||
# httpx
|
# httpx
|
||||||
# openai
|
# openai
|
||||||
|
# sse-starlette
|
||||||
# starlette
|
# starlette
|
||||||
# watchfiles
|
# watchfiles
|
||||||
|
asgiref==3.11.0
|
||||||
|
# via django
|
||||||
asyncpg==0.31.0
|
asyncpg==0.31.0
|
||||||
# via -r requirements.txt
|
# via -r requirements.txt
|
||||||
attrs==25.4.0
|
attrs==25.4.0
|
||||||
|
|
@ -25,16 +32,34 @@ attrs==25.4.0
|
||||||
# aiohttp
|
# aiohttp
|
||||||
# jsonschema
|
# jsonschema
|
||||||
# referencing
|
# referencing
|
||||||
|
authlib==1.6.6
|
||||||
|
# via safety
|
||||||
|
backoff==2.2.1
|
||||||
|
# via posthog
|
||||||
|
bandit==1.9.2
|
||||||
|
# via -r requirements.txt
|
||||||
bcrypt==4.3.0
|
bcrypt==4.3.0
|
||||||
# via
|
# via
|
||||||
# -r requirements.txt
|
# -r requirements.txt
|
||||||
|
# chromadb
|
||||||
# passlib
|
# passlib
|
||||||
|
beautifulsoup4==4.14.3
|
||||||
|
# via -r requirements.txt
|
||||||
|
boolean-py==5.0
|
||||||
|
# via license-expression
|
||||||
brotli==1.2.0
|
brotli==1.2.0
|
||||||
# via fonttools
|
# via fonttools
|
||||||
|
build==1.3.0
|
||||||
|
# via chromadb
|
||||||
|
cachecontrol==0.14.4
|
||||||
|
# via pip-audit
|
||||||
|
cachetools==6.2.3
|
||||||
|
# via google-auth
|
||||||
certifi==2025.11.12
|
certifi==2025.11.12
|
||||||
# via
|
# via
|
||||||
# httpcore
|
# httpcore
|
||||||
# httpx
|
# httpx
|
||||||
|
# kubernetes
|
||||||
# requests
|
# requests
|
||||||
cffi==2.0.0
|
cffi==2.0.0
|
||||||
# via
|
# via
|
||||||
|
|
@ -44,29 +69,67 @@ charset-normalizer==3.4.4
|
||||||
# via
|
# via
|
||||||
# reportlab
|
# reportlab
|
||||||
# requests
|
# requests
|
||||||
|
chromadb==1.3.7
|
||||||
|
# via -r requirements.txt
|
||||||
click==8.3.1
|
click==8.3.1
|
||||||
# via
|
# via
|
||||||
# litellm
|
# litellm
|
||||||
|
# nltk
|
||||||
|
# safety
|
||||||
|
# typer
|
||||||
# typer-slim
|
# typer-slim
|
||||||
# uvicorn
|
# uvicorn
|
||||||
|
coloredlogs==15.0.1
|
||||||
|
# via onnxruntime
|
||||||
|
colorlog==6.10.1
|
||||||
|
# via -r requirements.txt
|
||||||
cryptography==46.0.3
|
cryptography==46.0.3
|
||||||
# via python-jose
|
# via
|
||||||
|
# authlib
|
||||||
|
# python-jose
|
||||||
cssselect2==0.8.0
|
cssselect2==0.8.0
|
||||||
# via weasyprint
|
# via weasyprint
|
||||||
|
cyclonedx-python-lib==11.6.0
|
||||||
|
# via pip-audit
|
||||||
|
dataclasses-json==0.6.7
|
||||||
|
# via langchain-community
|
||||||
|
defusedxml==0.7.1
|
||||||
|
# via py-serializable
|
||||||
distro==1.9.0
|
distro==1.9.0
|
||||||
# via openai
|
# via
|
||||||
|
# openai
|
||||||
|
# posthog
|
||||||
|
django==6.0
|
||||||
|
# via -r requirements.txt
|
||||||
dnspython==2.8.0
|
dnspython==2.8.0
|
||||||
# via email-validator
|
# via email-validator
|
||||||
|
docker==7.1.0
|
||||||
|
# via -r requirements.txt
|
||||||
|
dparse==0.6.4
|
||||||
|
# via
|
||||||
|
# safety
|
||||||
|
# safety-schemas
|
||||||
|
durationpy==0.10
|
||||||
|
# via kubernetes
|
||||||
ecdsa==0.19.1
|
ecdsa==0.19.1
|
||||||
# via python-jose
|
# via python-jose
|
||||||
|
editorconfig==0.17.1
|
||||||
|
# via jsbeautifier
|
||||||
email-validator==2.3.0
|
email-validator==2.3.0
|
||||||
# via -r requirements.txt
|
# via -r requirements.txt
|
||||||
|
esprima==4.0.1
|
||||||
|
# via -r requirements.txt
|
||||||
fastapi==0.122.0
|
fastapi==0.122.0
|
||||||
# via -r requirements.txt
|
# via -r requirements.txt
|
||||||
fastuuid==0.14.0
|
fastuuid==0.14.0
|
||||||
# via litellm
|
# via litellm
|
||||||
filelock==3.20.0
|
filelock==3.20.0
|
||||||
# via huggingface-hub
|
# via
|
||||||
|
# cachecontrol
|
||||||
|
# huggingface-hub
|
||||||
|
# safety
|
||||||
|
flatbuffers==25.9.23
|
||||||
|
# via onnxruntime
|
||||||
fonttools==4.61.0
|
fonttools==4.61.0
|
||||||
# via weasyprint
|
# via weasyprint
|
||||||
frozenlist==1.8.0
|
frozenlist==1.8.0
|
||||||
|
|
@ -75,10 +138,17 @@ frozenlist==1.8.0
|
||||||
# aiosignal
|
# aiosignal
|
||||||
fsspec==2025.12.0
|
fsspec==2025.12.0
|
||||||
# via huggingface-hub
|
# via huggingface-hub
|
||||||
|
google-auth==2.43.0
|
||||||
|
# via kubernetes
|
||||||
|
googleapis-common-protos==1.72.0
|
||||||
|
# via opentelemetry-exporter-otlp-proto-grpc
|
||||||
greenlet==3.3.0
|
greenlet==3.3.0
|
||||||
# via -r requirements.txt
|
# via -r requirements.txt
|
||||||
grpcio==1.67.1
|
grpcio==1.67.1
|
||||||
# via litellm
|
# via
|
||||||
|
# chromadb
|
||||||
|
# litellm
|
||||||
|
# opentelemetry-exporter-otlp-proto-grpc
|
||||||
h11==0.16.0
|
h11==0.16.0
|
||||||
# via
|
# via
|
||||||
# httpcore
|
# httpcore
|
||||||
|
|
@ -92,11 +162,19 @@ httptools==0.7.1
|
||||||
httpx==0.28.1
|
httpx==0.28.1
|
||||||
# via
|
# via
|
||||||
# -r requirements.txt
|
# -r requirements.txt
|
||||||
|
# chromadb
|
||||||
# huggingface-hub
|
# huggingface-hub
|
||||||
|
# langgraph-sdk
|
||||||
|
# langsmith
|
||||||
# litellm
|
# litellm
|
||||||
# openai
|
# openai
|
||||||
|
# safety
|
||||||
|
httpx-sse==0.4.3
|
||||||
|
# via langchain-community
|
||||||
huggingface-hub==1.2.1
|
huggingface-hub==1.2.1
|
||||||
# via tokenizers
|
# via tokenizers
|
||||||
|
humanfriendly==10.0
|
||||||
|
# via coloredlogs
|
||||||
idna==3.11
|
idna==3.11
|
||||||
# via
|
# via
|
||||||
# anyio
|
# anyio
|
||||||
|
|
@ -105,66 +183,251 @@ idna==3.11
|
||||||
# requests
|
# requests
|
||||||
# yarl
|
# yarl
|
||||||
importlib-metadata==8.7.0
|
importlib-metadata==8.7.0
|
||||||
# via litellm
|
# via
|
||||||
|
# litellm
|
||||||
|
# opentelemetry-api
|
||||||
|
importlib-resources==6.5.2
|
||||||
|
# via chromadb
|
||||||
jinja2==3.1.6
|
jinja2==3.1.6
|
||||||
# via
|
# via
|
||||||
# -r requirements.txt
|
# -r requirements.txt
|
||||||
# litellm
|
# litellm
|
||||||
|
# safety
|
||||||
jiter==0.12.0
|
jiter==0.12.0
|
||||||
# via openai
|
# via openai
|
||||||
|
joblib==1.5.2
|
||||||
|
# via nltk
|
||||||
|
jsbeautifier==1.15.4
|
||||||
|
# via -r requirements.txt
|
||||||
json-repair==0.54.2
|
json-repair==0.54.2
|
||||||
# via -r requirements.txt
|
# via -r requirements.txt
|
||||||
|
jsonpatch==1.33
|
||||||
|
# via langchain-core
|
||||||
|
jsonpointer==3.0.0
|
||||||
|
# via jsonpatch
|
||||||
jsonschema==4.25.1
|
jsonschema==4.25.1
|
||||||
# via litellm
|
# via
|
||||||
|
# chromadb
|
||||||
|
# litellm
|
||||||
jsonschema-specifications==2025.9.1
|
jsonschema-specifications==2025.9.1
|
||||||
# via jsonschema
|
# via jsonschema
|
||||||
|
kubernetes==34.1.0
|
||||||
|
# via chromadb
|
||||||
|
langchain==1.1.3
|
||||||
|
# via -r requirements.txt
|
||||||
|
langchain-classic==1.0.0
|
||||||
|
# via langchain-community
|
||||||
|
langchain-community==0.4.1
|
||||||
|
# via -r requirements.txt
|
||||||
|
langchain-core==1.2.0
|
||||||
|
# via
|
||||||
|
# langchain
|
||||||
|
# langchain-classic
|
||||||
|
# langchain-community
|
||||||
|
# langchain-openai
|
||||||
|
# langchain-text-splitters
|
||||||
|
# langgraph
|
||||||
|
# langgraph-checkpoint
|
||||||
|
# langgraph-prebuilt
|
||||||
|
langchain-openai==1.1.3
|
||||||
|
# via -r requirements.txt
|
||||||
|
langchain-text-splitters==1.1.0
|
||||||
|
# via langchain-classic
|
||||||
|
langgraph==1.0.5
|
||||||
|
# via
|
||||||
|
# -r requirements.txt
|
||||||
|
# langchain
|
||||||
|
langgraph-checkpoint==3.0.1
|
||||||
|
# via
|
||||||
|
# langgraph
|
||||||
|
# langgraph-prebuilt
|
||||||
|
langgraph-prebuilt==1.0.5
|
||||||
|
# via langgraph
|
||||||
|
langgraph-sdk==0.3.0
|
||||||
|
# via langgraph
|
||||||
|
langsmith==0.4.59
|
||||||
|
# via
|
||||||
|
# langchain-classic
|
||||||
|
# langchain-community
|
||||||
|
# langchain-core
|
||||||
|
license-expression==30.4.4
|
||||||
|
# via cyclonedx-python-lib
|
||||||
litellm==1.80.8
|
litellm==1.80.8
|
||||||
# via -r requirements.txt
|
# via -r requirements.txt
|
||||||
mako==1.3.10
|
mako==1.3.10
|
||||||
# via alembic
|
# via alembic
|
||||||
|
markdown-it-py==4.0.0
|
||||||
|
# via rich
|
||||||
markupsafe==3.0.3
|
markupsafe==3.0.3
|
||||||
# via
|
# via
|
||||||
# jinja2
|
# jinja2
|
||||||
# mako
|
# mako
|
||||||
|
marshmallow==3.26.1
|
||||||
|
# via
|
||||||
|
# dataclasses-json
|
||||||
|
# safety
|
||||||
|
mdurl==0.1.2
|
||||||
|
# via markdown-it-py
|
||||||
|
mmh3==5.2.0
|
||||||
|
# via chromadb
|
||||||
|
mpmath==1.3.0
|
||||||
|
# via sympy
|
||||||
|
msgpack==1.1.2
|
||||||
|
# via cachecontrol
|
||||||
multidict==6.7.0
|
multidict==6.7.0
|
||||||
# via
|
# via
|
||||||
# aiohttp
|
# aiohttp
|
||||||
# yarl
|
# yarl
|
||||||
|
mypy-extensions==1.1.0
|
||||||
|
# via typing-inspect
|
||||||
|
nltk==3.9.2
|
||||||
|
# via safety
|
||||||
|
numpy==2.3.5
|
||||||
|
# via
|
||||||
|
# chromadb
|
||||||
|
# langchain-community
|
||||||
|
# onnxruntime
|
||||||
|
oauthlib==3.3.1
|
||||||
|
# via requests-oauthlib
|
||||||
|
onnxruntime==1.23.2
|
||||||
|
# via chromadb
|
||||||
openai==2.9.0
|
openai==2.9.0
|
||||||
# via litellm
|
# via
|
||||||
|
# langchain-openai
|
||||||
|
# litellm
|
||||||
|
opentelemetry-api==1.39.1
|
||||||
|
# via
|
||||||
|
# chromadb
|
||||||
|
# opentelemetry-exporter-otlp-proto-grpc
|
||||||
|
# opentelemetry-sdk
|
||||||
|
# opentelemetry-semantic-conventions
|
||||||
|
opentelemetry-exporter-otlp-proto-common==1.39.1
|
||||||
|
# via opentelemetry-exporter-otlp-proto-grpc
|
||||||
|
opentelemetry-exporter-otlp-proto-grpc==1.39.1
|
||||||
|
# via chromadb
|
||||||
|
opentelemetry-proto==1.39.1
|
||||||
|
# via
|
||||||
|
# opentelemetry-exporter-otlp-proto-common
|
||||||
|
# opentelemetry-exporter-otlp-proto-grpc
|
||||||
|
opentelemetry-sdk==1.39.1
|
||||||
|
# via
|
||||||
|
# chromadb
|
||||||
|
# opentelemetry-exporter-otlp-proto-grpc
|
||||||
|
opentelemetry-semantic-conventions==0.60b1
|
||||||
|
# via opentelemetry-sdk
|
||||||
|
orjson==3.11.5
|
||||||
|
# via
|
||||||
|
# chromadb
|
||||||
|
# langgraph-sdk
|
||||||
|
# langsmith
|
||||||
|
ormsgpack==1.12.1
|
||||||
|
# via langgraph-checkpoint
|
||||||
|
overrides==7.7.0
|
||||||
|
# via chromadb
|
||||||
|
packageurl-python==0.17.6
|
||||||
|
# via cyclonedx-python-lib
|
||||||
packaging==25.0
|
packaging==25.0
|
||||||
# via huggingface-hub
|
# via
|
||||||
|
# build
|
||||||
|
# dparse
|
||||||
|
# huggingface-hub
|
||||||
|
# langchain-core
|
||||||
|
# langsmith
|
||||||
|
# marshmallow
|
||||||
|
# onnxruntime
|
||||||
|
# pip-audit
|
||||||
|
# pip-requirements-parser
|
||||||
|
# safety
|
||||||
|
# safety-schemas
|
||||||
passlib==1.7.4
|
passlib==1.7.4
|
||||||
# via -r requirements.txt
|
# via -r requirements.txt
|
||||||
|
phply==1.2.6
|
||||||
|
# via -r requirements.txt
|
||||||
pillow==12.0.0
|
pillow==12.0.0
|
||||||
# via
|
# via
|
||||||
# reportlab
|
# reportlab
|
||||||
# weasyprint
|
# weasyprint
|
||||||
|
pip==25.3
|
||||||
|
# via pip-api
|
||||||
|
pip-api==0.0.34
|
||||||
|
# via pip-audit
|
||||||
|
pip-audit==2.10.0
|
||||||
|
# via -r requirements.txt
|
||||||
|
pip-requirements-parser==32.0.1
|
||||||
|
# via pip-audit
|
||||||
|
platformdirs==4.5.1
|
||||||
|
# via pip-audit
|
||||||
|
ply==3.11
|
||||||
|
# via phply
|
||||||
|
portalocker==3.2.0
|
||||||
|
# via -r requirements.txt
|
||||||
|
posthog==5.4.0
|
||||||
|
# via chromadb
|
||||||
|
prettytable==3.17.0
|
||||||
|
# via -r requirements.txt
|
||||||
propcache==0.4.1
|
propcache==0.4.1
|
||||||
# via
|
# via
|
||||||
# aiohttp
|
# aiohttp
|
||||||
# yarl
|
# yarl
|
||||||
|
protobuf==6.33.2
|
||||||
|
# via
|
||||||
|
# googleapis-common-protos
|
||||||
|
# onnxruntime
|
||||||
|
# opentelemetry-proto
|
||||||
|
py-serializable==2.1.0
|
||||||
|
# via cyclonedx-python-lib
|
||||||
pyasn1==0.6.1
|
pyasn1==0.6.1
|
||||||
# via
|
# via
|
||||||
|
# pyasn1-modules
|
||||||
# python-jose
|
# python-jose
|
||||||
# rsa
|
# rsa
|
||||||
|
pyasn1-modules==0.4.2
|
||||||
|
# via google-auth
|
||||||
|
pybase64==1.4.3
|
||||||
|
# via chromadb
|
||||||
pycparser==2.23
|
pycparser==2.23
|
||||||
# via cffi
|
# via cffi
|
||||||
pydantic==2.12.4
|
pydantic==2.12.4
|
||||||
# via
|
# via
|
||||||
# -r requirements.txt
|
# -r requirements.txt
|
||||||
|
# chromadb
|
||||||
# fastapi
|
# fastapi
|
||||||
|
# langchain
|
||||||
|
# langchain-classic
|
||||||
|
# langchain-core
|
||||||
|
# langgraph
|
||||||
|
# langsmith
|
||||||
# litellm
|
# litellm
|
||||||
# openai
|
# openai
|
||||||
# pydantic-settings
|
# pydantic-settings
|
||||||
|
# safety
|
||||||
|
# safety-schemas
|
||||||
pydantic-core==2.41.5
|
pydantic-core==2.41.5
|
||||||
# via pydantic
|
# via pydantic
|
||||||
pydantic-settings==2.12.0
|
pydantic-settings==2.12.0
|
||||||
# via -r requirements.txt
|
# via
|
||||||
|
# -r requirements.txt
|
||||||
|
# langchain-community
|
||||||
pydyf==0.12.1
|
pydyf==0.12.1
|
||||||
# via weasyprint
|
# via weasyprint
|
||||||
|
pygments==2.19.2
|
||||||
|
# via
|
||||||
|
# -r requirements.txt
|
||||||
|
# rich
|
||||||
|
pyjsparser==2.7.1
|
||||||
|
# via -r requirements.txt
|
||||||
|
pyparsing==3.2.5
|
||||||
|
# via pip-requirements-parser
|
||||||
pyphen==0.17.2
|
pyphen==0.17.2
|
||||||
# via weasyprint
|
# via weasyprint
|
||||||
|
pypika==0.48.9
|
||||||
|
# via chromadb
|
||||||
|
pyproject-hooks==1.2.0
|
||||||
|
# via build
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
# via
|
||||||
|
# kubernetes
|
||||||
|
# posthog
|
||||||
python-dotenv==1.2.1
|
python-dotenv==1.2.1
|
||||||
# via
|
# via
|
||||||
# litellm
|
# litellm
|
||||||
|
|
@ -176,40 +439,114 @@ python-multipart==0.0.20
|
||||||
# via -r requirements.txt
|
# via -r requirements.txt
|
||||||
pyyaml==6.0.3
|
pyyaml==6.0.3
|
||||||
# via
|
# via
|
||||||
|
# bandit
|
||||||
|
# chromadb
|
||||||
# huggingface-hub
|
# huggingface-hub
|
||||||
|
# kubernetes
|
||||||
|
# langchain-classic
|
||||||
|
# langchain-community
|
||||||
|
# langchain-core
|
||||||
# uvicorn
|
# uvicorn
|
||||||
|
rarfile==4.2
|
||||||
|
# via -r requirements.txt
|
||||||
referencing==0.37.0
|
referencing==0.37.0
|
||||||
# via
|
# via
|
||||||
# jsonschema
|
# jsonschema
|
||||||
# jsonschema-specifications
|
# jsonschema-specifications
|
||||||
regex==2025.11.3
|
regex==2025.11.3
|
||||||
# via tiktoken
|
# via
|
||||||
|
# nltk
|
||||||
|
# tiktoken
|
||||||
reportlab==4.4.5
|
reportlab==4.4.5
|
||||||
# via -r requirements.txt
|
# via -r requirements.txt
|
||||||
requests==2.32.5
|
requests==2.32.5
|
||||||
# via tiktoken
|
# via
|
||||||
|
# cachecontrol
|
||||||
|
# docker
|
||||||
|
# kubernetes
|
||||||
|
# langchain-classic
|
||||||
|
# langchain-community
|
||||||
|
# langsmith
|
||||||
|
# pip-audit
|
||||||
|
# posthog
|
||||||
|
# requests-oauthlib
|
||||||
|
# requests-toolbelt
|
||||||
|
# safety
|
||||||
|
# tiktoken
|
||||||
|
requests-oauthlib==2.0.0
|
||||||
|
# via kubernetes
|
||||||
|
requests-toolbelt==1.0.0
|
||||||
|
# via langsmith
|
||||||
|
rich==14.2.0
|
||||||
|
# via
|
||||||
|
# bandit
|
||||||
|
# chromadb
|
||||||
|
# pip-audit
|
||||||
|
# typer
|
||||||
rpds-py==0.30.0
|
rpds-py==0.30.0
|
||||||
# via
|
# via
|
||||||
# jsonschema
|
# jsonschema
|
||||||
# referencing
|
# referencing
|
||||||
rsa==4.9.1
|
rsa==4.9.1
|
||||||
# via python-jose
|
# via
|
||||||
|
# google-auth
|
||||||
|
# python-jose
|
||||||
|
ruamel-yaml==0.18.16
|
||||||
|
# via
|
||||||
|
# safety
|
||||||
|
# safety-schemas
|
||||||
|
ruamel-yaml-clib==0.2.15
|
||||||
|
# via ruamel-yaml
|
||||||
|
safety==3.7.0
|
||||||
|
# via -r requirements.txt
|
||||||
|
safety-schemas==0.0.16
|
||||||
|
# via safety
|
||||||
shellingham==1.5.4
|
shellingham==1.5.4
|
||||||
# via huggingface-hub
|
# via
|
||||||
|
# huggingface-hub
|
||||||
|
# typer
|
||||||
six==1.17.0
|
six==1.17.0
|
||||||
# via ecdsa
|
# via
|
||||||
|
# ecdsa
|
||||||
|
# jsbeautifier
|
||||||
|
# kubernetes
|
||||||
|
# posthog
|
||||||
|
# python-dateutil
|
||||||
sniffio==1.3.1
|
sniffio==1.3.1
|
||||||
# via
|
# via
|
||||||
# anyio
|
# anyio
|
||||||
# openai
|
# openai
|
||||||
|
sortedcontainers==2.4.0
|
||||||
|
# via cyclonedx-python-lib
|
||||||
|
soupsieve==2.8
|
||||||
|
# via beautifulsoup4
|
||||||
sqlalchemy==2.0.44
|
sqlalchemy==2.0.44
|
||||||
# via
|
# via
|
||||||
# -r requirements.txt
|
# -r requirements.txt
|
||||||
# alembic
|
# alembic
|
||||||
|
# langchain-classic
|
||||||
|
# langchain-community
|
||||||
|
sqlparse==0.5.4
|
||||||
|
# via django
|
||||||
|
sse-starlette==3.0.3
|
||||||
|
# via -r requirements.txt
|
||||||
starlette==0.50.0
|
starlette==0.50.0
|
||||||
# via fastapi
|
# via fastapi
|
||||||
|
stevedore==5.6.0
|
||||||
|
# via bandit
|
||||||
|
sympy==1.14.0
|
||||||
|
# via onnxruntime
|
||||||
|
tenacity==9.1.2
|
||||||
|
# via
|
||||||
|
# chromadb
|
||||||
|
# langchain-community
|
||||||
|
# langchain-core
|
||||||
|
# safety
|
||||||
tiktoken==0.12.0
|
tiktoken==0.12.0
|
||||||
# via litellm
|
# via
|
||||||
|
# -r requirements.txt
|
||||||
|
# langchain-openai
|
||||||
|
# litellm
|
||||||
tinycss2==1.5.1
|
tinycss2==1.5.1
|
||||||
# via
|
# via
|
||||||
# cssselect2
|
# cssselect2
|
||||||
|
|
@ -217,36 +554,85 @@ tinycss2==1.5.1
|
||||||
tinyhtml5==2.0.0
|
tinyhtml5==2.0.0
|
||||||
# via weasyprint
|
# via weasyprint
|
||||||
tokenizers==0.22.1
|
tokenizers==0.22.1
|
||||||
# via litellm
|
# via
|
||||||
|
# chromadb
|
||||||
|
# litellm
|
||||||
|
tomli==2.3.0
|
||||||
|
# via pip-audit
|
||||||
|
tomli-w==1.2.0
|
||||||
|
# via pip-audit
|
||||||
|
tomlkit==0.13.3
|
||||||
|
# via safety
|
||||||
tqdm==4.67.1
|
tqdm==4.67.1
|
||||||
# via
|
# via
|
||||||
|
# chromadb
|
||||||
# huggingface-hub
|
# huggingface-hub
|
||||||
|
# nltk
|
||||||
# openai
|
# openai
|
||||||
|
tree-sitter==0.25.2
|
||||||
|
# via
|
||||||
|
# -r requirements.txt
|
||||||
|
# tree-sitter-languages
|
||||||
|
tree-sitter-languages==1.10.2
|
||||||
|
# via -r requirements.txt
|
||||||
|
typer==0.20.0
|
||||||
|
# via
|
||||||
|
# chromadb
|
||||||
|
# safety
|
||||||
typer-slim==0.20.0
|
typer-slim==0.20.0
|
||||||
# via huggingface-hub
|
# via huggingface-hub
|
||||||
typing-extensions==4.15.0
|
typing-extensions==4.15.0
|
||||||
# via
|
# via
|
||||||
|
# aiosignal
|
||||||
# alembic
|
# alembic
|
||||||
|
# anyio
|
||||||
|
# beautifulsoup4
|
||||||
|
# chromadb
|
||||||
|
# cyclonedx-python-lib
|
||||||
# fastapi
|
# fastapi
|
||||||
# huggingface-hub
|
# huggingface-hub
|
||||||
|
# langchain-core
|
||||||
# openai
|
# openai
|
||||||
|
# opentelemetry-api
|
||||||
|
# opentelemetry-exporter-otlp-proto-grpc
|
||||||
|
# opentelemetry-sdk
|
||||||
|
# opentelemetry-semantic-conventions
|
||||||
# pydantic
|
# pydantic
|
||||||
# pydantic-core
|
# pydantic-core
|
||||||
|
# referencing
|
||||||
|
# safety
|
||||||
|
# safety-schemas
|
||||||
# sqlalchemy
|
# sqlalchemy
|
||||||
|
# starlette
|
||||||
|
# typer
|
||||||
# typer-slim
|
# typer-slim
|
||||||
|
# typing-inspect
|
||||||
# typing-inspection
|
# typing-inspection
|
||||||
|
typing-inspect==0.9.0
|
||||||
|
# via dataclasses-json
|
||||||
typing-inspection==0.4.2
|
typing-inspection==0.4.2
|
||||||
# via
|
# via
|
||||||
# pydantic
|
# pydantic
|
||||||
# pydantic-settings
|
# pydantic-settings
|
||||||
urllib3==2.6.0
|
urllib3==2.3.0
|
||||||
# via requests
|
# via
|
||||||
|
# docker
|
||||||
|
# kubernetes
|
||||||
|
# requests
|
||||||
|
uuid-utils==0.12.0
|
||||||
|
# via
|
||||||
|
# langchain-core
|
||||||
|
# langsmith
|
||||||
uvicorn==0.38.0
|
uvicorn==0.38.0
|
||||||
# via -r requirements.txt
|
# via
|
||||||
|
# -r requirements.txt
|
||||||
|
# chromadb
|
||||||
uvloop==0.22.1
|
uvloop==0.22.1
|
||||||
# via uvicorn
|
# via uvicorn
|
||||||
watchfiles==1.1.1
|
watchfiles==1.1.1
|
||||||
# via uvicorn
|
# via uvicorn
|
||||||
|
wcwidth==0.2.14
|
||||||
|
# via prettytable
|
||||||
weasyprint==67.0
|
weasyprint==67.0
|
||||||
# via -r requirements.txt
|
# via -r requirements.txt
|
||||||
webencodings==0.5.1
|
webencodings==0.5.1
|
||||||
|
|
@ -254,11 +640,17 @@ webencodings==0.5.1
|
||||||
# cssselect2
|
# cssselect2
|
||||||
# tinycss2
|
# tinycss2
|
||||||
# tinyhtml5
|
# tinyhtml5
|
||||||
|
websocket-client==1.9.0
|
||||||
|
# via kubernetes
|
||||||
websockets==15.0.1
|
websockets==15.0.1
|
||||||
# via uvicorn
|
# via uvicorn
|
||||||
|
xxhash==3.6.0
|
||||||
|
# via langgraph
|
||||||
yarl==1.22.0
|
yarl==1.22.0
|
||||||
# via aiohttp
|
# via aiohttp
|
||||||
zipp==3.23.0
|
zipp==3.23.0
|
||||||
# via importlib-metadata
|
# via importlib-metadata
|
||||||
zopfli==0.4.0
|
zopfli==0.4.0
|
||||||
# via fonttools
|
# via fonttools
|
||||||
|
zstandard==0.25.0
|
||||||
|
# via langsmith
|
||||||
|
|
|
||||||
|
|
@ -65,3 +65,29 @@ safety>=2.3.0
|
||||||
# 依赖漏洞扫描
|
# 依赖漏洞扫描
|
||||||
pip-audit>=2.6.0
|
pip-audit>=2.6.0
|
||||||
|
|
||||||
|
# ============ Kunlun-M 依赖 (MIT License) ============
|
||||||
|
# https://github.com/LoRexxar/Kunlun-M
|
||||||
|
# Kunlun-M 是静态代码安全审计工具,支持 PHP/JS 语义分析
|
||||||
|
|
||||||
|
# PHP/JS 解析器
|
||||||
|
pyjsparser>=2.7.1
|
||||||
|
phply>=1.2.6
|
||||||
|
esprima>=4.0.1
|
||||||
|
jsbeautifier>=1.14.0
|
||||||
|
|
||||||
|
# 工具库
|
||||||
|
colorlog>=6.0.0
|
||||||
|
portalocker>=2.0.0
|
||||||
|
prettytable>=3.0.0
|
||||||
|
rarfile>=4.0
|
||||||
|
|
||||||
|
# HTML 解析
|
||||||
|
beautifulsoup4>=4.12.0
|
||||||
|
|
||||||
|
# Django (Kunlun-M 数据库)
|
||||||
|
django>=4.2.0
|
||||||
|
|
||||||
|
# MySQL 客户端 (可选,用于 Kunlun-M Web 模式)
|
||||||
|
# 如需 MySQL 支持,请安装: pip install mysqlclient
|
||||||
|
# 注意: mysqlclient 需要系统级 MySQL 开发库
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -449,7 +449,7 @@ export default function FileSelectionDialog({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="!max-w-[1000px] !w-[95vw] max-h-[85vh] flex flex-col cyber-card p-0 bg-[#0c0c12]">
|
<DialogContent className="!max-w-[1000px] !w-[95vw] max-h-[85vh] flex flex-col cyber-card p-0 bg-[#0c0c12] !fixed">
|
||||||
<DialogHeader className="cyber-card-header flex-shrink-0">
|
<DialogHeader className="cyber-card-header flex-shrink-0">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<FolderOpen className="w-5 h-5 text-primary" />
|
<FolderOpen className="w-5 h-5 text-primary" />
|
||||||
|
|
@ -464,7 +464,7 @@ export default function FileSelectionDialog({
|
||||||
)}
|
)}
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="p-5 flex-1 flex flex-col min-h-0 space-y-3">
|
<div className="p-5 flex-1 flex flex-col min-h-0 space-y-3 overflow-y-auto">
|
||||||
{/* 工具栏 */}
|
{/* 工具栏 */}
|
||||||
<div className="flex items-center gap-2 flex-wrap">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
{/* 搜索框 */}
|
{/* 搜索框 */}
|
||||||
|
|
|
||||||
|
|
@ -60,91 +60,49 @@ export default function ExportReportDialog({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const formats = [
|
|
||||||
{
|
|
||||||
value: "json" as ExportFormat,
|
|
||||||
label: "JSON 格式",
|
|
||||||
description: "结构化数据,适合程序处理和集成",
|
|
||||||
icon: FileJson,
|
|
||||||
color: "text-amber-400",
|
|
||||||
bgColor: "bg-amber-500/20",
|
|
||||||
borderColor: "border-amber-500/30"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "pdf" as ExportFormat,
|
|
||||||
label: "PDF 格式",
|
|
||||||
description: "专业报告,适合打印和分享",
|
|
||||||
icon: FileText,
|
|
||||||
color: "text-rose-400",
|
|
||||||
bgColor: "bg-rose-500/20",
|
|
||||||
borderColor: "border-rose-500/30"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="sm:max-w-[600px] cyber-card p-0 bg-[#0c0c12]">
|
<DialogContent className="sm:max-w-[600px] bg-[#0c0c12] border-gray-700">
|
||||||
<DialogHeader className="cyber-card-header">
|
<DialogHeader>
|
||||||
<div className="flex items-center gap-3">
|
<DialogTitle className="flex items-center gap-3 text-lg font-bold uppercase tracking-wider text-white">
|
||||||
<Download className="w-5 h-5 text-primary" />
|
<Download className="w-5 h-5 text-primary" />
|
||||||
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-white">
|
导出审计报告
|
||||||
导出审计报告
|
</DialogTitle>
|
||||||
</DialogTitle>
|
<DialogDescription className="text-gray-400 font-mono text-xs">
|
||||||
</div>
|
选择报告格式并导出完整的代码审计结果
|
||||||
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<DialogDescription className="px-6 pt-4 text-gray-400 font-mono text-xs">
|
|
||||||
选择报告格式并导出完整的代码审计结果
|
|
||||||
</DialogDescription>
|
|
||||||
|
|
||||||
<div className="p-6">
|
<div className="py-4">
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={selectedFormat}
|
value={selectedFormat}
|
||||||
onValueChange={(value) => setSelectedFormat(value as ExportFormat)}
|
onValueChange={(value) => setSelectedFormat(value as ExportFormat)}
|
||||||
className="space-y-4"
|
className="space-y-4"
|
||||||
>
|
>
|
||||||
{formats.map((format) => {
|
<div className="flex items-center space-x-3 p-4 border border-gray-700 rounded bg-gray-900/30 cursor-pointer hover:bg-gray-800/50">
|
||||||
const Icon = format.icon;
|
<RadioGroupItem value="json" id="json" />
|
||||||
const isSelected = selectedFormat === format.value;
|
<Label htmlFor="json" className="flex items-center gap-3 cursor-pointer flex-1">
|
||||||
|
<FileJson className="w-5 h-5 text-amber-400" />
|
||||||
return (
|
<div>
|
||||||
<div key={format.value} className="relative">
|
<div className="font-bold text-gray-200">JSON 格式</div>
|
||||||
<RadioGroupItem
|
<div className="text-xs text-gray-500">结构化数据,适合程序处理和集成</div>
|
||||||
value={format.value}
|
|
||||||
id={format.value}
|
|
||||||
className="peer sr-only"
|
|
||||||
/>
|
|
||||||
<Label
|
|
||||||
htmlFor={format.value}
|
|
||||||
className={`flex items-start space-x-4 p-4 border cursor-pointer transition-all rounded font-mono ${isSelected
|
|
||||||
? "border-primary bg-primary/10"
|
|
||||||
: "border-gray-700 bg-gray-900/30 hover:bg-gray-800/50 hover:border-gray-600"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`w-12 h-12 flex items-center justify-center rounded ${isSelected ? "bg-primary/20 border border-primary/50" : format.bgColor + " border " + format.borderColor
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<Icon className={`w-6 h-6 ${isSelected ? "text-primary" : format.color}`} />
|
|
||||||
</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="flex items-center justify-between mb-1">
|
|
||||||
<h4 className={`font-bold uppercase ${isSelected ? "text-primary" : "text-gray-200"}`}>
|
|
||||||
{format.label}
|
|
||||||
</h4>
|
|
||||||
{isSelected && (
|
|
||||||
<div className="w-3 h-3 bg-primary rounded-full shadow-[0_0_10px_rgba(255,107,44,0.5)]" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-gray-500">{format.description}</p>
|
|
||||||
</div>
|
|
||||||
</Label>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
</Label>
|
||||||
})}
|
</div>
|
||||||
|
<div className="flex items-center space-x-3 p-4 border border-gray-700 rounded bg-gray-900/30 cursor-pointer hover:bg-gray-800/50">
|
||||||
|
<RadioGroupItem value="pdf" id="pdf" />
|
||||||
|
<Label htmlFor="pdf" className="flex items-center gap-3 cursor-pointer flex-1">
|
||||||
|
<FileText className="w-5 h-5 text-rose-400" />
|
||||||
|
<div>
|
||||||
|
<div className="font-bold text-gray-200">PDF 格式</div>
|
||||||
|
<div className="text-xs text-gray-500">专业报告,适合打印和分享</div>
|
||||||
|
</div>
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
||||||
{/* 报告预览信息 */}
|
{/* 报告预览信息 */}
|
||||||
<div className="mt-6 cyber-card p-0">
|
<div className="mt-6 border border-gray-700 rounded bg-gray-900/30">
|
||||||
<div className="px-4 py-2 border-b border-gray-800 bg-gray-900/50 flex items-center gap-2">
|
<div className="px-4 py-2 border-b border-gray-800 bg-gray-900/50 flex items-center gap-2">
|
||||||
<Terminal className="w-3 h-3 text-primary" />
|
<Terminal className="w-3 h-3 text-primary" />
|
||||||
<h4 className="font-bold text-gray-300 uppercase text-xs">报告内容预览</h4>
|
<h4 className="font-bold text-gray-300 uppercase text-xs">报告内容预览</h4>
|
||||||
|
|
@ -180,19 +138,17 @@ export default function ExportReportDialog({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter className="p-4 border-t border-gray-800 bg-gray-900/50 flex justify-end gap-3">
|
<DialogFooter className="border-t border-gray-800 pt-4">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => onOpenChange(false)}
|
onClick={() => onOpenChange(false)}
|
||||||
disabled={isExporting}
|
disabled={isExporting}
|
||||||
className="cyber-btn-outline h-10"
|
|
||||||
>
|
>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleExport}
|
onClick={handleExport}
|
||||||
disabled={isExporting}
|
disabled={isExporting}
|
||||||
className="cyber-btn-primary h-10 font-bold uppercase"
|
|
||||||
>
|
>
|
||||||
{isExporting ? (
|
{isExporting ? (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -68,105 +68,54 @@ export default function InstantExportDialog({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const formats = [
|
const isPdfDisabled = !analysisId;
|
||||||
{
|
|
||||||
value: "json" as ExportFormat,
|
|
||||||
label: "JSON 格式",
|
|
||||||
description: "结构化数据,适合程序处理和集成",
|
|
||||||
icon: FileJson,
|
|
||||||
color: "text-amber-400",
|
|
||||||
bgColor: "bg-amber-500/20",
|
|
||||||
borderColor: "border-amber-500/30",
|
|
||||||
disabled: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "pdf" as ExportFormat,
|
|
||||||
label: "PDF 格式",
|
|
||||||
description: analysisId ? "专业报告,适合打印和分享" : "需要先保存到历史记录",
|
|
||||||
icon: FileText,
|
|
||||||
color: "text-rose-400",
|
|
||||||
bgColor: "bg-rose-500/20",
|
|
||||||
borderColor: "border-rose-500/30",
|
|
||||||
disabled: !analysisId
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="sm:max-w-[600px] cyber-card p-0 bg-[#0c0c12]">
|
<DialogContent className="sm:max-w-[600px] bg-[#0c0c12] border-gray-700">
|
||||||
<DialogHeader className="cyber-card-header">
|
<DialogHeader>
|
||||||
<div className="flex items-center gap-3">
|
<DialogTitle className="flex items-center gap-3 text-lg font-bold uppercase tracking-wider text-white">
|
||||||
<Download className="w-5 h-5 text-primary" />
|
<Download className="w-5 h-5 text-primary" />
|
||||||
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-white">
|
导出分析报告
|
||||||
导出分析报告
|
</DialogTitle>
|
||||||
</DialogTitle>
|
<DialogDescription className="text-gray-400 font-mono text-xs">
|
||||||
</div>
|
选择报告格式并导出代码分析结果
|
||||||
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<DialogDescription className="px-6 pt-4 text-gray-400 font-mono text-xs">
|
|
||||||
选择报告格式并导出代码分析结果
|
|
||||||
</DialogDescription>
|
|
||||||
|
|
||||||
<div className="p-6">
|
<div className="py-4">
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={selectedFormat}
|
value={selectedFormat}
|
||||||
onValueChange={(value) => setSelectedFormat(value as ExportFormat)}
|
onValueChange={(value) => setSelectedFormat(value as ExportFormat)}
|
||||||
className="space-y-4"
|
className="space-y-4"
|
||||||
>
|
>
|
||||||
{formats.map((format) => {
|
<div className="flex items-center space-x-3 p-4 border border-gray-700 rounded bg-gray-900/30 cursor-pointer hover:bg-gray-800/50">
|
||||||
const Icon = format.icon;
|
<RadioGroupItem value="json" id="json" />
|
||||||
const isSelected = selectedFormat === format.value;
|
<Label htmlFor="json" className="flex items-center gap-3 cursor-pointer flex-1">
|
||||||
|
<FileJson className="w-5 h-5 text-amber-400" />
|
||||||
return (
|
<div>
|
||||||
<div key={format.value} className="relative">
|
<div className="font-bold text-gray-200">JSON 格式</div>
|
||||||
<RadioGroupItem
|
<div className="text-xs text-gray-500">结构化数据,适合程序处理和集成</div>
|
||||||
value={format.value}
|
|
||||||
id={format.value}
|
|
||||||
className="peer sr-only"
|
|
||||||
disabled={format.disabled}
|
|
||||||
/>
|
|
||||||
<Label
|
|
||||||
htmlFor={format.value}
|
|
||||||
className={`flex items-start space-x-4 p-4 border cursor-pointer transition-all rounded font-mono ${
|
|
||||||
format.disabled
|
|
||||||
? "border-gray-800 bg-gray-900/20 cursor-not-allowed opacity-50"
|
|
||||||
: isSelected
|
|
||||||
? "border-primary bg-primary/10"
|
|
||||||
: "border-gray-700 bg-gray-900/30 hover:bg-gray-800/50 hover:border-gray-600"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`w-12 h-12 flex items-center justify-center rounded ${
|
|
||||||
format.disabled
|
|
||||||
? "bg-gray-800 border border-gray-700"
|
|
||||||
: isSelected
|
|
||||||
? "bg-primary/20 border border-primary/50"
|
|
||||||
: format.bgColor + " border " + format.borderColor
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<Icon className={`w-6 h-6 ${format.disabled ? "text-gray-600" : isSelected ? "text-primary" : format.color}`} />
|
|
||||||
</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="flex items-center justify-between mb-1">
|
|
||||||
<h4 className={`font-bold uppercase ${format.disabled ? "text-gray-600" : isSelected ? "text-primary" : "text-gray-200"}`}>
|
|
||||||
{format.label}
|
|
||||||
</h4>
|
|
||||||
{isSelected && !format.disabled && (
|
|
||||||
<div className="w-3 h-3 bg-primary rounded-full shadow-[0_0_10px_rgba(255,107,44,0.5)]" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<p className={`text-xs ${format.disabled ? "text-gray-600" : "text-gray-500"}`}>
|
|
||||||
{format.disabled && <AlertTriangle className="w-3 h-3 inline mr-1 text-amber-500" />}
|
|
||||||
{format.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Label>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
</Label>
|
||||||
})}
|
</div>
|
||||||
|
<div className={`flex items-center space-x-3 p-4 border border-gray-700 rounded bg-gray-900/30 ${isPdfDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:bg-gray-800/50'}`}>
|
||||||
|
<RadioGroupItem value="pdf" id="pdf" disabled={isPdfDisabled} />
|
||||||
|
<Label htmlFor="pdf" className={`flex items-center gap-3 flex-1 ${isPdfDisabled ? 'cursor-not-allowed' : 'cursor-pointer'}`}>
|
||||||
|
<FileText className="w-5 h-5 text-rose-400" />
|
||||||
|
<div>
|
||||||
|
<div className="font-bold text-gray-200">PDF 格式</div>
|
||||||
|
<div className="text-xs text-gray-500">
|
||||||
|
{isPdfDisabled && <AlertTriangle className="w-3 h-3 inline mr-1 text-amber-500" />}
|
||||||
|
{isPdfDisabled ? "需要先保存到历史记录" : "专业报告,适合打印和分享"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
||||||
{/* 报告预览信息 */}
|
{/* 报告预览信息 */}
|
||||||
<div className="mt-6 cyber-card p-0">
|
<div className="mt-6 border border-gray-700 rounded bg-gray-900/30">
|
||||||
<div className="px-4 py-2 border-b border-gray-800 bg-gray-900/50 flex items-center gap-2">
|
<div className="px-4 py-2 border-b border-gray-800 bg-gray-900/50 flex items-center gap-2">
|
||||||
<Terminal className="w-3 h-3 text-primary" />
|
<Terminal className="w-3 h-3 text-primary" />
|
||||||
<h4 className="font-bold text-gray-300 uppercase text-xs">报告内容预览</h4>
|
<h4 className="font-bold text-gray-300 uppercase text-xs">报告内容预览</h4>
|
||||||
|
|
@ -192,19 +141,17 @@ export default function InstantExportDialog({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter className="p-4 border-t border-gray-800 bg-gray-900/50 flex justify-end gap-3">
|
<DialogFooter className="border-t border-gray-800 pt-4">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => onOpenChange(false)}
|
onClick={() => onOpenChange(false)}
|
||||||
disabled={isExporting}
|
disabled={isExporting}
|
||||||
className="cyber-btn-outline h-10"
|
|
||||||
>
|
>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleExport}
|
onClick={handleExport}
|
||||||
disabled={isExporting || (selectedFormat === "pdf" && !analysisId)}
|
disabled={isExporting || (selectedFormat === "pdf" && !analysisId)}
|
||||||
className="cyber-btn-primary h-10 font-bold uppercase"
|
|
||||||
>
|
>
|
||||||
{isExporting ? (
|
{isExporting ? (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -1,135 +1,122 @@
|
||||||
"use client";
|
"use client"
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react"
|
||||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||||
import { XIcon } from "lucide-react";
|
import { X } from "lucide-react"
|
||||||
|
|
||||||
import { cn } from "@/shared/utils/utils";
|
import { cn } from "@/shared/utils/utils"
|
||||||
|
|
||||||
function Dialog({
|
const Dialog = DialogPrimitive.Root
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
||||||
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function DialogTrigger({
|
const DialogTrigger = DialogPrimitive.Trigger
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
||||||
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function DialogPortal({
|
const DialogPortal = DialogPrimitive.Portal
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
||||||
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function DialogClose({
|
const DialogClose = DialogPrimitive.Close
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
|
||||||
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function DialogOverlay({
|
const DialogOverlay = React.forwardRef<
|
||||||
className,
|
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||||
...props
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||||
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
>(({ className, ...props }, ref) => (
|
||||||
return (
|
<DialogPrimitive.Overlay
|
||||||
<DialogPrimitive.Overlay
|
ref={ref}
|
||||||
data-slot="dialog-overlay"
|
className={cn(
|
||||||
|
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
|
||||||
|
|
||||||
|
const DialogContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<DialogPortal>
|
||||||
|
<DialogOverlay />
|
||||||
|
<DialogPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
>
|
||||||
);
|
{children}
|
||||||
}
|
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</DialogPrimitive.Close>
|
||||||
|
</DialogPrimitive.Content>
|
||||||
|
</DialogPortal>
|
||||||
|
))
|
||||||
|
DialogContent.displayName = DialogPrimitive.Content.displayName
|
||||||
|
|
||||||
function DialogContent({
|
const DialogHeader = ({
|
||||||
className,
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof DialogPrimitive.Content>) {
|
|
||||||
return (
|
|
||||||
<DialogPortal data-slot="dialog-portal">
|
|
||||||
<DialogOverlay />
|
|
||||||
<DialogPrimitive.Content
|
|
||||||
data-slot="dialog-content"
|
|
||||||
className={cn(
|
|
||||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
|
|
||||||
<XIcon />
|
|
||||||
<span className="sr-only">Close</span>
|
|
||||||
</DialogPrimitive.Close>
|
|
||||||
</DialogPrimitive.Content>
|
|
||||||
</DialogPortal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="dialog-header"
|
|
||||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="dialog-footer"
|
|
||||||
className={cn(
|
|
||||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function DialogTitle({
|
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
return (
|
<div
|
||||||
<DialogPrimitive.Title
|
className={cn(
|
||||||
data-slot="dialog-title"
|
"flex flex-col space-y-1.5 text-center sm:text-left",
|
||||||
className={cn("text-lg leading-none font-semibold", className)}
|
className
|
||||||
{...props}
|
)}
|
||||||
/>
|
{...props}
|
||||||
);
|
/>
|
||||||
}
|
)
|
||||||
|
DialogHeader.displayName = "DialogHeader"
|
||||||
|
|
||||||
function DialogDescription({
|
const DialogFooter = ({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
return (
|
<div
|
||||||
<DialogPrimitive.Description
|
className={cn(
|
||||||
data-slot="dialog-description"
|
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||||
className={cn("text-muted-foreground text-sm", className)}
|
className
|
||||||
{...props}
|
)}
|
||||||
/>
|
{...props}
|
||||||
);
|
/>
|
||||||
}
|
)
|
||||||
|
DialogFooter.displayName = "DialogFooter"
|
||||||
|
|
||||||
|
const DialogTitle = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DialogPrimitive.Title>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<DialogPrimitive.Title
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"text-lg font-semibold leading-none tracking-tight",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
DialogTitle.displayName = DialogPrimitive.Title.displayName
|
||||||
|
|
||||||
|
const DialogDescription = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DialogPrimitive.Description>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<DialogPrimitive.Description
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
DialogDescription.displayName = DialogPrimitive.Description.displayName
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogClose,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogOverlay,
|
|
||||||
DialogPortal,
|
DialogPortal,
|
||||||
DialogTitle,
|
DialogOverlay,
|
||||||
|
DialogClose,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
};
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogFooter,
|
||||||
|
DialogTitle,
|
||||||
|
DialogDescription,
|
||||||
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -423,7 +423,7 @@ export default function Projects() {
|
||||||
初始化项目
|
初始化项目
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto cyber-card border-gray-700 bg-[#0c0c12] p-0">
|
<DialogContent className="max-w-2xl max-h-[85vh] !overflow-y-auto cyber-card border-gray-700 bg-[#0c0c12] p-0 !fixed">
|
||||||
{/* Terminal Header */}
|
{/* Terminal Header */}
|
||||||
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50">
|
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50">
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
|
|
@ -934,7 +934,7 @@ export default function Projects() {
|
||||||
|
|
||||||
{/* Edit Dialog */}
|
{/* Edit Dialog */}
|
||||||
<Dialog open={showEditDialog} onOpenChange={setShowEditDialog}>
|
<Dialog open={showEditDialog} onOpenChange={setShowEditDialog}>
|
||||||
<DialogContent className="max-w-2xl cyber-card border-gray-700 bg-[#0c0c12] p-0">
|
<DialogContent className="max-w-2xl cyber-card border-gray-700 bg-[#0c0c12] p-0 !fixed">
|
||||||
{/* Terminal Header */}
|
{/* Terminal Header */}
|
||||||
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50">
|
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50">
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
|
|
@ -1185,7 +1185,7 @@ export default function Projects() {
|
||||||
|
|
||||||
{/* Delete Dialog */}
|
{/* Delete Dialog */}
|
||||||
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
||||||
<AlertDialogContent className="cyber-card border-gray-700 bg-[#0c0c12] p-0">
|
<AlertDialogContent className="cyber-card border-gray-700 bg-[#0c0c12] p-0 !fixed">
|
||||||
{/* Terminal Header */}
|
{/* Terminal Header */}
|
||||||
<div className="flex items-center gap-2 px-4 py-3 bg-rose-500/10 border-b border-rose-500/30">
|
<div className="flex items-center gap-2 px-4 py-3 bg-rose-500/10 border-b border-rose-500/30">
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
|
|
|
||||||
|
|
@ -401,7 +401,7 @@ export default function PromptManager() {
|
||||||
|
|
||||||
{/* Test Dialog */}
|
{/* Test Dialog */}
|
||||||
<Dialog open={showTestDialog} onOpenChange={setShowTestDialog}>
|
<Dialog open={showTestDialog} onOpenChange={setShowTestDialog}>
|
||||||
<DialogContent className="!max-w-6xl w-[90vw] max-h-[90vh] overflow-y-auto cyber-card p-0 bg-[#0c0c12]">
|
<DialogContent className="!max-w-6xl w-[90vw] max-h-[90vh] !overflow-y-auto cyber-card p-0 bg-[#0c0c12] !fixed">
|
||||||
<DialogHeader className="cyber-card-header">
|
<DialogHeader className="cyber-card-header">
|
||||||
<Sparkles className="w-5 h-5 text-violet-400" />
|
<Sparkles className="w-5 h-5 text-violet-400" />
|
||||||
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-white">
|
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-white">
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,7 @@ export default function RecycleBin() {
|
||||||
|
|
||||||
{/* Restore Dialog */}
|
{/* Restore Dialog */}
|
||||||
<AlertDialog open={showRestoreDialog} onOpenChange={setShowRestoreDialog}>
|
<AlertDialog open={showRestoreDialog} onOpenChange={setShowRestoreDialog}>
|
||||||
<AlertDialogContent className="cyber-card p-0 bg-[#0c0c12] max-w-md">
|
<AlertDialogContent className="cyber-card p-0 bg-[#0c0c12] max-w-md !fixed">
|
||||||
<AlertDialogHeader className="cyber-card-header">
|
<AlertDialogHeader className="cyber-card-header">
|
||||||
<RotateCcw className="w-5 h-5 text-emerald-400" />
|
<RotateCcw className="w-5 h-5 text-emerald-400" />
|
||||||
<AlertDialogTitle className="text-lg font-bold uppercase tracking-wider text-white">
|
<AlertDialogTitle className="text-lg font-bold uppercase tracking-wider text-white">
|
||||||
|
|
@ -288,7 +288,7 @@ export default function RecycleBin() {
|
||||||
|
|
||||||
{/* Permanent Delete Dialog */}
|
{/* Permanent Delete Dialog */}
|
||||||
<AlertDialog open={showPermanentDeleteDialog} onOpenChange={setShowPermanentDeleteDialog}>
|
<AlertDialog open={showPermanentDeleteDialog} onOpenChange={setShowPermanentDeleteDialog}>
|
||||||
<AlertDialogContent className="cyber-card p-0 bg-[#0c0c12] max-w-md">
|
<AlertDialogContent className="cyber-card p-0 bg-[#0c0c12] max-w-md !fixed">
|
||||||
<AlertDialogHeader className="p-4 border-b border-rose-500/30 bg-rose-500/10 flex flex-row items-center gap-2">
|
<AlertDialogHeader className="p-4 border-b border-rose-500/30 bg-rose-500/10 flex flex-row items-center gap-2">
|
||||||
<AlertTriangle className="w-5 h-5 text-rose-400" />
|
<AlertTriangle className="w-5 h-5 text-rose-400" />
|
||||||
<AlertDialogTitle className="text-lg font-bold uppercase tracking-wider text-rose-400">
|
<AlertDialogTitle className="text-lg font-bold uppercase tracking-wider text-rose-400">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
代码阅读IDE 集成开发环境(IDE、Integrated Development Environment)主要用于提供应用程序开发的环境、通常包括编辑器、编译器、调试器等。Chocolat C++、PHP、Ruby、Haskell https://chocolatapp.com/ Mac OS X Mac系统上一款强大的文本编辑器
|
||||||
|
Eclipse-aptana PHP、Javascript、AJAX、Adobe AIR、Apple iPhone、Ruby on Rails http://www.aptana.com/ Windows、Mac OS X 一个非常强大、开源、JavaScript-focused的AJAX开发IDE
|
||||||
|
netbeans PHP、C、C++、JavaScript、Ruby、Groovy、Grails https://netbeans.org/index_zh_CN.html Windows、Mac OS X 、 Linux 开放源代码的Java IDE
|
||||||
|
Coda HTML、PHP、JavaScript、CSS https://www.panic.com/coda/ Mac OS X 界面漂亮、操作简单的网页开发工具
|
||||||
|
Editplus HTML、、PHP、Java、 C、C++、CSS、ASP、Perl、JavaScript、 VBScript、Python、 Ruby on Rails https://www.editplus.com/ Windows 小巧但是功能强大的可处理文本、HTML和程序语言的Windows编辑器
|
||||||
|
Anjuta C、C++ http://anjuta.org/ Linux 一个建立在GNU/Linux下为C、C++提供编译的集成开发环境
|
||||||
|
kDevelop C、 C++、 Python、 QML、JavaScript and PHP https://www.kdevelop.org/ Windows、Mac OS X 、 Linux 一个自由、开放源代码 的 IDE(集成开发环境
|
||||||
|
Ultimate C、C++ https://www.ultimatepp.org/ Windows、 Linux 一个C ++跨平台快速应用程序开发框架
|
||||||
|
C-Free C、C++ http://www.programarts.com/cfree_en/download.htm Windows 一款C/C++集成开发环境(IDE)
|
||||||
|
visual-mingw C、C++ http://visual-mingw.sourceforge.net/ Windows 一个可自由使用和自由发布的Windows特定头文件和使用GNU工具集导入库的集合
|
||||||
|
Eclipse CDT C、C++ http://www.eclipse.org/cdt/ Windows、Mac OS X Eclipse的CDT搭建标准的C/C++开发环境
|
||||||
|
espresso HTML、XML、 CSS、JavaScript https://espresso.en.softonic.com/mac Windows、Mac OS X 强大的HTML、XML、CSS和JavaScript的Web开发工具
|
||||||
|
visual-web-developer-express C#、Visual Basic、F#、C++、HTML、JavaScript、TypeScript、Python 等 https://www.visualstudio.com/zh-hans/vs/visual-studio-express/?rr=http%3A%2F%2Fwww.csdn.net%2Farticle%2F2012-02-15%2F311835 Windows、Mac OS X 一款非常好用且功能强大的web应用程序开发工具
|
||||||
|
IntelliJ IDEA Java http://www.jetbrains.com/webstorm/ Windows、Mac OS X 、 Linux 业界被公认为最好的java开发工具之一
|
||||||
|
Codelite C、C++、PHP 、Node.js https://codelite.org/ Windows、 Linux 一个功能强大的开源、C/C++编程语言的跨平台ID
|
||||||
|
Zend Studio PHP http://www.zend.com/en/products/studio Windows、Mac OS X 、 Linux Zend Technologies公司开发的PHP语言集成开发环境(IDE)
|
||||||
|
GNU Emacs All http://www.gnu.org/software/emacs/download.html Windows、Mac OS X 著名的集成开发环境和文本编辑器
|
||||||
|
E-TextEditor All https://e-texteditor.en.softonic.com/download Windows 一款新概念的窗口本文编辑软件
|
||||||
|
Notepad++ All https://notepad-plus-plus.org/ Windows Windows操作系统下的一套文本编辑器
|
||||||
|
Bbedit All http://www.barebones.com/products/bbedit/ Mac OS X 一款适用于Mac操作系统的软件
|
||||||
|
TextMate All http://macromates.com/ Mac OS X Mac下的著名的文本编辑器软件
|
||||||
|
textpad All https://www.textpad.com/ Windows 一个强大的替代 Windows 记事本 Notepad 的文本编辑器
|
||||||
|
SUBLIME TEXT All http://www.sublimetext.com/ Windows、Mac OS X 、 Linux 一个代码编辑器、也是HTML和散文先进的文本编辑器
|
||||||
|
skEdit All https://skedit.en.softonic.com/mac Mac OS X Mac HTML和文本编辑器
|
||||||
|
ATOM All https://atom.io/ Windows、Mac OS X 、 Linux github专门为程序员推出的一个跨平台文本编辑器
|
||||||
|
GEANY C、CPP、Java、Python、PHP、 HTML、 DocBook、 Perl、 LateX 和 Bash脚本 http://www.geany.org/ Windows、Mac OS X 、 Linux 一个小巧的使用GTK+2开发的跨平台的开源集成开发环境
|
||||||
|
JEDIT All http://www.jedit.org/ Windows、Mac OS X 、 Linux 一个用Java语言开发的文本编辑器
|
||||||
|
VIM All http://www.vim.org/ Windows、Mac OS X 、 Linux 一个类似于Vi的著名的功能强大、高度可定制的文本编辑器
|
||||||
|
PSPAD All http://www.pspad.com/en/ Windows 功能非常强大的代码浏览器
|
||||||
|
komodo-ide Python、Perl、PHP 、Ruby https://www.activestate.com/komodo-ide Windows、Mac OS X 、 Linux 一个独特的特性是常规表达式调试器
|
||||||
|
|
||||||
|
商业代码审计工具 在源代码的静态安全审计中、使用自动化工具代替人工漏洞挖掘、可以显著提高审计工作的效率。学会利用自动化代码审计工具、是每一个代码审计人员必备的能力。Fortify SCA Java、JSP、ASP.NET、C#、VB.NET、C、C++、COBOL、ColdFusion https://www.fortify.com Windows、Mac OS X 、 Linux 是一个静态的、白盒的软件源代码安全测试工具
|
||||||
|
Checkmarx CxSuite JAVA、ASP.NET(C#、VB.NET)、JavaScript、Jscript、C、C++、APEX等语言 http://www.dumasoftware.com/corporate01.asp Windows、Mac OS X 、 Linux 一个独特的源代码分析解决方案
|
||||||
|
360代码卫士 C、C++、C#、Objective-C、Java、JSP、JavaScript、PHP、Python、Cobol等 http://b.360.cn/codesafe/intro.html Windows、Mac OS X 、 Linux 360企业安全集团基于多年源代码安全实践经验开发的新一代源代码安全检测系统
|
||||||
|
RIPS PHP https://sourceforge.net/projects/rips-scanner/ Windows、Mac OS X 、 Linux PHP代码审计系统
|
||||||
|
VCG(VisualCodeGrepper) C、C++、C#、VB、PHP、Java、PL、SQL http://downloads.informer.com/visualcodegrepper/ Windows、Mac OS X 、 Linux 免费代码安全审计工具
|
||||||
|
blackduck(protex、Codecenter、和Export) All https://www.blackducksoftware.com/ Windows、Mac OS X 、 Linux 是一款对源代码进行扫描、审计和代码管理的软件工具。
|
||||||
|
|
||||||
|
代码扫描工具 静态源代码检查工具,能对源代码进行全面的分析 Coverity Prevent C、C++、C#、JAVA http://www.coverity.com/index.html Windows、Mac OS X 、 Linux 开发测试领域的领导者
|
||||||
|
Rational Purify C、C++、Java http://www-01.ibm.com/software/awdtools/purify/ Windows 是一个面向VC, VB或者Java开发的测试Visual C/C++ 和Java 代码中与内存有关的错误,确保整个应用程序的质量和可靠性
|
||||||
|
pc-lint C、C++ http://www.gimpel.com/html/pcl.htm Windows GIMPEL SOFTWARE公司开发的C/C++软件代码静态分析工具
|
||||||
|
Cppcheck C、C++ http://cppcheck.sourceforge.net/ Windows、Mac OS X 一种C/C++代码缺陷静态检查工具
|
||||||
|
Klocwork Insight C、C++ 、Java http://www.klocwork.com/products/insight.asp Windows、 Linux 使用高度的解析手法(Truepath™)让到现在为止的调试和测试方法很难检测出来的错误在早期可以检出,使软件的信赖性提高并提高测试的效率
|
||||||
|
PolySpace Client/Server C、C++ http://www.mathworks.cn/ Windows、Mac OS X 可以识别 C 和 C++ 代码中的运行时错误、并发问题、安全漏洞和其他缺陷
|
||||||
|
cqual C、C++ http://www.cs.umd.edu/~jfoster/cqual/ Windows、Mac OS X 、 Linux 一种基于类型的分析工具,它为指定和检查C程序的属性提供了一种轻量级的实用机制
|
||||||
|
ITS4 C\C++ http://www.cigital.com/its4/ Windows 一个静态检测源代码的工具,它是基于词法分析的
|
||||||
|
flawfinder C、C++ http://www.dwheeler.com/flawfinder/ Windows、Mac OS X 、 Linux 应用在代码是否有漏洞,是否存在被攻击的可能。
|
||||||
|
Splint C http://www.splint.org/ Windows 应用在分析代码是否符合编程规范
|
||||||
|
MOPS C http://www.cs.berkeley.edu/~daw/mops/ Unix 是在C程序中查找安全漏洞并验证是否符合防御性编程规则的工具
|
||||||
|
BLAST C http://mtc.epfl.ch/software-tools/blast/ Windows、 Linux C程序的软件模型检查器
|
||||||
|
Frama-C C http://frama-c.cea.fr/ Windows、Mac OS X 、 Linux 一个用来分析 C 代码的工具,它收集了很多静态统计技术
|
||||||
|
FindBugs Java http://findbugs.sourceforge.net/ Windows、Mac OS X 、 Linux 一个静态分析工具,它检查类或者 JAR 文件
|
||||||
|
Checkstyle Java http://checkstyle.sourceforge.net/ Windows、Mac OS X 、 Linux SourceForge下的一个项目,提供了一个帮助JAVA开发人员遵守某些编码规范的工具
|
||||||
|
Eclipse-Jtest Java http://www.parasoft.com/jsp/cn/support.jsp Windows、Mac OS X 、 Linux 一款优秀的 Java 代码优化和测试工具
|
||||||
|
PMD Java http://pmd.sourceforge.net/ Windows、Mac OS X 、 Linux 一种开源分析Java代码错误的工具
|
||||||
|
QJ-Pro Java http://qjpro.sourceforge.net/ Windows、Mac OS X 、 Linux 一个全面的软件检测工具
|
||||||
|
Jint Java http://artho.com/jlint/ Windows 通过进行数据流分析和构建锁图来检查您的Java代码并发现错误
|
||||||
|
Hammurapi Java http://www.hammurapi.biz/hammurapi-biz/ef/xmenu/hammurapi-group/index.html Windows、Mac OS X 、 Linux 一个开源的代码审查/评审(review)工具
|
||||||
|
Dependency Finder Java http://depfind.sourceforge.net/ Windows、Mac OS X 、 Linux 一个java依赖分析工具
|
||||||
|
Classycle Java http://classycle.sourceforge.net/ Windows 可以分析静态类和Java应用程序或库的包依赖性,生成XML报表
|
||||||
|
JDepend Java http://www.clarkware.com/software/JDepend.html Windows、Mac OS X 、 Linux 一个开放源代码的可以用来评价Java程序质量的优秀工具
|
||||||
|
LAPSE Java http://www.owasp.org/index.php/Category:OWASP_LAPSE_Project Windows、Mac OS X 、 Linux 向开发人员和审计人员提供一种用于检测Java EE应用程序中的漏洞的工具
|
||||||
|
JSLint JavaScript http://www.jslint.com/ Windows、Mac OS X 、 Linux 基于Web的验证JavaScript错误代码的工具
|
||||||
|
J3Unit JavaScript http://j3unit.sourceforge.net/ Windows、Mac OS X 、 Linux 一个面向对象的JavaScript单元测试框架
|
||||||
|
JSHint JavaScript http://jshint.com/ Windows、Mac OS X 、 Linux JSLint的一个更加灵活,可配置的一个版本
|
||||||
|
JSCS JavaScript http://jscs.info/ Windows、Mac OS X 、 Linux 一个代码风格检查器
|
||||||
|
ESLint JavaScript http://eslint.org/ Windows、Mac OS X 、 Linux 易于拓展的,具有大量的自定义规则,并且很容易通过插件的形式来安装
|
||||||
|
Regular Expression Tool JavaScript http://erik.eae.net/playground/regexp/regexp.html Windows、Mac OS X 、 Linux 一款在线工具,用来测试您的正则表达式代码是否正确
|
||||||
|
JSLitmus JavaScript http://broofa.com/Tools/JSLitmus/ Windows、Mac OS X 、 Linux 是款轻量级的工具,用来测试JavaScript执行性能情况,采用直观的API
|
||||||
|
JavaScript Regular Expression Tester JavaScript http://broofa.com/Tools/JSLitmus/ Windows、Mac OS X 、 Linux 是在浏览器中使用JavaScript来测试JavaScript正则表达式的
|
||||||
|
Exakat PHP https://www.exakat.io/ Mac OS X 提供从PHP 5.2到PHP 7.2-dev的实时PHP静态分析器
|
||||||
|
PHPSecurityScanner PHP https://sourceforge.net/projects/securityscanner/ Linux 扫描PHP代码中是否有存在漏洞
|
||||||
|
PHPMD PHP https://phpmd.org/ Windows、Mac OS X 、 Linux 探测PHP源代码中一些潜在的问题
|
||||||
|
PHPStan PHP https://packagist.org/packages/phpstan/phpstan Windows、Mac OS X 、 Linux 一款 PHP 静态分析工具,它专注于在代码中发现错误而不实际运行它
|
||||||
|
PhpSecInfo PHP http://phpsec.org/projects/phpsecinfo/ Windows、Mac OS X 、 Linux 相当于phpinfo()函数,它报告有关PHP环境的安全信息
|
||||||
|
StyleCop C# http://stylecop.codeplex.com/releases/view/79972 Windows 微软的一个开源的静态代码分析工具,检查c#代码一致性和编码风格
|
||||||
|
FxCop C# https://msdn.microsoft.com/library/bb429476 Windows 微软的代码分析工具,以微软内部使用的.NET编码规范为参照,找出源代码中潜在的设计和编写缺陷
|
||||||
|
PyChecker Python http://pychecker.sourceforge.net/ Linux Python代码的静态分析工具
|
||||||
|
Pylint Python https://www.pylint.org/ Windows、Mac OS X 、 Linux 一个高阶的Python代码分析工具
|
||||||
|
Bandit Python https://wiki.openstack.org/wiki/Security/Projects/Bandit Windows、Mac OS X 、 Linux 一款Python源码分析框架,可用于Python代码的安全性分析
|
||||||
|
|
||||||
|
反编译工具 高级语言源程序经过编译变成可执行文件,反编译就是逆过程。jd-gui Java http://jd.benow.ca/ Windows、Mac OS X 、 Linux 一个用 C++ 开发的 Java 反编译工具
|
||||||
|
Eclips-Jadclipse Java http://sourceforge.net/projects/jadclipse/ Windows、Mac OS X 、 Linux Jad的Eclipse插件,是一款非常实用而且方便地Java反编译插件
|
||||||
|
jad Java http://varaneckas.com/jad/ Windows、Mac OS X 、 Linux 一款使用非常广泛地Java反编译工具
|
||||||
|
jdec Java http://jdec.sourceforge.net/ Windows、Mac OS X 、 Linux 一个Java反编译器
|
||||||
|
uuDeJava Java http://www.uuware.com/uudejava_cn.htm Windows、Mac OS X 、 Linux Java Class文件的反编译工具
|
||||||
|
Minjava Java https://code.google.com/p/minjava/ Windows、Mac OS X 、 Linux 一个 Java 反向工程软件
|
||||||
|
Java Decompiler Java http://jd.benow.ca/ Windows、Mac OS X 、 Linux 一个Java反编译器
|
||||||
|
Reflector C# http://www.red-gate.com/products/dotnet-development/reflector/ Windows、Mac OS X 可将·NET程序集中的中间语言反编译成C#或者Visual Basic代码
|
||||||
|
ILSpy C# http://ilspy.NET/ Windows、Mac OS X 一个开源的.net反编译软件,使用十分方便
|
||||||
|
dnSpy C# https://github.com/0xd4d/dnSpy/blame/master/dnSpy.sln Windows、Mac OS X .net反编译工具
|
||||||
|
JetBrains dotPeek C# http://www.jetbrains.com/decompiler/ Windows .NET反编译工具
|
||||||
|
Telerik JustDecompile C# http://www.telerik.com/products/decompiler.aspx Windows 一款非常实用的.net反编译工具
|
||||||
|
Retargetable Decompiler C、pyhton https://retdec.com/ Windows 一个可重定位的反编译器
|
||||||
|
Easy Python Decompiler pyhton https://sourceforge.net/projects/easypythondecompiler/files/?source=navbar Windows python字节码反编译器,反编译pyc&pyo文件
|
||||||
|
uncompyle2 pyhton2.7 https://github.com/Mysterie/uncompyle2 Windows Python 2.7的反编译工具
|
||||||
|
exescope C++ http://www.softpedia.com/get/Programming/File-Editors/eXeScope.shtml Windows 能在没有资源文件的情况下分析,显示不同的信息,重写可执行文件的资源
|
||||||
|
|
||||||
|
C、C++通过编译把文本形式源代码翻译为机器语言形式的目标文件的,再通过链接把目标文件、操作系统的启动代码和用到的库文件进行组织,形成最终生成可执行代码。OllyDBG C、C++ http://www.ollydbg.de/ Windows 一个新的动态追踪工具
|
||||||
|
c32asm C、C++ http://www.c32asm.com/ Windows 具有反汇编模式和十六进制编辑模式,能跟踪exe文件的断点,也可直接修改软件内部代码。
|
||||||
|
W32Dasm C、C++ http://www.softpedia.com/get/Programming/Debuggers-Decompilers-Dissasemblers/WDASM.shtml Windows 一个静态反汇编工具,也是破解人常用的工具之一
|
||||||
|
Hex Rays Ida C、C++ https://www.hex-rays.com/ Windows、Mac OS X 、 Linux 一款调试工具的模拟器,可以更好的反汇编和更有深层分析
|
||||||
Loading…
Reference in New Issue