From f7392a8b63ba399582418f097026c80bab30bd12 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sun, 5 Jul 2026 20:15:05 +0900 Subject: [PATCH] Pre-create become file with O_EXCL Prevents another user on a shared TMPDIR from planting a file or a symbolic link at the predictable path while fzf is running, like the exit status file of the floating pane. Reject an empty become command. --- src/proxy.go | 16 +++++++++++++--- test/test_tmux.rb | 7 +++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/proxy.go b/src/proxy.go index c02aedcf..866cc40a 100644 --- a/src/proxy.go +++ b/src/proxy.go @@ -138,6 +138,17 @@ func runProxy(commandPrefix string, cmdBuilder func(temp string, needBash bool) temp := WriteTemporaryFile(append(exports, command), "\n") defer os.Remove(temp) + // Pre-create the become file so that another user on a shared TMPDIR + // cannot plant a file or a symbolic link at the predictable path while + // fzf is running + becomeFile := temp + becomeSuffix + becomeF, err := os.OpenFile(becomeFile, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o600) + if err != nil { + return ExitError, err + } + becomeF.Close() + defer os.Remove(becomeFile) + cmd, err := cmdBuilder(temp, needBash) if err != nil { return ExitError, err @@ -155,16 +166,15 @@ func runProxy(commandPrefix string, cmdBuilder func(temp string, needBash bool) if exitError, ok := err.(*exec.ExitError); ok { code := exitError.ExitCode() if code == ExitBecome { - becomeFile := temp + becomeSuffix data, err := os.ReadFile(becomeFile) os.Remove(becomeFile) if err != nil { return ExitError, err } - elems := strings.Split(string(data), "\x00") - if len(elems) < 1 { + if len(data) == 0 { return ExitError, errors.New("invalid become command") } + elems := strings.Split(string(data), "\x00") command := elems[0] env := []string{} if len(elems) > 1 { diff --git a/test/test_tmux.rb b/test/test_tmux.rb index ac43eb2a..eebc4c66 100644 --- a/test/test_tmux.rb +++ b/test/test_tmux.rb @@ -48,6 +48,13 @@ class TestTmux < TestInteractive assert_equal '1', fzf_output end + def test_floating_pane_become + tmux.send_keys "seq 100 | #{fzf(%(--popup center,80% --margin 0 --bind 'enter:become(echo became-{})'))}", :Enter + tmux.until { |lines| assert_equal 100, lines.item_count } + tmux.send_keys :Enter + assert_equal 'became-1', fzf_output + end + def test_explicit_border_falls_back_to_popup # display-popup requires an attached client, which the test environment # may not have; intercept it with a tmux shim on PATH