feat: 新增发现详情页(多 Tab + 状态区可改严重度/处置状态)
详情页 /function/findings/detail?id=<finding_id>:
- 概览 Tab:左栏摘要 + 证据/PoC,右栏状态区(严重等级、漏洞类型、涉及资产、处理状态、所属任务、发现时间)
- 严重等级与处理状态用下拉,选择后直接调后端接口即时修正
- 另有「证据/PoC」「元数据」两个 Tab
- 发现列表页漏洞名称改为链接进入详情
后端:
- GET /api/exploration/findings/{id} 取单条(含资产解析)
- PATCH /api/exploration/findings/{id} 扩展为部分更新 {status?, severity?}
- db.GetFinding / db.SetFindingSeverity(同步探索节点 payload,保持任务内视图一致)
- db.ValidSeverity 白名单;api 增加 getFinding / setFindingSeverity
- mock 支持单条 GET / PATCH,并回填 finding_id 供 demo 链接
db 测试补充 GetFinding / SetFindingSeverity 往返断言;next build 通过。
This commit is contained in:
1 parent
c69534e09b
commit
64d9c66f6a
7 files changed
+552
-18
No files matched your search
+5
-2
@@ -6,7 +6,7 @@
|
||||
import { MOCK } from "@/lib/mock/enabled";
|
||||
import { mockHandle } from "@/lib/mock/handler";
|
||||
import type {
|
||||
Task, Stats, Asset, AssetNode, Edge, Company, TaskNode, Finding, FindingStatus,
|
||||
Task, Stats, Asset, AssetNode, Edge, Company, TaskNode, Finding, Severity, FindingStatus,
|
||||
FindingsPage, FindingStats, FindingQuery, Activity,
|
||||
Audit, TrafficResp, TrafficDetail, TrafficHost, Settings, LLMProfile, Agent, AgentDetail, PromptVar,
|
||||
PromptVersion, MCPServer, MCPTool, SkillItem, TokenUsage, TokenTotal, DailyTokenBucket,
|
||||
@@ -245,8 +245,11 @@ export const api = {
|
||||
return get<FindingsPage>(`/exploration/findings?${p.toString()}`);
|
||||
},
|
||||
findingStats: () => get<FindingStats>("/exploration/findings/stats"),
|
||||
getFinding: (id: string) => get<Finding>(`/exploration/findings/${id}`),
|
||||
setFindingStatus: (id: string, status: FindingStatus) =>
|
||||
patch<{ id: string; status: FindingStatus }>(`/exploration/findings/${id}`, { status }),
|
||||
patch<Finding>(`/exploration/findings/${id}`, { status }),
|
||||
setFindingSeverity: (id: string, severity: Severity) =>
|
||||
patch<Finding>(`/exploration/findings/${id}`, { severity }),
|
||||
intents: (task?: string) => get<TaskNode[]>(`/exploration/intents${tq(task)}`).then(arr),
|
||||
tokenStats: (task?: string) =>
|
||||
get<{ workers: TokenUsage[]; total: TokenTotal }>(`/exploration/tokens${tq(task)}`).then((r) => ({
|
||||
|
||||
@@ -85,10 +85,28 @@ function route(m: string, path: string, seg: string[], q: URLSearchParams, b: Re
|
||||
vulnclasses,
|
||||
};
|
||||
}
|
||||
// 单条 finding:GET 详情 / PATCH 改状态或严重度(demo 直接改内存对象)。
|
||||
if (
|
||||
seg[0] === "exploration" &&
|
||||
seg[1] === "findings" &&
|
||||
seg.length === 3 &&
|
||||
seg[2] !== "stats"
|
||||
) {
|
||||
const f = D.findings.find((x) => x.id === seg[2]);
|
||||
if (!f) return {};
|
||||
if (m === "PATCH") {
|
||||
if (typeof b.status === "string") f.status = b.status as typeof f.status;
|
||||
if (typeof b.severity === "string")
|
||||
f.severity = b.severity as typeof f.severity;
|
||||
}
|
||||
return { ...f, finding_id: f.id };
|
||||
}
|
||||
if (path === "/exploration/findings") {
|
||||
if (task) return D.findings.filter((f) => f.task_id === task);
|
||||
// finding_id=id:真后端用独立表行 id 作为状态/详情句柄,mock 里用自身 id 顶上。
|
||||
const withFid = (f: (typeof D.findings)[number]) => ({ ...f, finding_id: f.id });
|
||||
if (task) return D.findings.filter((f) => f.task_id === task).map(withFid);
|
||||
// 全局:带 page/limit → 分页对象;否则裸数组(dashboard)。
|
||||
if (!q.has("page") && !q.has("limit")) return D.findings;
|
||||
if (!q.has("page") && !q.has("limit")) return D.findings.map(withFid);
|
||||
const sev = { critical: 4, high: 3, medium: 2, low: 1 } as const;
|
||||
let list = D.findings.slice();
|
||||
const fSev = q.get("severity");
|
||||
@@ -105,7 +123,7 @@ function route(m: string, path: string, seg: string[], q: URLSearchParams, b: Re
|
||||
const page = Number(q.get("page") ?? 1);
|
||||
const pageSize = Number(q.get("limit") ?? 20);
|
||||
return {
|
||||
items: list.slice((page - 1) * pageSize, page * pageSize),
|
||||
items: list.slice((page - 1) * pageSize, page * pageSize).map(withFid),
|
||||
total: list.length,
|
||||
page,
|
||||
page_size: pageSize,
|
||||
|
||||
Reference in new issue
Block a user