package localapi import ( "context" "fmt" "gitea.dooplex.hu/admin/felhom-agent/internal/storage" ) // antiRetargetResolve mirrors signedjobs.WipeExecutor.Execute's anti-retarget // sequence for the inline customer-confirmed wipe path [AGENT-001]. // // The bug: handleDiskFormat inspected and gate-bound the device by its durable // id but then formatted the mutable caller-supplied /dev path (req.Device). If a // USB re-enumeration reassigned that /dev node to a DIFFERENT physical disk // between inspection and mkfs, the wipe hit the wrong drive — the classic // classify→mkfs TOCTOU the signed-jobs path already guards against. // // The fix: resolve the confirmed durable id to the CURRENT device, re-derive the // device's durable id and require an exact match (a path now pointing at a // different disk derives a different id → refuse), then re-inspect and require it // to STILL be data-bearing. Returns the re-resolved device to format — never the // caller-supplied path. resolve/derive/inspect are injected so this is unit // testable without touching real /dev (the Server wires the real storage funcs). func antiRetargetResolve( durableID string, resolve func(string) (string, error), derive func(string) (string, error), inspect func(string) (storage.DeviceProbe, error), ) (string, error) { if durableID == "" { // A path-only binding is exactly what the durable id exists to prevent. return "", fmt.Errorf("no durable_id — refusing a path-only wipe binding") } device, err := resolve(durableID) if err != nil { return "", fmt.Errorf("durable id %q no longer resolves (device removed/replaced?) — refusing: %w", durableID, err) } got, err := derive(device) if err != nil { return "", fmt.Errorf("cannot re-derive durable id for %s — refusing: %w", device, err) } if got != durableID { return "", fmt.Errorf("durable-id mismatch — %s now has id %q, confirmed id was %q — refusing", device, got, durableID) } probe, err := inspect(device) if err != nil { return "", fmt.Errorf("re-inspect %s failed — refusing: %w", device, err) } if !probe.Probed { return "", fmt.Errorf("%s did not probe cleanly at execution — refusing", device) } if !probe.DataBearing() { // The customer confirmed wiping a DATA-BEARING device; if it is now blank, // the target changed since confirmation — refuse rather than wipe blindly. return "", fmt.Errorf("%s is no longer data-bearing (target changed since confirmation) — refusing", device) } return device, nil } // reresolveDurableForWipe wires antiRetargetResolve with the real storage funcs // and the server's disk inspector. Returns the device to format, or an error to // refuse on. func (s *Server) reresolveDurableForWipe(ctx context.Context, durableID string) (string, error) { return antiRetargetResolve( durableID, storage.ResolveDurableDevice, storage.DeviceDurableID, func(dev string) (storage.DeviceProbe, error) { return s.disks.InspectDevice(ctx, dev) }, ) }