fix(AGENT-001): anti-retarget re-resolution for inline customer-confirmed wipe
handleDiskFormat's customer-confirmed branch formatted the mutable req.Device path; the durable id only bound the confirmation, never the mkfs target. A /dev reassignment between inspect and mkfs could wipe the wrong physical disk. Now mirrors signedjobs.WipeExecutor: resolve confirmed durable id -> current device, re-derive + require exact match, re-inspect (still data-bearing), then format THAT device. Any refusal -> 409, no mkfs. New antiRetargetResolve helper (injected deps, unit-tested: mismatch/gone/blank/empty all refuse). Injectable reresolveWipe seam on Server (defaults to real storage funcs). BRANCH ONLY — pending supervised review/deploy (see AGENT-001-FIX-NOTES.md). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -457,15 +457,30 @@ func (s *Server) handleDiskFormat(w http.ResponseWriter, r *http.Request, vmid i
|
||||
})
|
||||
|
||||
if dec.Allowed {
|
||||
// USER-DATA, customer-confirmed (durable-id-bound). The gate already AUDITED it. Wipe.
|
||||
if err := s.disks.Format(r.Context(), req.Device, req.FSType); err != nil {
|
||||
s.logger.Error("local-api: customer-confirmed format", "vmid", vmid, "device", req.Device, "err", err)
|
||||
// USER-DATA, customer-confirmed (durable-id-bound). The gate already AUDITED it.
|
||||
// [AGENT-001] anti-retarget: re-resolve the confirmed durable id to the CURRENT
|
||||
// device, require the re-derived id to match, and confirm it is still
|
||||
// data-bearing — then format THAT device, never the mutable req.Device path.
|
||||
// This closes the classify→mkfs TOCTOU (a /dev reassignment in the window could
|
||||
// otherwise wipe a different physical disk). Mirrors signedjobs.WipeExecutor.
|
||||
device, rerr := s.reresolveWipe(r.Context(), deviceDurable)
|
||||
if rerr != nil {
|
||||
s.logger.Warn("local-api: customer-confirmed wipe REFUSED at anti-retarget re-resolve",
|
||||
"vmid", vmid, "req_device", req.Device, "durable_id", deviceDurable, "err", rerr)
|
||||
writeStatus(w, http.StatusConflict, false,
|
||||
FormatResponse{VMID: vmid, Device: req.Device, Formatted: false, DataBearing: true,
|
||||
Role: string(role), DurableID: deviceDurable, Reason: probe.Reason()},
|
||||
"wipe refused (device may have changed since confirmation): "+rerr.Error())
|
||||
return
|
||||
}
|
||||
if err := s.disks.Format(r.Context(), device, req.FSType); err != nil {
|
||||
s.logger.Error("local-api: customer-confirmed format", "vmid", vmid, "device", device, "err", err)
|
||||
writeErr(w, http.StatusBadGateway, "format failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
s.logger.Warn("local-api: USER-DATA data-bearing format — CUSTOMER CONFIRMED (no operator signature)",
|
||||
"vmid", vmid, "device", req.Device, "durable_id", deviceDurable, "fstype", req.FSType, "why", probe.Reason())
|
||||
writeOK(w, FormatResponse{VMID: vmid, Device: req.Device, Formatted: true, DataBearing: true,
|
||||
"vmid", vmid, "device", device, "durable_id", deviceDurable, "fstype", req.FSType, "why", probe.Reason())
|
||||
writeOK(w, FormatResponse{VMID: vmid, Device: device, Formatted: true, DataBearing: true,
|
||||
Role: string(role), DurableID: deviceDurable, Reason: "customer-confirmed wipe (" + probe.Reason() + ")"})
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user