E: format modifier allows unintended #() execution from variable values

5 views
Skip to first unread message

Mason Davis

unread,
Feb 24, 2026, 12:57:55 PM (2 days ago) Feb 24
to tmux-...@googlegroups.com
Hi,

My apologies for the duplicate thread on this.

The E: format modifier (format.c:5242) calls format_expand1() on the resolved value of a variable. Because format_expand1() processes #() directives, any variable whose resolved value contains #() will have that content executed as a shell command.

For example, if a user configures:
    set -g status-right '#{E:pane_title}'
and the pane title contains "#(id)" -- whether set by an escape
sequence, a script, or any other means -- the id command runs every
time the status line redraws.

This applies to any variable used with E: in a context where jobs are enabled (status-left, status-right, window-status-format, pane-border-format, etc.). It is not specific to pane_title.

In practice, any program output displayed in a tmux pane can set these variables via standard escape sequences, such as curling a URL, cat'ing a file, tailing a log, or connecting to a remote host over SSH. These are routine operations where the user does not expect terminal output to influence command execution.

The user has no reason to expect this. The default status-format template uses E: approximately 25 times (on style options), so the modifier appears safe for general use. Nothing documents that E: causes resolved variable values to be evaluated as format strings including #() shell execution.

The existing FORMAT_EXPAND_NOJOBS mechanism already prevents this class of issue in one place: format_job_get() (format.c:386) sets FORMAT_EXPAND_NOJOBS before re-expanding #() output, blocking nested command execution. But the E: path at format.c:5242 does not set this flag, so #() in re-expanded values is executed.

Proposed fix: Set FORMAT_EXPAND_NOJOBS before the E: re-expansion, matching the existing pattern at line 386:

    if (modifiers & FORMAT_EXPAND) {
-       new = format_expand1(es, value);
+       struct format_expand_state next;
+       format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS);
+       new = format_expand1(&next, value);
        free(value);
        value = new;
    }

This prevents #() execution during E: re-expansion while preserving all other format expansion (variable lookups, conditionals, etc.). Legitimate uses of E:, primarily re-expanding style options, do not use #() and are unaffected.

To reproduce:
    tmux set -g status-right '#{E:pane_title}'
    printf '\033]2;#(touch /tmp/test-e-modifier)\007'
    tmux refresh-client
    ls /tmp/test-e-modifier

Tested on current master (8356578a)

Cheers,
Mason


--

Mason Davis
Staff Security Engineer
Praetorian
 
Praetorian

Nicholas Marriott

unread,
Feb 24, 2026, 1:07:17 PM (2 days ago) Feb 24
to Mason Davis, tmux-...@googlegroups.com
Makes sense to me, fixed in OpenBSD now, will be in GitHub later. I made the same change for T: as well.


--
You received this message because you are subscribed to the Google Groups "tmux-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tmux-users+...@googlegroups.com.
To view this discussion, visit https://groups.google.com/d/msgid/tmux-users/CAB9y1TdkrhOm3NU_SoM3hqA3jqEyjHU89k9BkgZ-oc4-7-sR1w%40mail.gmail.com.

Nicholas Marriott

unread,
Feb 25, 2026, 5:58:21 PM (11 hours ago) Feb 25
to Mason Davis, tmux-users
I think we will need to revert or modify this because it breaks the common case of putting #() in for example status-left.

I wonder if the best approach might be to allow #() when E: is used with an option but forbid it with a format variable. This would allow commands to execute if someone had explicitly put them in an option, but prevent them being used unintentionally by changing the pane title or renaming a window.

Although TBH I don't know how much of a footgun this really is...
Reply all
Reply to author
Forward
0 new messages