Files
ARTEX/db/findings_test.go
T
Autumn-27 ff3d41ea35 feat: 漏洞新增 severity=critical(严重) 与 name(漏洞名称)字段
severity 严重级:
- report_finding 工具 severity 说明改为 critical|high|medium|low
- DB 排序 CASE、report 报告 sevRank、FindingStats 增加 critical
- 前端 Severity 类型、status.ts 新增 rose 色调(实心高强调,视觉高于高危)、
  发现页严重度筛选/统计卡、任务发现 Tab 排序、dashboard 汇总均加入严重

name 漏洞名称:
- findings 表加 name 列(schema + ALTER TABLE ADD COLUMN IF NOT EXISTS 补旧库)
- report_finding 工具新增 name 入参(非必填),写入 node payload 与独立 findings 行
- DBFinding/AddFinding/AddStandaloneFinding、FindingDTO、findingPayload、report 全链路透传
- 发现列表页与任务发现 Tab 展示 name,为空时回退 vulnclass;vulnclass 移到展开详情「类型」
- report 报告标题优先 name,回退 vulnclass

无 CHECK 约束,旧库仅需补 name 列;critical/name 均向后兼容。
db 测试补充 critical 排序/统计与 name 往返断言。
2026-08-18 11:21:08 -04:00

104 lines
3.1 KiB
Go

package db
import (
"slices"
"testing"
)
// TestFindingsPageAndStats exercises ListFindingsPage (filter/sort/paging) and
// FindingStats against the live dev PG. It tags its rows with a unique vulnclass
// so assertions are isolated from any pre-existing data, and cleans up after.
func TestFindingsPageAndStats(t *testing.T) {
d, err := Open(testDSN(t))
if err != nil {
t.Skipf("postgres unavailable (%v) — skipping", err)
}
defer d.Close()
const vc = "__test_vc_pagination__"
// clean any leftovers from a prior aborted run, and clean up on exit
cleanup := func() { _, _ = d.Exec(`DELETE FROM findings WHERE vulnclass=$1`, vc) }
cleanup()
defer cleanup()
// Seed 6 findings under the marker vulnclass: 1 critical, 3 high, 2 low; 2 pending.
// The critical row carries a name to verify round-trip.
seed := []struct {
sev, status, name string
}{
{"critical", "pending", "严重漏洞标题"},
{"high", "resolved", ""},
{"high", "resolved", ""},
{"high", "pending", ""},
{"low", "resolved", ""},
{"low", "resolved", ""},
}
var ids []int64
for i, s := range seed {
id, err := d.AddFinding(0, 0, vc, s.name, s.sev, "summary", "poc", "tester", nil)
if err != nil {
t.Fatalf("AddFinding[%d]: %v", i, err)
}
if _, err := d.SetFindingStatus(id, s.status); err != nil {
t.Fatalf("SetFindingStatus[%d]: %v", i, err)
}
ids = append(ids, id)
}
// Filter by our vulnclass → exactly the 6 seeded rows, paged 2 per page.
p1, total, err := d.ListFindingsPage(FindingFilter{VulnClass: vc, Sort: "severity"}, 1, 2)
if err != nil {
t.Fatal(err)
}
if total != 6 {
t.Fatalf("total: want 6, got %d", total)
}
if len(p1) != 2 {
t.Fatalf("page1 size: want 2, got %d", len(p1))
}
// severity sort → critical first (with its name round-tripped), then high.
if p1[0].Severity != "critical" {
t.Fatalf("severity sort: want critical first, got %q", p1[0].Severity)
}
if p1[0].Name != "严重漏洞标题" {
t.Fatalf("name round-trip: want 严重漏洞标题, got %q", p1[0].Name)
}
if p1[1].Severity != "high" {
t.Fatalf("severity sort: want high second, got %q", p1[1].Severity)
}
// Combined filter: vulnclass + status=pending → 2 rows.
pend, total, err := d.ListFindingsPage(FindingFilter{VulnClass: vc, Status: FindingPending}, 1, 50)
if err != nil {
t.Fatal(err)
}
if total != 2 || len(pend) != 2 {
t.Fatalf("pending filter: want 2/2, got %d/%d", total, len(pend))
}
// Combined filter: vulnclass + severity=high → 3 rows.
_, total, err = d.ListFindingsPage(FindingFilter{VulnClass: vc, Severity: "high"}, 1, 50)
if err != nil {
t.Fatal(err)
}
if total != 3 {
t.Fatalf("high filter: want 3, got %d", total)
}
// Stats: whole-table, so assert our contribution is reflected (>=) and the
// marker vulnclass is present.
st, err := d.FindingStats()
if err != nil {
t.Fatal(err)
}
if st.Critical < 1 {
t.Fatalf("stats critical undercount: %+v", st)
}
if st.Total < 6 || st.High < 3 || st.Low < 2 || st.Pending < 2 {
t.Fatalf("stats undercount: %+v", st)
}
if !slices.Contains(st.VulnClasses, vc) {
t.Fatalf("stats vulnclasses missing %q", vc)
}
}