From cbc3cd21f7952a286ea1aeca25518bfd12e01d18 Mon Sep 17 00:00:00 2001 From: saku0512 Date: Mon, 18 May 2026 22:24:12 +0900 Subject: [PATCH] prompts: strip control characters from VCS state and PWD Strip control characters from VCS branch and state strings before writing them in prompts. Also route the informative and minimalist sample prompts through prompt_pwd instead of printing PWD directly. --- CHANGELOG.rst | 2 ++ share/functions/fish_fossil_prompt.fish | 3 +++ share/functions/fish_git_prompt.fish | 16 ++++++++--- share/functions/fish_hg_prompt.fish | 7 ++++- share/prompts/informative.fish | 2 +- share/prompts/minimalist.fish | 2 +- tests/checks/vcs-prompts.fish | 36 +++++++++++++++++++++++++ 7 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 tests/checks/vcs-prompts.fish diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9165c5d6e..98cd6ebe9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,6 +9,8 @@ Interactive improvements ------------------------ - On the first run after upgrading from an older version, fish will try harder to check if the current theme matches a historical default, in which case fish won't create ``~/.config/fish/conf.d/fish_frozen_theme.fish``. This means that on systems where fish version 3.x was installed originally, the update will avoid creating that file (:issue:`12725`). +- ``fish_hg_prompt``, ``fish_git_prompt`` and ``fish_fossil_prompt`` now strip control characters from VCS state read off disk, matching ``prompt_pwd``. +- The sample informative and minimalist prompts now use ``prompt_pwd`` instead of printing ``$PWD`` directly. For distributors and developers ------------------------------- diff --git a/share/functions/fish_fossil_prompt.fish b/share/functions/fish_fossil_prompt.fish index 122d57656..21636dffb 100644 --- a/share/functions/fish_fossil_prompt.fish +++ b/share/functions/fish_fossil_prompt.fish @@ -48,6 +48,9 @@ function fish_fossil_prompt --description 'Write out the fossil prompt' echo -n ' (' set_color magenta + # Strip control characters to avoid injecting terminal escape sequences into the prompt. + # Raw C1 bytes are stored in fish's private-use encoding at U+F680-U+F69F. + set branch (string replace -ra '[\x00-\x1f\x7f-\x9f\x{f680}-\x{f69f}]' '' -- $branch) echo -n "$branch" set_color --reset echo -n '|' diff --git a/share/functions/fish_git_prompt.fish b/share/functions/fish_git_prompt.fish index ec8ff7423..73dcdd1ec 100644 --- a/share/functions/fish_git_prompt.fish +++ b/share/functions/fish_git_prompt.fish @@ -495,6 +495,8 @@ function __fish_git_prompt_operation_branch_bare --description "fish_git_prompt set -l step set -l total + # Strip control characters from these dot-files before showing them in the prompt. + # (git itself does not validate the contents of rebase-merge/rebase-apply state files). if test -d $git_dir/rebase-merge set branch (cat $git_dir/rebase-merge/head-name 2>/dev/null) set step (cat $git_dir/rebase-merge/msgnum 2>/dev/null) @@ -527,10 +529,6 @@ function __fish_git_prompt_operation_branch_bare --description "fish_git_prompt end end - if test -n "$step" -a -n "$total" - set operation "$operation $step/$total" - end - if test -z "$branch" if not set branch (command git symbolic-ref HEAD 2>/dev/null) set detached yes @@ -567,6 +565,16 @@ function __fish_git_prompt_operation_branch_bare --description "fish_git_prompt end end + for var_name in branch step total + if set -q $var_name[1] + set $var_name (string replace -ra '[\x00-\x1f\x7f-\x9f\x{f680}-\x{f69f}]' '' -- $$var_name) + end + end + + if test -n "$step" -a -n "$total" + set operation "$operation $step/$total" + end + echo $operation echo $branch echo $detached diff --git a/share/functions/fish_hg_prompt.fish b/share/functions/fish_hg_prompt.fish index f7a1c09d1..df6efe9cd 100644 --- a/share/functions/fish_hg_prompt.fish +++ b/share/functions/fish_hg_prompt.fish @@ -30,9 +30,14 @@ function fish_hg_prompt --description 'Write out the hg prompt' set -l root (fish_print_hg_root) or return 1 - # Read branch and bookmark + # Read branch and bookmark. + # Strip control characters before showing branch/bookmark names in the prompt. + # Raw C1 bytes are stored in fish's private-use encoding at U+F680-U+F69F. + # (they live under .hg/ and are not validated by hg itself in this code path). set -l branch (cat $root/branch 2>/dev/null; or echo default) + set branch (string replace -ra '[\x00-\x1f\x7f-\x9f\x{f680}-\x{f69f}]' '' -- $branch) if set -l bookmark (cat $root/bookmarks.current 2>/dev/null) + set bookmark (string replace -ra '[\x00-\x1f\x7f-\x9f\x{f680}-\x{f69f}]' '' -- $bookmark) set branch "$branch|$bookmark" end diff --git a/share/prompts/informative.fish b/share/prompts/informative.fish index 67a05fac6..b8fbacb6d 100644 --- a/share/prompts/informative.fish +++ b/share/prompts/informative.fish @@ -17,7 +17,7 @@ function fish_prompt --description 'Informative prompt' set -l pipestatus_string (__fish_print_pipestatus "[" "]" "|" "$status_color" "$statusb_color" $last_pipestatus) printf '[%s] %s%s@%s %s%s %s%s%s \n> ' (date "+%H:%M:%S") (set_color brblue) \ - $USER (prompt_hostname) (set_color $fish_color_cwd) $PWD $pipestatus_string \ + $USER (prompt_hostname) (set_color $fish_color_cwd) (prompt_pwd) $pipestatus_string \ (set_color --reset) end end diff --git a/share/prompts/minimalist.fish b/share/prompts/minimalist.fish index 5e0dba7d7..e223ff37a 100644 --- a/share/prompts/minimalist.fish +++ b/share/prompts/minimalist.fish @@ -3,7 +3,7 @@ function fish_prompt set_color $fish_color_cwd - echo -n (path basename $PWD) + echo -n (prompt_pwd | path basename) set_color --reset echo -n ' ) ' end diff --git a/tests/checks/vcs-prompts.fish b/tests/checks/vcs-prompts.fish new file mode 100644 index 000000000..c4b4d61dc --- /dev/null +++ b/tests/checks/vcs-prompts.fish @@ -0,0 +1,36 @@ +#RUN: %fish %s +#REQUIRES: command -v git + +# Verify VCS prompts strip control characters from +# state files before showing them (same class as prompt_pwd / #12629). + +set -l tmp (mktemp -d) + +# --- fish_hg_prompt --- +# Stub `hg` to satisfy `command -sq hg` without requiring real Mercurial. +mkdir $tmp/bin +echo '#!/bin/sh' >$tmp/bin/hg +chmod +x $tmp/bin/hg +set -gx PATH $tmp/bin $PATH + +mkdir -p $tmp/hgrepo/.hg +touch $tmp/hgrepo/.hg/dirstate +printf 'main\e]0;OHNO\a\x7f\u0080\x80' >$tmp/hgrepo/.hg/branch + +cd $tmp/hgrepo +fish_hg_prompt +echo +# CHECK: {{.*}} (main]0;OHNO) + +# --- fish_git_prompt: rebase-merge state with escape sequence in head-name --- +mkdir $tmp/gitrepo +cd $tmp/gitrepo +git init -q +mkdir .git/rebase-merge +printf 'refs/heads/main\e]0;OHNO\a\x7f\u0080\x80' >.git/rebase-merge/head-name +: >.git/rebase-merge/msgnum +: >.git/rebase-merge/end + +fish_git_prompt +echo +# CHECK: (main]0;OHNO|REBASE-m)