Files
CyberStrikeAI/internal/handler/openapi.go
2026-01-27 20:56:20 +08:00

648 lines
22 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handler
import (
"net/http"
"time"
"cyberstrike-ai/internal/database"
"cyberstrike-ai/internal/storage"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// OpenAPIHandler OpenAPI处理器
type OpenAPIHandler struct {
db *database.DB
logger *zap.Logger
resultStorage storage.ResultStorage
conversationHdlr *ConversationHandler
agentHdlr *AgentHandler
}
// NewOpenAPIHandler 创建新的OpenAPI处理器
func NewOpenAPIHandler(db *database.DB, logger *zap.Logger, resultStorage storage.ResultStorage, conversationHdlr *ConversationHandler, agentHdlr *AgentHandler) *OpenAPIHandler {
return &OpenAPIHandler{
db: db,
logger: logger,
resultStorage: resultStorage,
conversationHdlr: conversationHdlr,
agentHdlr: agentHdlr,
}
}
// GetOpenAPISpec 获取OpenAPI规范
func (h *OpenAPIHandler) GetOpenAPISpec(c *gin.Context) {
host := c.Request.Host
scheme := "http"
if c.Request.TLS != nil {
scheme = "https"
}
spec := map[string]interface{}{
"openapi": "3.0.0",
"info": map[string]interface{}{
"title": "CyberStrikeAI API",
"description": "AI驱动的自动化安全测试平台API文档",
"version": "1.0.0",
"contact": map[string]interface{}{
"name": "CyberStrikeAI",
},
},
"servers": []map[string]interface{}{
{
"url": scheme + "://" + host,
"description": "当前服务器",
},
},
"components": map[string]interface{}{
"securitySchemes": map[string]interface{}{
"bearerAuth": map[string]interface{}{
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
"description": "使用Bearer Token进行认证。Token通过 /api/auth/login 接口获取。",
},
},
"schemas": map[string]interface{}{
"CreateConversationRequest": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"title": map[string]interface{}{
"type": "string",
"description": "对话标题",
"example": "Web应用安全测试",
},
},
},
"Conversation": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]interface{}{
"type": "string",
"description": "对话ID",
"example": "550e8400-e29b-41d4-a716-446655440000",
},
"title": map[string]interface{}{
"type": "string",
"description": "对话标题",
"example": "Web应用安全测试",
},
"createdAt": map[string]interface{}{
"type": "string",
"format": "date-time",
"description": "创建时间",
},
"updatedAt": map[string]interface{}{
"type": "string",
"format": "date-time",
"description": "更新时间",
},
},
},
"ConversationDetail": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]interface{}{
"type": "string",
"description": "对话ID",
},
"title": map[string]interface{}{
"type": "string",
"description": "对话标题",
},
"status": map[string]interface{}{
"type": "string",
"description": "对话状态active进行中、completed已完成、failed失败",
"enum": []string{"active", "completed", "failed"},
},
"createdAt": map[string]interface{}{
"type": "string",
"format": "date-time",
"description": "创建时间",
},
"updatedAt": map[string]interface{}{
"type": "string",
"format": "date-time",
"description": "更新时间",
},
"messages": map[string]interface{}{
"type": "array",
"description": "消息列表",
"items": map[string]interface{}{
"$ref": "#/components/schemas/Message",
},
},
"messageCount": map[string]interface{}{
"type": "integer",
"description": "消息数量",
},
},
},
"Message": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]interface{}{
"type": "string",
"description": "消息ID",
},
"conversationId": map[string]interface{}{
"type": "string",
"description": "对话ID",
},
"role": map[string]interface{}{
"type": "string",
"description": "消息角色user用户、assistant助手",
"enum": []string{"user", "assistant"},
},
"content": map[string]interface{}{
"type": "string",
"description": "消息内容",
},
"createdAt": map[string]interface{}{
"type": "string",
"format": "date-time",
"description": "创建时间",
},
},
},
"ConversationResults": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"conversationId": map[string]interface{}{
"type": "string",
"description": "对话ID",
},
"messages": map[string]interface{}{
"type": "array",
"description": "消息列表",
"items": map[string]interface{}{
"$ref": "#/components/schemas/Message",
},
},
"vulnerabilities": map[string]interface{}{
"type": "array",
"description": "发现的漏洞列表",
"items": map[string]interface{}{
"$ref": "#/components/schemas/Vulnerability",
},
},
"executionResults": map[string]interface{}{
"type": "array",
"description": "执行结果列表",
"items": map[string]interface{}{
"$ref": "#/components/schemas/ExecutionResult",
},
},
},
},
"Vulnerability": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]interface{}{
"type": "string",
"description": "漏洞ID",
},
"title": map[string]interface{}{
"type": "string",
"description": "漏洞标题",
},
"description": map[string]interface{}{
"type": "string",
"description": "漏洞描述",
},
"severity": map[string]interface{}{
"type": "string",
"description": "严重程度",
"enum": []string{"critical", "high", "medium", "low", "info"},
},
"status": map[string]interface{}{
"type": "string",
"description": "状态",
"enum": []string{"open", "closed", "fixed"},
},
"target": map[string]interface{}{
"type": "string",
"description": "受影响的目标",
},
},
},
"ExecutionResult": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]interface{}{
"type": "string",
"description": "执行ID",
},
"toolName": map[string]interface{}{
"type": "string",
"description": "工具名称",
},
"status": map[string]interface{}{
"type": "string",
"description": "执行状态",
"enum": []string{"success", "failed", "running"},
},
"result": map[string]interface{}{
"type": "string",
"description": "执行结果",
},
"createdAt": map[string]interface{}{
"type": "string",
"format": "date-time",
"description": "创建时间",
},
},
},
"Error": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"error": map[string]interface{}{
"type": "string",
"description": "错误信息",
},
},
},
},
},
"security": []map[string]interface{}{
{
"bearerAuth": []string{},
},
},
"paths": map[string]interface{}{
"/api/conversations": map[string]interface{}{
"post": map[string]interface{}{
"tags": []string{"对话管理"},
"summary": "创建对话",
"description": "创建一个新的安全测试对话。\n\n**重要说明**\n- ✅ 创建的对话会**立即保存到数据库**\n- ✅ 前端页面会**自动刷新**显示新对话\n- ✅ 与前端创建的对话**完全一致**\n\n**创建对话的两种方式**\n\n**方式1推荐** 直接使用 `/api/agent-loop` 发送消息,**不提供** `conversationId` 参数,系统会自动创建新对话并发送消息。这是最简单的方式,一步完成创建和发送。\n\n**方式2** 先调用此端点创建空对话,然后使用返回的 `conversationId` 调用 `/api/agent-loop` 发送消息。适用于需要先创建对话,稍后再发送消息的场景。\n\n**示例**\n```json\n{\n \"title\": \"Web应用安全测试\"\n}\n```",
"operationId": "createConversation",
"requestBody": map[string]interface{}{
"required": true,
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/CreateConversationRequest",
},
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "对话创建成功",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/Conversation",
},
},
},
},
"400": map[string]interface{}{
"description": "请求参数错误",
},
"401": map[string]interface{}{
"description": "未授权需要有效的Token",
},
"500": map[string]interface{}{
"description": "服务器内部错误",
},
},
},
"get": map[string]interface{}{
"tags": []string{"对话管理"},
"summary": "列出对话",
"description": "获取对话列表,支持分页和搜索",
"operationId": "listConversations",
"parameters": []map[string]interface{}{
{
"name": "limit",
"in": "query",
"required": false,
"description": "返回数量限制",
"schema": map[string]interface{}{
"type": "integer",
"default": 50,
"minimum": 1,
"maximum": 100,
},
},
{
"name": "offset",
"in": "query",
"required": false,
"description": "偏移量",
"schema": map[string]interface{}{
"type": "integer",
"default": 0,
"minimum": 0,
},
},
{
"name": "search",
"in": "query",
"required": false,
"description": "搜索关键词",
"schema": map[string]interface{}{
"type": "string",
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "获取成功",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"$ref": "#/components/schemas/Conversation",
},
},
},
},
},
"401": map[string]interface{}{
"description": "未授权需要有效的Token",
},
},
},
},
"/api/conversations/{id}": map[string]interface{}{
"get": map[string]interface{}{
"tags": []string{"对话管理"},
"summary": "查看对话详情",
"description": "获取指定对话的详细信息,包括对话信息和消息列表",
"operationId": "getConversation",
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"description": "对话ID",
"schema": map[string]interface{}{
"type": "string",
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "获取成功",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/ConversationDetail",
},
},
},
},
"404": map[string]interface{}{
"description": "对话不存在",
},
"401": map[string]interface{}{
"description": "未授权需要有效的Token",
},
},
},
"delete": map[string]interface{}{
"tags": []string{"对话管理"},
"summary": "删除对话",
"description": "删除指定的对话及其所有相关数据(消息、漏洞等)。**此操作不可恢复**。",
"operationId": "deleteConversation",
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"description": "对话ID",
"schema": map[string]interface{}{
"type": "string",
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "删除成功",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"message": map[string]interface{}{
"type": "string",
"description": "成功消息",
"example": "删除成功",
},
},
},
},
},
},
"404": map[string]interface{}{
"description": "对话不存在",
},
"401": map[string]interface{}{
"description": "未授权需要有效的Token",
},
"500": map[string]interface{}{
"description": "服务器内部错误",
},
},
},
},
"/api/conversations/{id}/results": map[string]interface{}{
"get": map[string]interface{}{
"tags": []string{"结果查询"},
"summary": "获取对话结果",
"description": "获取指定对话的执行结果,包括消息、漏洞信息和执行结果",
"operationId": "getConversationResults",
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"description": "对话ID",
"schema": map[string]interface{}{
"type": "string",
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "获取成功",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/ConversationResults",
},
},
},
},
"404": map[string]interface{}{
"description": "对话不存在或结果不存在",
},
"401": map[string]interface{}{
"description": "未授权需要有效的Token",
},
},
},
},
"/api/agent-loop": map[string]interface{}{
"post": map[string]interface{}{
"tags": []string{"对话交互"},
"summary": "发送消息并获取AI回复核心端点",
"description": "向AI发送消息并获取回复。**这是与AI交互的核心端点**,与前端聊天功能完全一致。\n\n**重要说明**\n- ✅ 通过此API创建/发送的消息会**立即保存到数据库**\n- ✅ 前端页面会**自动刷新**显示新创建的对话和消息\n- ✅ 所有操作都有**完整的交互痕迹**,就像在前端操作一样\n- ✅ 支持角色配置,可以指定使用哪个测试角色\n\n**推荐使用流程**\n\n1. **先创建对话**:调用 `POST /api/conversations` 创建新对话,获取 `conversationId`\n2. **再发送消息**:使用返回的 `conversationId` 调用此端点发送消息\n\n**使用示例**\n\n**步骤1 - 创建对话:**\n```json\nPOST /api/conversations\n{\n \"title\": \"Web应用安全测试\"\n}\n```\n\n**步骤2 - 发送消息:**\n```json\nPOST /api/agent-loop\n{\n \"conversationId\": \"返回的对话ID\",\n \"message\": \"扫描 http://example.com 的SQL注入漏洞\",\n \"role\": \"渗透测试\"\n}\n```\n\n**其他方式**\n\n如果不提供 `conversationId`,系统会自动创建新对话并发送消息。但**推荐先创建对话**,这样可以更好地管理对话列表。\n\n**响应**返回AI的回复、对话ID和MCP执行ID列表。前端会自动刷新显示新消息。",
"operationId": "sendMessage",
"requestBody": map[string]interface{}{
"required": true,
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"message": map[string]interface{}{
"type": "string",
"description": "要发送的消息(必需)",
"example": "扫描 http://example.com 的SQL注入漏洞",
},
"conversationId": map[string]interface{}{
"type": "string",
"description": "对话ID可选。\n- **不提供**:自动创建新对话并发送消息(推荐)\n- **提供**:消息会添加到指定对话中(对话必须存在)",
"example": "550e8400-e29b-41d4-a716-446655440000",
},
"role": map[string]interface{}{
"type": "string",
"description": "角色名称可选默认、渗透测试、Web应用扫描等",
"example": "默认",
},
},
"required": []string{"message"},
},
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "消息发送成功返回AI回复",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"response": map[string]interface{}{
"type": "string",
"description": "AI的回复内容",
},
"conversationId": map[string]interface{}{
"type": "string",
"description": "对话ID",
},
"mcpExecutionIds": map[string]interface{}{
"type": "array",
"description": "MCP执行ID列表",
"items": map[string]interface{}{
"type": "string",
},
},
"time": map[string]interface{}{
"type": "string",
"format": "date-time",
"description": "响应时间",
},
},
},
},
},
},
"400": map[string]interface{}{
"description": "请求参数错误",
},
"401": map[string]interface{}{
"description": "未授权需要有效的Token",
},
"500": map[string]interface{}{
"description": "服务器内部错误",
},
},
},
},
},
}
c.JSON(http.StatusOK, spec)
}
// GetConversationResults 获取对话结果OpenAPI端点
// 注意:创建对话和获取对话详情直接使用标准的 /api/conversations 端点
// 这个端点只是为了提供结果聚合功能
func (h *OpenAPIHandler) GetConversationResults(c *gin.Context) {
conversationID := c.Param("id")
// 验证对话是否存在
conv, err := h.db.GetConversation(conversationID)
if err != nil {
h.logger.Error("获取对话失败", zap.Error(err))
c.JSON(http.StatusNotFound, gin.H{"error": "对话不存在"})
return
}
// 获取消息列表
messages, err := h.db.GetMessages(conversationID)
if err != nil {
h.logger.Error("获取消息失败", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// 获取漏洞列表
vulnList, err := h.db.ListVulnerabilities(1000, 0, "", conversationID, "", "")
if err != nil {
h.logger.Warn("获取漏洞列表失败", zap.Error(err))
vulnList = []*database.Vulnerability{}
}
vulnerabilities := make([]database.Vulnerability, len(vulnList))
for i, v := range vulnList {
vulnerabilities[i] = *v
}
// 获取执行结果从MCP执行记录中获取
executionResults := []map[string]interface{}{}
for _, msg := range messages {
if len(msg.MCPExecutionIDs) > 0 {
for _, execID := range msg.MCPExecutionIDs {
// 尝试从结果存储中获取执行结果
if h.resultStorage != nil {
result, err := h.resultStorage.GetResult(execID)
if err == nil && result != "" {
// 获取元数据以获取工具名称和创建时间
metadata, err := h.resultStorage.GetResultMetadata(execID)
toolName := "unknown"
createdAt := time.Now()
if err == nil && metadata != nil {
toolName = metadata.ToolName
createdAt = metadata.CreatedAt
}
executionResults = append(executionResults, map[string]interface{}{
"id": execID,
"toolName": toolName,
"status": "success",
"result": result,
"createdAt": createdAt.Format(time.RFC3339),
})
}
}
}
}
}
response := map[string]interface{}{
"conversationId": conv.ID,
"messages": messages,
"vulnerabilities": vulnerabilities,
"executionResults": executionResults,
}
c.JSON(http.StatusOK, response)
}