mirror of
https://github.com/RuoJi6/ARTEX.git
synced 2026-11-09 04:23:49 +08:00
feat(findings): consolidate duplicate reports into expandable cases
This commit is contained in:
1 parent
a91b27feb7
commit
24d4726aee
29 files changed
+3051
-63
No files matched your search
@@ -131,6 +131,8 @@ type provEntry struct {
|
||||
// taskID + mergeable let the drainer coalesce several event triggers (finding/goal)
|
||||
// from the SAME task into one conversation before it starts (interval fires don't merge).
|
||||
type triggeredRun struct {
|
||||
conversationID int64 // pre-created manual finding review; never coalesced
|
||||
|
||||
agentKey string
|
||||
title string
|
||||
message string // 事件正文(触发语 + 工具/入参/返回等);不含任务描述/目标头
|
||||
@@ -755,6 +757,16 @@ func (s *Server) Handler() http.Handler {
|
||||
mux.HandleFunc("GET /api/exploration/frontier", s.frontier)
|
||||
mux.HandleFunc("GET /api/exploration/findings", s.findings)
|
||||
mux.HandleFunc("GET /api/exploration/findings/groups", s.findingGroups)
|
||||
mux.HandleFunc("GET /api/exploration/finding-cases", s.listFindingCases)
|
||||
mux.HandleFunc("POST /api/exploration/finding-cases", s.mergeFindingCase)
|
||||
mux.HandleFunc("GET /api/exploration/finding-cases/{id}", s.getFindingCase)
|
||||
mux.HandleFunc("GET /api/exploration/finding-cases/{id}/members", s.getFindingCaseMembers)
|
||||
mux.HandleFunc("DELETE /api/exploration/finding-cases/{id}/members/{fid}", s.removeFindingCaseMember)
|
||||
mux.HandleFunc("PUT /api/exploration/finding-cases/{id}/report", s.updateCaseReport)
|
||||
mux.HandleFunc("GET /api/exploration/finding-case-suggestions", s.caseSuggestions)
|
||||
mux.HandleFunc("POST /api/exploration/finding-case-suggestions/{id}", s.resolveCaseSuggestion)
|
||||
mux.HandleFunc("POST /api/exploration/finding-case-review", s.reviewFindingCases)
|
||||
mux.HandleFunc("GET /api/exploration/finding-case-review", s.caseReviewRuns)
|
||||
mux.HandleFunc("GET /api/exploration/findings/asset-tree", s.findingAssetTree)
|
||||
mux.HandleFunc("GET /api/exploration/findings/stats", s.findingStats)
|
||||
mux.HandleFunc("GET /api/exploration/findings/export", s.findingsExport)
|
||||
@@ -1040,6 +1052,7 @@ func (s *Server) listTasks(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
list := s.m.List()
|
||||
metrics, _ := s.m.PG().TaskListMetricsAll()
|
||||
distinct, _ := s.m.pg.DistinctFindingStatsByTask()
|
||||
archiveBlockers, _ := s.m.PG().TaskArchiveBlockers()
|
||||
dtos := make([]TaskDTO, 0, len(list))
|
||||
for _, t := range list {
|
||||
@@ -1056,6 +1069,7 @@ func (s *Server) listTasks(w http.ResponseWriter, r *http.Request) {
|
||||
dto.GoalsTotal = metric.Goals.Total
|
||||
dto.GoalsMet = metric.Goals.Met
|
||||
dto.InFlight = metric.RunningIntents
|
||||
dto.DistinctFindings = distinct[t.ID]
|
||||
dto.Findings = FindingSeverityDTO{
|
||||
Critical: metric.Findings.Critical,
|
||||
High: metric.Findings.High,
|
||||
@@ -2052,6 +2066,11 @@ func (s *Server) findingStats(w http.ResponseWriter, r *http.Request) {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
st.Distinct, err = s.m.pg.DistinctFindingStats("")
|
||||
if err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, st)
|
||||
}
|
||||
|
||||
@@ -2074,6 +2093,14 @@ func (s *Server) getFinding(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
assets := s.resolveAssetIDs(f.AssetIDs)
|
||||
dto := findingFromDB(f, assets)
|
||||
cid, err := s.m.pg.FindingCaseID(f.ID)
|
||||
if err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
if cid > 0 {
|
||||
dto.CaseID = i64s(cid)
|
||||
}
|
||||
if contextTaskID := strings.TrimSpace(r.URL.Query().Get("context_task")); contextTaskID != "" {
|
||||
contextTask := s.m.ResolveTask(contextTaskID)
|
||||
if contextTask == nil {
|
||||
@@ -2138,6 +2165,14 @@ func (s *Server) findingsExport(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var casePlan *findingCaseExportPlan
|
||||
if q.Get("mode") == "consolidated" {
|
||||
fs, casePlan, err = s.prepareFindingCaseExport(fs)
|
||||
if err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
stage, err := os.MkdirTemp("", "artex-finding-export-")
|
||||
if err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
@@ -2148,6 +2183,13 @@ func (s *Server) findingsExport(w http.ResponseWriter, r *http.Request) {
|
||||
evidenceError(w, err)
|
||||
return
|
||||
}
|
||||
if casePlan != nil {
|
||||
fs, err = s.consolidateFindingCaseExport(fs, casePlan, q.Get("include_originals") == "true")
|
||||
if err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
now := time.Now()
|
||||
stamp := now.Format("20060102-150405")
|
||||
setDownload := func(contentType, filename string) {
|
||||
|
||||
Reference in new issue
Block a user