feat(tasks): 删除任务可选连带删除资产/漏洞/流量/文件(默认不选)
删除弹窗新增四个默认不勾的复选框:
- 删除关联的资产:仅删本任务独有资产,与其他任务共享的仅解除关联(保留)。
- 删除发现的漏洞:删本任务全部 findings。
- 删除测试的流量:按本任务资产的 host 清录制流量(host 无任务标签,会连带清
其他任务命中同一 host 的流量)。
- 删除测试过程中写的文件:删 <workDir>/tasks/<id> 目录(worker 产物 + 上传)。
- db: AssetStore.DeleteByTaskID(独有删+共享解关联)/HostsByTask;DeleteFindingsByTask。
- server: pgDeleteTask 读可选 JSON body {assets,findings,traffic,files},先按 host
清流量(需在删资产前取 host)→删漏洞→删资产→删任务→删文件目录;空 body=只删任务。
- web: api.deleteTask(id,opts);删除弹窗四复选框(默认不选)+说明。
go build/vet、tsc 通过;新增 TestDeleteByTaskID(独有删/共享解关联/host 反查)。
This commit is contained in:
1 parent
2aeb2e49da
commit
4f7c1a199d
6 files changed
+215
-14
No files matched your search
+48
-1
@@ -66,14 +66,61 @@ func decode(r *http.Request, v any) error { return json.NewDecoder(r.Body).Decod
|
||||
|
||||
// ---------- tasks (delete) ----------
|
||||
|
||||
// pgDeleteTask removes a task and, per opt-in flags in the (optional) JSON body,
|
||||
// its associated data: assets (task-owned), findings, recorded traffic (by the
|
||||
// task's hosts) and the per-task working directory. Empty body → all flags false →
|
||||
// only the task + its exploration subgraph are removed (legacy behavior).
|
||||
func (s *Server) pgDeleteTask(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
var opts struct {
|
||||
Assets bool `json:"assets"`
|
||||
Findings bool `json:"findings"`
|
||||
Traffic bool `json:"traffic"`
|
||||
Files bool `json:"files"`
|
||||
}
|
||||
_ = json.NewDecoder(r.Body).Decode(&opts) // optional body; ignore EOF/parse errors
|
||||
|
||||
result := map[string]any{"deleted": id}
|
||||
nid, _ := strconv.ParseInt(id, 10, 64)
|
||||
as := s.m.Assets()
|
||||
|
||||
// Traffic first: it needs the task's hosts, which come from assets that a later
|
||||
// asset-delete would remove. Traffic is host-keyed (no per-task tag), so this
|
||||
// purges ALL recorded traffic for those hosts, not just this task's.
|
||||
if opts.Traffic && nid > 0 && as != nil {
|
||||
if tr := s.m.Traffic(); tr != nil {
|
||||
if hosts, err := as.HostsByTask(nid); err == nil && len(hosts) > 0 {
|
||||
if n, err := tr.DeleteHostsExact(hosts); err == nil {
|
||||
result["traffic_deleted"] = n
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if opts.Findings && nid > 0 {
|
||||
if n, err := s.m.pg.DeleteFindingsByTask(nid); err == nil {
|
||||
result["findings_deleted"] = n
|
||||
}
|
||||
}
|
||||
if opts.Assets && nid > 0 && as != nil {
|
||||
if n, err := as.DeleteByTaskID(nid); err == nil {
|
||||
result["assets_deleted"] = n
|
||||
}
|
||||
}
|
||||
|
||||
s.engine.Pause(id, agent.AbortTaskDeleted) // cancel running loops before removing
|
||||
if err := s.m.DeleteTask(id); err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"deleted": id})
|
||||
|
||||
// Files last: the per-task working dir <workDir>/tasks/<id> (worker artifacts +
|
||||
// uploads). Confined to the work dir; id is numeric so no traversal risk.
|
||||
if opts.Files && nid > 0 {
|
||||
if err := os.RemoveAll(filepath.Join(s.m.dir, "tasks", id)); err == nil {
|
||||
result["files_deleted"] = true
|
||||
}
|
||||
}
|
||||
writeJSON(w, 200, result)
|
||||
}
|
||||
|
||||
// ---------- agents ----------
|
||||
|
||||
Reference in new issue
Block a user