fix: harden agent writeback safety

This commit is contained in:
Yoilun
2026-05-25 21:26:37 +08:00
parent a01dd36fb0
commit d7b75a1112
6 changed files with 341 additions and 34 deletions

View File

@@ -6,6 +6,8 @@ import (
"path/filepath"
"strings"
"testing"
"codex-agent-manager/internal/codexhome"
)
func TestValidateInvalidTOMLReturnsInvalidAndDoesNotWrite(t *testing.T) {
@@ -78,6 +80,62 @@ func TestWriteExpectedHashMismatchRejectsAndLeavesOriginal(t *testing.T) {
assertFileContent(t, target, `name = "用户已改"`+"\n")
}
func TestWriteRejectsAgentsDirectoryReplacementBeforeBackup(t *testing.T) {
store, target := writebackFixture(t, `name = "旧名称"`+"\n")
validation, err := store.ValidateDraft("backend", `name = "新名称"`+"\n")
if err != nil {
t.Fatal(err)
}
root := filepath.Dir(filepath.Dir(target))
agentsDir := filepath.Join(root, "agents")
realAgentsDir := filepath.Join(root, "agents-real")
externalDir := filepath.Join(root, "external")
if err := os.MkdirAll(externalDir, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(externalDir, "backend.toml"), []byte(`name = "外部"`+"\n"), 0o644); err != nil {
t.Fatal(err)
}
writebackTestHookBeforeBackup = func() {
if err := os.Rename(agentsDir, realAgentsDir); err != nil {
t.Fatal(err)
}
if err := os.Symlink(externalDir, agentsDir); err != nil {
t.Fatal(err)
}
}
defer func() { writebackTestHookBeforeBackup = nil }()
_, err = store.WriteDraft("backend", `name = "新名称"`+"\n", validation.CurrentHash)
if !errors.Is(err, ErrWriteConflict) && !errors.Is(err, codexhome.ErrForbiddenPath) {
t.Fatalf("expected directory replacement to be rejected, got %v", err)
}
assertFileContent(t, filepath.Join(realAgentsDir, "backend.toml"), `name = "旧名称"`+"\n")
assertFileContent(t, filepath.Join(externalDir, "backend.toml"), `name = "外部"`+"\n")
}
func TestWriteRejectsTargetChangeAfterBackup(t *testing.T) {
store, target := writebackFixture(t, `name = "旧名称"`+"\n")
validation, err := store.ValidateDraft("backend", `name = "新名称"`+"\n")
if err != nil {
t.Fatal(err)
}
writebackTestHookAfterBackup = func() {
if err := os.WriteFile(target, []byte(`name = "用户已改"`+"\n"), 0o644); err != nil {
t.Fatal(err)
}
}
defer func() { writebackTestHookAfterBackup = nil }()
_, err = store.WriteDraft("backend", `name = "新名称"`+"\n", validation.CurrentHash)
if !errors.Is(err, ErrWriteConflict) {
t.Fatalf("expected post-backup target change to be rejected, got %v", err)
}
assertFileContent(t, target, `name = "用户已改"`+"\n")
}
func TestWriteSuccessCreatesBackupAndAtomicallyWrites(t *testing.T) {
store, target := writebackFixture(t, `name = "旧名称"`+"\n")
validation, err := store.ValidateDraft("backend", `name = "新名称"`+"\n")