v0.61.0: audit fixes B1 (random temp staging) + D1 (mkfs wrapper member/RO re-checks) + D2 (empty-lsblk fail-safe) + D3 (blank-format anti-retarget)
From AUDIT-blast-radius-hostroot-localapi-2026-07-02.md. Each fix ships with a non-hollow test + a companion red-proof (shown failing on the pre-fix impl). Sudoers install-source grants became globs — deploy the sudoers drop-in with the binary. A1 (stale-lock pool-membership) deliberately excluded (spike). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -678,16 +678,41 @@ func (s *Server) handleDiskFormat(w http.ResponseWriter, r *http.Request, vmid i
|
||||
// Blank device → benign → mkfs (role is irrelevant; there is nothing to destroy). F20-BUG3: run
|
||||
// it DETACHED off s.baseCtx so a request/client deadline can't SIGKILL mkfs mid-write; we still
|
||||
// wait here to return the synchronous result (backward-compatible with the controller's client).
|
||||
done := s.startFormatDetached(req.Device, "", req.FSType)
|
||||
if err := s.awaitFormat(r.Context(), done, vmid, req.Device); err != nil {
|
||||
//
|
||||
// [audit D3, AGENT-001's benign-branch twin] anti-retarget: bind the format to the device's
|
||||
// durable id and re-resolve it to the CURRENT device (re-derive + exact match + re-inspect
|
||||
// STILL blank) — then format THAT device, never the mutable req.Device path. A /dev
|
||||
// re-enumeration in the window could otherwise mkfs a data-bearing disk that inherited the
|
||||
// node, with neither the DataBearing customer-confirm nor any durable-id binding. A device with
|
||||
// no durable id cannot be bound → refused (a path-only format is what the guard prevents).
|
||||
blankDurable, derr := s.deviceDurableID(req.Device)
|
||||
if derr != nil || blankDurable == "" {
|
||||
s.logger.Warn("local-api: blank format REFUSED — device has no durable id to bind (anti-retarget)",
|
||||
"vmid", vmid, "device", req.Device, "err", derr)
|
||||
writeStatus(w, http.StatusConflict, false,
|
||||
FormatResponse{VMID: vmid, Device: req.Device, Formatted: false, DataBearing: false},
|
||||
"format refused: device has no durable id to bind the format to (path-only formats are not permitted)")
|
||||
return
|
||||
}
|
||||
device, rerr := s.reresolveBlank(r.Context(), blankDurable)
|
||||
if rerr != nil {
|
||||
s.logger.Warn("local-api: blank format REFUSED at anti-retarget re-resolve",
|
||||
"vmid", vmid, "req_device", req.Device, "durable_id", blankDurable, "err", rerr)
|
||||
writeStatus(w, http.StatusConflict, false,
|
||||
FormatResponse{VMID: vmid, Device: req.Device, Formatted: false, DataBearing: false, DurableID: blankDurable},
|
||||
"format refused (device may have changed since inspection): "+rerr.Error())
|
||||
return
|
||||
}
|
||||
done := s.startFormatDetached(device, blankDurable, req.FSType, true)
|
||||
if err := s.awaitFormat(r.Context(), done, vmid, device); err != nil {
|
||||
if err == errFormatClientGone {
|
||||
return // client gone; mkfs continues detached + the job record records the outcome
|
||||
}
|
||||
s.logger.Error("local-api: format", "vmid", vmid, "device", req.Device, "err", err)
|
||||
s.logger.Error("local-api: format", "vmid", vmid, "device", device, "err", err)
|
||||
writeErr(w, http.StatusBadGateway, "format failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
writeOK(w, FormatResponse{VMID: vmid, Device: req.Device, Formatted: true, DataBearing: false, Reason: "blank device formatted " + req.FSType})
|
||||
writeOK(w, FormatResponse{VMID: vmid, Device: device, Formatted: true, DataBearing: false, DurableID: blankDurable, Reason: "blank device formatted " + req.FSType})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -724,7 +749,7 @@ func (s *Server) handleDiskFormat(w http.ResponseWriter, r *http.Request, vmid i
|
||||
// F20-BUG3: run the destructive mkfs DETACHED off s.baseCtx (bound durable id recorded for
|
||||
// restart-recovery), so a request/client deadline can never SIGKILL it mid-write and corrupt the
|
||||
// disk. We still wait to return the synchronous result (backward-compatible with the controller).
|
||||
done := s.startFormatDetached(device, deviceDurable, req.FSType)
|
||||
done := s.startFormatDetached(device, deviceDurable, req.FSType, false)
|
||||
if err := s.awaitFormat(r.Context(), done, vmid, device); err != nil {
|
||||
if err == errFormatClientGone {
|
||||
return // client gone; the wipe continues detached + survives a restart via the job record
|
||||
|
||||
Reference in New Issue
Block a user