vim/SOURCES/0001-patch-9.2.0847-security-vimball-code-execution-via-..patch
2026-09-10 13:11:06 -04:00

222 lines
7.0 KiB
Diff

diff --git a/runtime/autoload/vimball.vim b/runtime/autoload/vimball.vim
index 9c7dcbd..8171bcd 100644
--- a/runtime/autoload/vimball.vim
+++ b/runtime/autoload/vimball.vim
@@ -268,6 +268,14 @@ fun! vimball#Vimball(really,...)
let filecnt = filecnt + 1
" call Decho("fname<".fname."> fsize=".fsize." filecnt=".filecnt. " fenc=".fenc)
+ if fname =~? '\%(^\|/\)\.VimballRecord$'
+ echomsg "(Vimball) Forbidding .VimballRecord filename, aborting..."
+ exe "tabn ".curtabnr
+ bw! Vimball
+ call s:ChgDir(curdir)
+ return
+ endif
+
if a:really
echomsg "extracted <".fname.">: ".fsize." lines"
else
@@ -306,7 +314,7 @@ fun! vimball#Vimball(really,...)
else
call mkdir(dirname)
endif
- call s:RecordInVar(home,"rmdir('".dirname."')")
+ call s:RecordDirInVar(dirname)
endif
endwhile
endif
@@ -340,7 +348,7 @@ fun! vimball#Vimball(really,...)
exe "silent w! ".fnameescape(fnamepath)
endif
echo "wrote ".fnameescape(fnamepath)
- call s:RecordInVar(home,"call delete('".fnamepath."')")
+ call s:RecordInVar(fnamepath)
endif
" return to tab with vimball
@@ -460,12 +468,17 @@ fun! vimball#RmVimball(...)
endif
let s:VBRstring= substitute(exestring,'call delete(','','g')
let s:VBRstring= substitute(s:VBRstring,"[')]",'','g')
-" call Decho("exe ".exestring)
- sil! keepalt keepjumps exe exestring
- sil! keepalt keepjumps d
- let exestring= strlen(substitute(exestring,'call delete(.\{-})|\=',"D","g"))
-" call Decho("exestring<".exestring.">")
- echomsg "removed ".exestring." files"
+ let nr_files= 0
+ for line in split(exestring, '|')
+ if line !~ '^call delete(''[^'']\{-}''\(,"d"\)\?)$'
+ echomsg "ignoring .VimballRecord entry: " line
+ else
+ sil! keepalt keepjumps exe line
+ let nr_files+= 1
+ endif
+ endfor
+ sil! keepalt keepjumps d
+ echomsg "removed ".nr_files." files"
else
let s:VBRstring= ''
let curfile = substitute(curfile,'\.vmb','','')
@@ -599,21 +612,20 @@ fun! s:ChgDir(newdir)
endfun
" ---------------------------------------------------------------------
-" s:RecordInVar: record a un-vimball command in the .VimballRecord file {{{2
-fun! s:RecordInVar(home,cmd)
-" call Dfunc("RecordInVar(home<".a:home."> cmd<".a:cmd.">)")
- if a:cmd =~ '^rmdir'
-" if !exists("s:recorddir")
-" let s:recorddir= substitute(a:cmd,'^rmdir',"call s:Rmdir",'')
-" else
-" let s:recorddir= s:recorddir."|".substitute(a:cmd,'^rmdir',"call s:Rmdir",'')
-" endif
- elseif !exists("s:recordfile")
- let s:recordfile= a:cmd
- else
- let s:recordfile= s:recordfile."|".a:cmd
+" s:RecordInVar: record a un-vimball file deletion in the .VimballRecord file {{{2
+fun! s:RecordInVar(file)
+ if !exists("s:recordfile")
+ let s:recordfile=[]
+ endif
+ call add(s:recordfile, 'call delete(' . string(a:file) . ')')
+endfun
+
+" s:RecordDirInVar: record a un-vimball dir deletion in the .VimballRecord file {{{2
+fun! s:RecordDirInVar(dir)
+ if !exists("s:recorddir")
+ let s:recorddir = []
endif
-" call Dret("RecordInVar : s:recordfile<".(exists("s:recordfile")? s:recordfile : "")."> s:recorddir<".(exists("s:recorddir")? s:recorddir : "").">")
+ call add(s:recorddir, 'call delete(' . string(a:dir) . ',"d")')
endfun
" ---------------------------------------------------------------------
@@ -637,11 +649,11 @@ fun! s:RecordInFile(home)
setlocal ma
$
if exists("s:recordfile") && exists("s:recorddir")
- let cmd= cmd.s:recordfile."|".s:recorddir
+ let cmd= cmd.join(s:recordfile, '|')."|".join(s:recorddir, '|')
elseif exists("s:recorddir")
- let cmd= cmd.s:recorddir
+ let cmd= cmd.join(s:recorddir, '|')
elseif exists("s:recordfile")
- let cmd= cmd.s:recordfile
+ let cmd= cmd.join(s:recordfile, '|')
else
" call Dret("s:RecordInFile : neither recordfile nor recorddir exist")
return
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 98e8903..7eef9a1 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -214,6 +214,7 @@ NEW_TESTS = \
test_plugin_phpcomplete \
test_plugin_python3complete \
test_plugin_tar \
+ test_plugin_vimball \
test_plus_arg_edit \
test_popup \
test_popupwin \
@@ -457,6 +458,7 @@ NEW_TESTS_RES = \
test_plugin_phpcomplete.res \
test_plugin_python3complete.res \
test_plugin_tar.res \
+ test_plugin_vimball.res \
test_plus_arg_edit.res \
test_popup.res \
test_popupwin.res \
diff --git a/src/testdir/test_plugin_vimball.vim b/src/testdir/test_plugin_vimball.vim
new file mode 100644
index 0000000..0e2ee28
--- /dev/null
+++ b/src/testdir/test_plugin_vimball.vim
@@ -0,0 +1,84 @@
+" Test for the vimball plugin
+
+let s:testdir = fnamemodify(resolve(expand("%:p")), ":h")
+let s:default_vimball = ['" Vimball Archiver by Charles E. Campbell',
+ \ 'UseVimball',
+ \ 'finish',
+ \ 'XVimball/Xtest.txt [[[1',
+ \ '2',
+ \ 'Hello Vimball',
+ \ '123']
+
+func SetUp()
+ ru plugin/vimballPlugin.vim
+ let g:vimball_home = s:testdir
+endfunc
+
+func TearDown()
+ call delete('Xtest.vmb')
+ call delete('.VimballRecord')
+endfunc
+
+func s:setup()
+ call mkdir('XVimball', 'p')
+ call writefile(['Hello Vimball', '123'], 'XVimball/Xtest.txt')
+endfunc
+
+func s:teardown()
+ call delete('XVimball', 'rf')
+ call delete('Xtest.vmb')
+ bw! Xtest.vmb
+ bw! XVimball/Xtest.txt
+ if bufloaded('.VimballRecord')
+ bw! .VimballRecord
+ endif
+endfunc
+
+func s:Mkvimball()
+ call s:setup()
+ new
+ 0put ='XVimball/Xtest.txt'
+ $d
+ 1,1MkVimball! Xtest
+ bw!
+endfunc
+
+func Test_vimball_basic()
+ call s:Mkvimball()
+ call assert_true(filereadable('Xtest.vmb'), 'vimball file should be created')
+ call assert_equal(s:default_vimball, readfile('Xtest.vmb'))
+
+ call delete('XVimball', 'rf')
+ sp Xtest.vmb
+ let mess = execute(':mess')
+ call assert_match('\*\*\*vimball\*\*\* Source this file to extract it!', mess)
+ so %
+ call feedkeys("\<cr>", "t")
+ unlet mess
+ let mess = execute(':mess')->split('\n')
+ call assert_equal('extracted <XVimball/Xtest.txt>: 2 lines', mess[-2])
+
+ call assert_true(filereadable('XVimball/Xtest.txt'), 'extracted file should exist')
+ call assert_equal(['Hello Vimball', '123'], readfile('XVimball/Xtest.txt'))
+
+ " Vimball extraction has been recorded
+ call assert_true(filereadable('.VimballRecord'))
+ let record = readfile('.VimballRecord')
+ call assert_equal(1, record->len())
+ call assert_match('^Xtest.vmb: call delete(''.\{-}'')|call delete(''.\{-}'',"d")$', record[0])
+ call s:teardown()
+endfunc
+
+func Test_vimball_VimballRecord_filenames()
+ call s:Mkvimball()
+ call delete('XVimball', 'rf')
+ sp Xtest.vmb
+ 4s#.*\ze\t#.VimballRecord#
+ so %
+ call feedkeys("\<cr>", "it")
+
+ let mess = execute(':mess')->split('\n')[-1]
+ call assert_match('(Vimball) Forbidding .VimballRecord filename.* aborting\.\.\.', mess)
+ call assert_false(filereadable('.VimballRecord'))
+ call s:teardown()
+endfunc