//go:build linux package host import ( "os" "path/filepath" "testing" ) func TestClaimPrivateStateRepairsOwnedSQLiteMode(t *testing.T) { root := t.TempDir() path := filepath.Join(root, "launches.db") if err := os.WriteFile(path, []byte("journal"), 0644); err != nil { t.Fatal(err) } if err := claimPrivatePathOwner(path, os.Getuid(), os.Getgid(), false); err != nil { t.Fatal(err) } info, err := os.Stat(path) if err != nil || info.Mode().Perm() != 0600 { t.Fatalf("private database: info=%v err=%v", info, err) } if err := claimPrivatePathOwner(root, os.Getuid(), os.Getgid(), true); err != nil { t.Fatal(err) } info, err = os.Stat(root) if err != nil || info.Mode().Perm() != 0700 { t.Fatalf("private directory: info=%v err=%v", info, err) } } func TestClaimPrivateStateRefusesSymlinkAndForeignOwner(t *testing.T) { root := t.TempDir() path := filepath.Join(root, "journal") if err := os.WriteFile(path, nil, 0600); err != nil { t.Fatal(err) } link := filepath.Join(root, "link") if err := os.Symlink(path, link); err != nil { t.Fatal(err) } if err := claimPrivatePathOwner(link, os.Getuid(), os.Getgid(), false); err == nil { t.Fatal("symlink accepted") } if os.Geteuid() != 0 { if err := claimPrivatePathOwner(path, os.Getuid()+1, os.Getgid(), false); err == nil { t.Fatal("foreign owner accepted") } } } func TestClaimReviewDatabaseOwnerCheckedBeforeSQLite(t *testing.T) { path := filepath.Join(t.TempDir(), "journal.db") if err := prepareClaimDatabaseOwner(path, os.Getuid(), os.Getgid()); err != nil { t.Fatal(err) } if err := prepareClaimDatabaseOwner(path, os.Getuid()+1, os.Getgid()); err == nil { t.Fatal("foreign journal owner accepted") } victim := filepath.Join(t.TempDir(), "victim") if err := os.Symlink(victim, path+"-wal"); err != nil { t.Fatal(err) } if err := prepareClaimDatabaseOwner(path, os.Getuid(), os.Getgid()); err == nil { t.Fatal("planted WAL accepted") } if _, err := os.Lstat(victim); !os.IsNotExist(err) { t.Fatalf("touched victim: %v", err) } } func TestClaimStateOwnersRejectHardLinksAndSymlinkParents(t *testing.T) { for _, kind := range []string{"hardlink", "parent"} { for _, operation := range []string{"repair", "prepare"} { t.Run(kind+"/"+operation, func(t *testing.T) { dir := t.TempDir() victim := filepath.Join(dir, "victim") if err := os.WriteFile(victim, []byte("unchanged"), 0644); err != nil { t.Fatal(err) } path := filepath.Join(dir, "unsafe") if kind == "hardlink" { if err := os.Link(victim, path); err != nil { t.Fatal(err) } } else { if err := os.Symlink(dir, path); err != nil { t.Fatal(err) } path = filepath.Join(path, "victim") } var err error if operation == "repair" { err = claimPrivatePathOwner(path, os.Getuid(), os.Getgid(), false) } else { err = prepareClaimDatabaseOwner(path, os.Getuid(), os.Getgid()) } if err == nil { t.Fatal("unsafe claim path accepted") } info, err := os.Stat(victim) if err != nil || info.Mode().Perm() != 0644 { t.Fatalf("victim mode changed: %v %v", info, err) } body, err := os.ReadFile(victim) if err != nil || string(body) != "unchanged" { t.Fatalf("victim changed: %q %v", body, err) } }) } } }