diff --git a/internal/tui/open_remote_unix.go b/internal/tui/open_remote_unix.go index 45f0e586..88363c44 100644 --- a/internal/tui/open_remote_unix.go +++ b/internal/tui/open_remote_unix.go @@ -76,12 +76,15 @@ func tuiRuntimeDir() (string, error) { return ensurePrivateDirectory(filepath.Join(runtimeDir, tuiRuntimeDirname)) } - tempDir := os.TempDir() + tempDir, err := filepath.EvalSymlinks(os.TempDir()) + if err != nil { + return "", fmt.Errorf("inspect temporary directory: %w", err) + } info, err := os.Lstat(tempDir) if err != nil { return "", fmt.Errorf("inspect temporary directory: %w", err) } - if !info.IsDir() || info.Mode()&os.ModeSymlink != 0 { + if !info.IsDir() { return "", fmt.Errorf("temporary directory is not a directory") } if info.Mode().Perm()&0o022 != 0 && info.Mode()&os.ModeSticky == 0 { diff --git a/internal/tui/open_remote_unix_test.go b/internal/tui/open_remote_unix_test.go index 04489195..b3cdf306 100644 --- a/internal/tui/open_remote_unix_test.go +++ b/internal/tui/open_remote_unix_test.go @@ -80,6 +80,28 @@ func TestTopicRemoteFallbackUsesPrivatePerUserDirectory(t *testing.T) { } } +// macOS's /tmp is a symlink to /private/tmp, and it is what os.TempDir answers whenever +// TMPDIR is unset — an ssh session, a launchd job, a shell started without the login +// environment — so the fallback has to follow the link rather than refuse it. +func TestTopicRemoteFallbackFollowsSymlinkedTemporaryDirectory(t *testing.T) { + t.Setenv("XDG_RUNTIME_DIR", "") + target := t.TempDir() + link := filepath.Join(t.TempDir(), "tmp") + if err := os.Symlink(target, link); err != nil { + t.Fatalf("symlink temporary directory: %v", err) + } + t.Setenv("TMPDIR", link) + + path := mustTUISocketPath(t, "omarchy") + resolved, err := filepath.EvalSymlinks(target) + if err != nil { + t.Fatalf("resolve temporary directory: %v", err) + } + if got := filepath.Dir(filepath.Dir(path)); got != resolved { + t.Fatalf("socket %s is not under the resolved temporary directory %s", path, resolved) + } +} + func TestTopicRemoteRejectsInsecureRuntimeDirectory(t *testing.T) { runtimeDir := t.TempDir() if err := os.Chmod(runtimeDir, 0o755); err != nil {