mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-10-20 06:27:53 +02:00
104 lines
3.6 KiB
Go
104 lines
3.6 KiB
Go
package update
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestRollbackRecoveryRejectsAmbiguousEvidence(t *testing.T) {
|
|
for _, variant := range []string{"valid", "missing_restore_binary", "missing_live_hash", "contradictory_previous_hash", "duplicate_restore_binary"} {
|
|
t.Run(variant, func(t *testing.T) {
|
|
requireGit(t)
|
|
tr := newTree(t)
|
|
scriptFakeGo(t)
|
|
bin := filepath.Join(tr.install, Binary)
|
|
writeFile(t, bin, "original binary")
|
|
tr.upstreamCommit(t, "second", map[string]string{"run.sh": "new script\n"})
|
|
if _, err := Apply(context.Background(), buildOpts(tr), nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
st, _ := readState(tr.install)
|
|
live := readFile(t, bin)
|
|
liveHash, _ := fileSHA256(bin)
|
|
previousHash, _ := fileSHA256(bin + ".prev")
|
|
pending := rollbackPendingState{State: st, RestoreBinary: true, LiveSHA256: liveHash, PreviousSHA256: previousHash}
|
|
encoded, _ := json.Marshal(pending)
|
|
var fields map[string]interface{}
|
|
_ = json.Unmarshal(encoded, &fields)
|
|
switch variant {
|
|
case "missing_restore_binary":
|
|
delete(fields, "restore_binary")
|
|
case "missing_live_hash":
|
|
delete(fields, "live_sha256")
|
|
case "contradictory_previous_hash":
|
|
writeFile(t, bin+".prev", "unrelated binary")
|
|
wrongHash, _ := fileSHA256(bin + ".prev")
|
|
fields["previous_sha256"] = wrongHash
|
|
}
|
|
encoded, _ = json.Marshal(fields)
|
|
if variant == "duplicate_restore_binary" {
|
|
encoded = append(encoded[:len(encoded)-1], []byte(",\"restore_binary\":false}")...)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(tr.install, rollbackPendingFile), encoded, 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
mustGit(t, tr.install, "reset", "--hard", st.PreviousCommit)
|
|
_, err := Rollback(context.Background(), buildOpts(tr))
|
|
if variant == "valid" {
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := readFile(t, bin); got != "original binary" {
|
|
t.Fatalf("valid rollback did not restore binary: %q", got)
|
|
}
|
|
return
|
|
}
|
|
if err == nil {
|
|
t.Errorf("malformed/contradictory marker authorized recovery, live before=%q after=%q; state exists=%v marker exists=%v", live, readFile(t, bin), fileExists(statePath(tr.install)), fileExists(filepath.Join(tr.install, rollbackPendingFile)))
|
|
}
|
|
if got := readFile(t, bin); got != live {
|
|
t.Errorf("invalid evidence mutated binary: %q => %q", live, got)
|
|
}
|
|
if !fileExists(statePath(tr.install)) || !fileExists(filepath.Join(tr.install, rollbackPendingFile)) {
|
|
t.Error("invalid evidence deleted recovery records")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRollbackRecoveryAfterStateCleanup(t *testing.T) {
|
|
requireGit(t)
|
|
tr := newTree(t)
|
|
scriptFakeGo(t)
|
|
bin := filepath.Join(tr.install, Binary)
|
|
writeFile(t, bin, "original binary")
|
|
tr.upstreamCommit(t, "second", map[string]string{"run.sh": "new script\n"})
|
|
if _, err := Apply(context.Background(), buildOpts(tr), nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
st, _ := readState(tr.install)
|
|
pending := rollbackPendingState{State: st, RestoreBinary: true, LiveSHA256: st.BinarySHA256, PreviousSHA256: st.PreviousBinarySHA256}
|
|
if err := writeRollbackPending(tr.install, pending); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
mustGit(t, tr.install, "reset", "--hard", st.PreviousCommit)
|
|
if err := restorePrevBinary(bin); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Remove(statePath(tr.install)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := Rollback(context.Background(), buildOpts(tr)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if readFile(t, bin) != "original binary" {
|
|
t.Fatal("cleanup retry changed restored binary")
|
|
}
|
|
if fileExists(filepath.Join(tr.install, rollbackPendingFile)) {
|
|
t.Fatal("cleanup retry left journal")
|
|
}
|
|
}
|