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:
2026-07-03 07:25:50 +02:00
parent cc93dae792
commit 3f382bf762
20 changed files with 767 additions and 44 deletions
+30 -5
View File
@@ -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