package target_test import ( "os" "path/filepath" "reflect" "strings" "testing" "proxyctl/internal/target" ) func TestReadOnlyWriteAuthorityRejectsUnreadableExistingFile(t *testing.T) { if os.Geteuid() == 0 { t.Skip("root can read files regardless of mode bits") } path := filepath.Join(t.TempDir(), "managed.json") if err := os.WriteFile(path, []byte("{}\n"), 0o200); err != nil { t.Fatal(err) } check := target.ReadOnlyWriteAuthority(path) if check.OK || !strings.Contains(check.Evidence, "cannot be read") { t.Fatalf("unreadable destination reported writable: %#v", check) } } func TestReadOnlyWriteAuthorityRejectsSymlinkedFinalAndAncestorWithoutTouchingVictim(t *testing.T) { for _, test := range []struct { name string path func(root, victim string) string }{ { name: "final path", path: func(root, victim string) string { link := filepath.Join(root, "managed.json") if err := os.Symlink(victim, link); err != nil { t.Fatal(err) } return link }, }, { name: "ancestor path", path: func(root, victim string) string { victimDir := filepath.Dir(victim) link := filepath.Join(root, "managed") if err := os.Symlink(victimDir, link); err != nil { t.Fatal(err) } return filepath.Join(link, filepath.Base(victim)) }, }, } { t.Run(test.name, func(t *testing.T) { root := t.TempDir() victimDir := t.TempDir() victim := filepath.Join(victimDir, "victim.json") const content = "VICTIM-CONTENT-MUST-STAY" if err := os.WriteFile(victim, []byte(content), 0o640); err != nil { t.Fatal(err) } beforeContent, err := os.ReadFile(victim) if err != nil { t.Fatal(err) } before, err := os.Stat(victim) if err != nil { t.Fatal(err) } check := target.ReadOnlyWriteAuthority(test.path(root, victim)) if check.OK || (!strings.Contains(check.Evidence, "symbolic link") && !strings.Contains(check.Evidence, "symlink")) { t.Fatalf("symlink path reported writable: %#v", check) } after, err := os.Stat(victim) if err != nil { t.Fatal(err) } data, err := os.ReadFile(victim) if err != nil { t.Fatal(err) } if string(beforeContent) != content || string(data) != content || !reflect.DeepEqual(before.Sys(), after.Sys()) { t.Fatalf("victim changed: before=%#v after=%#v content=%q", before, after, data) } }) } }