From 96c08602acfa3e27ec7a08bbdc818b8a982eb4ad Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 28 Sep 2026 22:50:14 +0900 Subject: [PATCH] Sandbox the tag address in the preview too A preview runs as soon as an entry is highlighted, so a crafted tags file did not even need to be selected to get a shell command out of 'vim -c "silent {excmd}"'. The address goes to ':execute' as a string literal with its quotes doubled, since ':sandbox' as a modifier would stop at the first '|'. An entry with no address is refused rather than previewing the last line of the file, which is where Ex mode leaves the cursor. --- bin/tagpreview.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/bin/tagpreview.sh b/bin/tagpreview.sh index 48ee539..e22a732 100755 --- a/bin/tagpreview.sh +++ b/bin/tagpreview.sh @@ -30,9 +30,21 @@ else exit 1 fi +# Ex mode starts on the last line, so an entry with no address would report the +# end of the file as the tag. Every other path refuses such an entry +if [ -z "${EXCMD}" ]; then + exit 1 +fi + +# The address comes from a tags file, which can be untrusted, and a preview runs +# as soon as an entry is highlighted. Sandboxed as a builtin tag jump is, so that +# it cannot run a shell command or touch a file. ':sandbox' on its own only covers +# the command up to the first '|', so hand the whole address to ':execute' as a +# string literal, doubling the quotes in it +EXCMD_LITERAL=${EXCMD//\'/\'\'} CENTER="$("${VIMNAME}" -R -i NONE -u NONE -e -m -s "${FILE}" \ -c "set nomagic" \ - -c "silent ${EXCMD}" \ + -c "silent sandbox execute '${EXCMD_LITERAL}'" \ -c 'let l=line(".") | new | put =l | print | qa!')" || exit START_LINE="$(( CENTER - FZF_PREVIEW_LINES / 2 ))"