v0.107.0: key-auth-first bridge + staged-secret wipe on escrow confirm
Key-auth-first: a KeyAuthProber seam lets the bridge skip consume+install when the already-installed key still authenticates (pinned to the freshly verified host key) — descriptor changes on provisioned guests no longer loop on consume-404. Fingerprint verify still precedes everything. Wipe-on-escrowed: confirm-escrow now calls the agent's new DELETE /escrow/stage-secret (v0.78.0) best-effort, closing the hygiene gap where a ceremony-less confirm left the staged password file behind. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net/http/httptest"
|
||||
@@ -67,6 +70,45 @@ func TestOffboxWeb_RunGatedUntilConfirm(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Scenario E — the confirm flip wipes the agent-staged secret; a wipe failure is logged loudly but does
|
||||
// NOT fail the confirm (the state flip is the primary effect).
|
||||
func TestOffboxWeb_ConfirmWipesStagedSecret(t *testing.T) {
|
||||
s, sett, m := newOffboxWebServer(t)
|
||||
if err := m.WriteOffboxSecrets("KEYMATERIAL", "nas.local ssh-ed25519 HOSTKEY"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := sett.SetOffboxTarget(&settings.OffboxTarget{
|
||||
Enabled: true, Host: "nas.local", Port: 22, User: "felhom", RepoPath: "/srv/repo", Schedule: "daily",
|
||||
EscrowState: "pending",
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wipes := 0
|
||||
s.wipeStagedEscrowFn = func(context.Context) error { wipes++; return nil }
|
||||
w := httptest.NewRecorder()
|
||||
s.offboxConfirmEscrowHandler(w, httptest.NewRequest("POST", "/backup/offbox/confirm-escrow", nil))
|
||||
if w.Code != 302 || sett.GetOffboxTarget().EscrowState != "escrowed" {
|
||||
t.Fatalf("confirm failed: code=%d state=%q", w.Code, sett.GetOffboxTarget().EscrowState)
|
||||
}
|
||||
if wipes != 1 {
|
||||
t.Fatalf("confirm must wipe the staged secret exactly once, got %d", wipes)
|
||||
}
|
||||
|
||||
// wipe failure → confirm still succeeds (best-effort), loud ERROR logged
|
||||
var logbuf bytes.Buffer
|
||||
s.logger = log.New(&logbuf, "", 0)
|
||||
_ = sett.UpdateOffboxStatus(func(o *settings.OffboxTarget) { o.EscrowState = "pending" })
|
||||
s.wipeStagedEscrowFn = func(context.Context) error { return errors.New("agent unreachable") }
|
||||
w2 := httptest.NewRecorder()
|
||||
s.offboxConfirmEscrowHandler(w2, httptest.NewRequest("POST", "/backup/offbox/confirm-escrow", nil))
|
||||
if w2.Code != 302 || sett.GetOffboxTarget().EscrowState != "escrowed" {
|
||||
t.Fatal("a wipe failure must NOT fail the confirm (state flip is primary)")
|
||||
}
|
||||
if !strings.Contains(logbuf.String(), "NOT wiped") {
|
||||
t.Fatal("a failed wipe must log the loud NOT-wiped signal")
|
||||
}
|
||||
}
|
||||
|
||||
// The inject endpoint pre-places a recovered password (DR seam).
|
||||
func TestOffboxWeb_InjectPassword(t *testing.T) {
|
||||
s, _, _ := newOffboxWebServer(t)
|
||||
|
||||
Reference in New Issue
Block a user