mirror of
https://github.com/chaitin/MonkeyCode.git
synced 2026-02-11 19:23:56 +08:00
Merge pull request #276 from yokowu/feat-security-detail
feat: 提供给插件的安全详情接口
This commit is contained in:
1
.github/workflows/backend-ci-cd.yml
vendored
1
.github/workflows/backend-ci-cd.yml
vendored
@@ -65,6 +65,7 @@ jobs:
|
||||
build:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request'
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./backend
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@ type ProxyUsecase interface {
|
||||
Report(ctx context.Context, req *ReportReq) error
|
||||
CreateSecurityScanning(ctx context.Context, req *CreateSecurityScanningReq) (string, error)
|
||||
ListSecurityScanning(ctx context.Context, req *ListSecurityScanningReq) (*ListSecurityScanningBriefResp, error)
|
||||
ListSecurityDetail(ctx context.Context, req *ListSecurityScanningDetailReq) (*ListSecurityScanningDetailResp, error)
|
||||
}
|
||||
|
||||
type ProxyRepo interface {
|
||||
|
||||
@@ -3,7 +3,6 @@ package domain
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
"sort"
|
||||
|
||||
"github.com/GoYoko/web"
|
||||
"github.com/google/uuid"
|
||||
@@ -25,6 +24,7 @@ type SecurityScanningRepo interface {
|
||||
Create(ctx context.Context, req CreateSecurityScanningReq) (string, error)
|
||||
Update(ctx context.Context, id string, fileMap map[string]string, status consts.SecurityScanningStatus, result *scan.Result) error
|
||||
List(ctx context.Context, req ListSecurityScanningReq) (*ListSecurityScanningResp, error)
|
||||
ListDetail(ctx context.Context, req ListSecurityScanningDetailReq) (*ListSecurityScanningDetailResp, error)
|
||||
Detail(ctx context.Context, userID, id string) ([]*SecurityScanningRiskDetail, error)
|
||||
ListBrief(ctx context.Context, req ListSecurityScanningReq) (*ListSecurityScanningBriefResp, error)
|
||||
AllRunning(ctx context.Context) ([]*db.SecurityScanning, error)
|
||||
@@ -39,6 +39,12 @@ type ListSecurityScanningReq struct {
|
||||
ProjectName string `json:"project_name" query:"project_name"` // 项目名称
|
||||
}
|
||||
|
||||
type ListSecurityScanningDetailReq struct {
|
||||
web.Pagination
|
||||
ID string `json:"id" query:"id"` // 扫描任务id
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type ListSecurityScanningResp struct {
|
||||
*db.PageInfo
|
||||
|
||||
@@ -51,7 +57,14 @@ type ListSecurityScanningBriefResp struct {
|
||||
Items []*SecurityScanningBrief `json:"items"`
|
||||
}
|
||||
|
||||
type ListSecurityScanningDetailResp struct {
|
||||
*db.PageInfo
|
||||
|
||||
Items []*SecurityScanningRiskDetail `json:"items"`
|
||||
}
|
||||
|
||||
type SecurityScanningBrief struct {
|
||||
ID string `json:"id"` // 扫描任务id
|
||||
Workspace string `json:"workspace"` // 项目目录
|
||||
Status consts.SecurityScanningStatus `json:"status"` // 扫描状态
|
||||
ReportURL string `json:"report_url"` // 报告url
|
||||
@@ -63,6 +76,7 @@ func (s *SecurityScanningBrief) From(e *db.SecurityScanning) *SecurityScanningBr
|
||||
return s
|
||||
}
|
||||
|
||||
s.ID = e.ID.String()
|
||||
s.Status = e.Status
|
||||
s.Workspace = e.Workspace
|
||||
s.CreatedAt = e.CreatedAt.Unix()
|
||||
@@ -131,31 +145,6 @@ type SecurityScanningRiskDetail struct {
|
||||
Content string `json:"content"` // 代码内容
|
||||
}
|
||||
|
||||
func (s *SecurityScanningRiskDetail) GetRiskLevelPriority() int {
|
||||
switch s.Level {
|
||||
case consts.SecurityScanningRiskLevelSevere:
|
||||
return 1 // 严重 - 最高优先级
|
||||
case consts.SecurityScanningRiskLevelCritical:
|
||||
return 2 // 高危 - 中等优先级
|
||||
case consts.SecurityScanningRiskLevelSuggest:
|
||||
return 3 // 建议 - 最低优先级
|
||||
default:
|
||||
return 4 // 未知等级放在最后
|
||||
}
|
||||
}
|
||||
|
||||
type ByRiskLevel []*SecurityScanningRiskDetail
|
||||
|
||||
func (a ByRiskLevel) Len() int { return len(a) }
|
||||
func (a ByRiskLevel) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a ByRiskLevel) Less(i, j int) bool {
|
||||
return a[i].GetRiskLevelPriority() < a[j].GetRiskLevelPriority()
|
||||
}
|
||||
|
||||
func SortRiskDetailsByLevel(details []*SecurityScanningRiskDetail) {
|
||||
sort.Sort(ByRiskLevel(details))
|
||||
}
|
||||
|
||||
func (s *SecurityScanningRiskDetail) From(e *db.SecurityScanningResult) *SecurityScanningRiskDetail {
|
||||
if e == nil {
|
||||
return s
|
||||
|
||||
@@ -61,7 +61,8 @@ func NewV1Handler(
|
||||
g.POST("/completions", web.BaseHandler(h.Completions), active.Active("apikey"))
|
||||
g.POST("/embeddings", web.BaseHandler(h.Embeddings), active.Active("apikey"))
|
||||
g.POST("/security/scanning", web.BindHandler(h.CreateSecurityScanning), active.Active("apikey"))
|
||||
g.GET("/security/scanning", web.BindHandler(h.ListSecurityScanning), active.Active("apikey"))
|
||||
g.GET("/security/scanning", web.BindHandler(h.ListSecurityScanning, web.WithPage()), active.Active("apikey"))
|
||||
g.GET("/security/scanning/detail", web.BindHandler(h.ListSecurityScanningDetail, web.WithPage()), active.Active("apikey"))
|
||||
return h
|
||||
}
|
||||
|
||||
@@ -244,7 +245,7 @@ func (h *V1Handler) CreateSecurityScanning(c *web.Context, req domain.CreateSecu
|
||||
//
|
||||
// @Tags OpenAIV1
|
||||
// @Summary 扫描任务列表
|
||||
// @Description 分页逻辑只支持用 next_token
|
||||
// @Description 扫描任务列表
|
||||
// @ID list-security-scanning
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
@@ -253,6 +254,7 @@ func (h *V1Handler) CreateSecurityScanning(c *web.Context, req domain.CreateSecu
|
||||
// @Router /v1/security/scanning [get]
|
||||
func (h *V1Handler) ListSecurityScanning(c *web.Context, req domain.ListSecurityScanningReq) error {
|
||||
req.UserID = middleware.GetApiKey(c).UserID
|
||||
req.Pagination = *c.Page()
|
||||
s, err := h.uuse.GetSetting(c.Request().Context())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -264,3 +266,25 @@ func (h *V1Handler) ListSecurityScanning(c *web.Context, req domain.ListSecurity
|
||||
}
|
||||
return c.Success(resp)
|
||||
}
|
||||
|
||||
// ListSecurityScanningDetail 获取扫描任务风险详情列表
|
||||
//
|
||||
// @Tags OpenAIV1
|
||||
// @Summary 获取扫描任务风险详情列表
|
||||
// @Description 分页只支持 next_token; 首页传空,后续判断has_next_page是否为true,传入回包给的next_token
|
||||
// @ID list-security-scanning-detail
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id query domain.ListSecurityScanningDetailReq true "扫描任务ID"
|
||||
// @Success 200 {object} web.Resp{data=domain.ListSecurityScanningDetailResp}
|
||||
// @Router /v1/security/scanning/detail [get]
|
||||
func (h *V1Handler) ListSecurityScanningDetail(c *web.Context, req domain.ListSecurityScanningDetailReq) error {
|
||||
req.Pagination = *c.Page()
|
||||
user := middleware.GetApiKey(c)
|
||||
req.UserID = user.ID
|
||||
resp, err := h.proxyUse.ListSecurityDetail(c.Request().Context(), &req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Success(resp)
|
||||
}
|
||||
|
||||
@@ -189,3 +189,7 @@ func (p *ProxyUsecase) TaskHandle(ctx context.Context, task *queuerunner.Task[do
|
||||
func (p *ProxyUsecase) ListSecurityScanning(ctx context.Context, req *domain.ListSecurityScanningReq) (*domain.ListSecurityScanningBriefResp, error) {
|
||||
return p.securityRepo.ListBrief(ctx, *req)
|
||||
}
|
||||
|
||||
func (p *ProxyUsecase) ListSecurityDetail(ctx context.Context, req *domain.ListSecurityScanningDetailReq) (*domain.ListSecurityScanningDetailResp, error) {
|
||||
return p.securityRepo.ListDetail(ctx, *req)
|
||||
}
|
||||
|
||||
@@ -216,7 +216,11 @@ func (s *SecurityScanningRepo) Detail(ctx context.Context, userID, id string) ([
|
||||
}
|
||||
|
||||
q := s.db.SecurityScanningResult.Query().
|
||||
Where(securityscanningresult.SecurityScanningID(sid))
|
||||
Where(securityscanningresult.SecurityScanningID(sid)).
|
||||
Order(
|
||||
BySeverityOrder(),
|
||||
securityscanningresult.ByCreatedAt(sql.OrderDesc()),
|
||||
)
|
||||
|
||||
if userID != "" {
|
||||
uid, err := uuid.Parse(userID)
|
||||
@@ -236,7 +240,6 @@ func (s *SecurityScanningRepo) Detail(ctx context.Context, userID, id string) ([
|
||||
rs := cvt.Iter(scannings, func(_ int, r *db.SecurityScanningResult) *domain.SecurityScanningRiskDetail {
|
||||
return cvt.From(r, &domain.SecurityScanningRiskDetail{})
|
||||
})
|
||||
domain.SortRiskDetailsByLevel(rs)
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
@@ -308,3 +311,37 @@ func (s *SecurityScanningRepo) PageWorkspaceFiles(ctx context.Context, id string
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SecurityScanningRepo) ListDetail(ctx context.Context, req domain.ListSecurityScanningDetailReq) (*domain.ListSecurityScanningDetailResp, error) {
|
||||
sid, err := uuid.Parse(req.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q := s.db.SecurityScanningResult.Query().
|
||||
Where(securityscanningresult.SecurityScanningID(sid)).
|
||||
Order(
|
||||
BySeverityOrder(),
|
||||
securityscanningresult.ByCreatedAt(sql.OrderDesc()),
|
||||
securityscanningresult.ByID(sql.OrderDesc()),
|
||||
)
|
||||
|
||||
rs, p, err := q.Page(ctx, req.Page, req.Size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &domain.ListSecurityScanningDetailResp{
|
||||
PageInfo: p,
|
||||
Items: cvt.Iter(rs, func(_ int, r *db.SecurityScanningResult) *domain.SecurityScanningRiskDetail {
|
||||
return cvt.From(r, &domain.SecurityScanningRiskDetail{})
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func BySeverityOrder() func(s *sql.Selector) {
|
||||
return func(s *sql.Selector) {
|
||||
s.OrderExprFunc(func(b *sql.Builder) {
|
||||
b.WriteString("case when severity = 'CRITICAL' then 5 when severity = 'ERROR' then 4 when severity = 'WARNING' then 3 when severity = 'INFO' then 2 else 1 end desc")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user