diff --git a/functions/_tide_internal_vcs_jj.fish b/functions/_tide_internal_vcs_jj.fish index 213e8f5..8ed42b3 100644 --- a/functions/_tide_internal_vcs_jj.fish +++ b/functions/_tide_internal_vcs_jj.fish @@ -48,6 +48,12 @@ if(self.contained_in("::trunk() & ~::@"), -T $tmpl 2>/dev/null) or return 1 + # Descriptions/bookmarks/tags are freeform text (unlike git refnames), + # so scrub control/ESC bytes before they ever reach the terminal -- a + # crafted commit description could otherwise inject escape sequences. + # \t is preserved: it's this template's field separator. + set raw_lines (string replace -ra '[\x00-\x08\x0b-\x1f\x7f]' '' -- $raw_lines) + # Colors # if bg color is normal, we can use matching jj colors if test $tide_jj_bg_color = normal diff --git a/functions/_tide_pwd.fish b/functions/_tide_pwd.fish index 0ac2c50..fe7b51f 100644 --- a/functions/_tide_pwd.fish +++ b/functions/_tide_pwd.fish @@ -8,6 +8,10 @@ set -l pwd_icon $tide_pwd_icon' ' eval "function _tide_pwd if set -l split_pwd (string replace -r '^$HOME' '~' -- \$PWD | string split /) + # Directory names may legally contain control/ESC bytes; scrub them + # before they reach the terminal (no \t exception here -- unlike the + # jj item, nothing downstream splits on tabs). + set split_pwd (string replace -ra '[\\x00-\\x1f\\x7f]' '' -- \$split_pwd) test -w . && set -f split_output \"$pwd_icon\$split_pwd[1]\" \$split_pwd[2..] || set -f split_output \"$unwritable_icon\$split_pwd[1]\" \$split_pwd[2..] set split_output[-1] \"$color_anchors\$split_output[-1]$reset_to_color_dirs\" diff --git a/tests/_tide_item_pwd.test.fish b/tests/_tide_item_pwd.test.fish index cff454b..b49cbc2 100644 --- a/tests/_tide_item_pwd.test.fish +++ b/tests/_tide_item_pwd.test.fish @@ -141,5 +141,9 @@ _pwd $tmpdir/tmp/noread/$longDir # CHECK: ~/t/n/a/b/c/d/e/f/golf/hotel/india/jul chmod 700 $tmpdir/tmp/noread command rm -r $tmpdir/tmp/noread +# ---------- Control/ESC bytes in a directory name are scrubbed ---------- +mkdir -p (printf '%s/tmp/evil\033dir/foo' $tmpdir) +_pwd (printf '%s/tmp/evil\033dir/foo' $tmpdir) # CHECK: ~/tmp/evildir/foo + # ------------------------------------Cleanup------------------------------------ command rm -r $tmpdir diff --git a/tests/_tide_item_vcs.test.fish b/tests/_tide_item_vcs.test.fish index 04e176d..8c20bc1 100644 --- a/tests/_tide_item_vcs.test.fish +++ b/tests/_tide_item_vcs.test.fish @@ -119,5 +119,14 @@ _vcs_item # CHECK: (@ abc123 main def456 * ↑1) touch .disable-jj-prompt _vcs_item # CHECK: +# -------- jj description control-char scrubbing -------- +# Regression test: a crafted commit description could otherwise inject a raw +# ESC byte straight into the terminal (see _tide_internal_vcs_jj.fish). +mkdir -p $dir/jj-scrub-repo/.jj +printf '#!/bin/sh\ncmd="$*"\ncase "$cmd" in\n *"workspace list"*) printf "default\\n" ;;\n *"log"*) printf "abc123\\t.\\tbookmark/main\\tdefault\\tdef456\\t*\\tfalse\\tevil\\033[31mdesc\\n" ;;\n *) exit 0 ;;\nesac\n' >$dir/bin/jj +cd $dir/jj-scrub-repo +set -e tide_jj_show_description +_vcs_item # CHECK: (@ abc123 main def456 * evil[31mdesc ↑1) + # ------ cleanup ------ command rm -r $dir