]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1552: [security]: path traversal issue in tar.vim v9.1.1552
authorChristian Brabandt <cb@256bit.org>
Tue, 15 Jul 2025 19:54:00 +0000 (21:54 +0200)
committerChristian Brabandt <cb@256bit.org>
Tue, 15 Jul 2025 19:58:08 +0000 (21:58 +0200)
Problem:  [security]: path traversal issue in tar.vim
          (@ax)
Solution: warn the user for such things, drop leading /, don't
          forcefully overwrite files when writing temporary files,
          refactor autoload/tar.vim

tar.vim: drop leading / in path names

A tar archive containing files with leading `/` may cause confusions as
to where the content is extracted.  Let's make sure we drop the leading
`/` and use a relative path instead.

Also while at it, had to refactor it quite a bit and increase the
minimum supported Vim version to v9. Also add a test for some basic tar
functionality

closes: #17733

Filelist
runtime/autoload/tar.vim
runtime/doc/pi_tar.txt
runtime/doc/tags
runtime/plugin/tarPlugin.vim
src/po/vim.pot
src/testdir/Make_all.mak
src/testdir/samples/evil.tar [new file with mode: 0644]
src/testdir/samples/sample.tar [new file with mode: 0644]
src/testdir/test_plugin_tar.vim [new file with mode: 0644]
src/version.c

index 3c9f783018f5a7a6f0f644766b1b4aa952d8dc3c..41eba31075bde6a8df9ca90a61338210e8a84e41 100644 (file)
--- a/Filelist
+++ b/Filelist
@@ -213,7 +213,9 @@ SRC_ALL =   \
                src/testdir/samples/*.txt \
                src/testdir/samples/*.vim \
                src/testdir/samples/evil.zip \
+               src/testdir/samples/evil.tar \
                src/testdir/samples/poc.zip \
+               src/testdir/samples/sample.tar \
                src/testdir/samples/test.zip \
                src/testdir/samples/test000 \
                src/testdir/samples/test_undo.txt.undo \
index 7c1cefa63eaad72fcbd5c0ea34e1df3fd8145169..1a0d4f8a3d28e407dbe971fadbf6c3b218e44e07 100644 (file)
@@ -16,6 +16,7 @@
 "                               instead of shelling out to file(1)
 "   2025 Apr 16 by Vim Project: decouple from netrw by adding s:WinPath()
 "   2025 May 19 by Vim Project: restore working directory after read/write
+"   2025 Jul 13 by Vim Project: warn with path traversal attacks
 "
 "      Contains many ideas from Michael Toren's <tar.vim>
 "
@@ -34,9 +35,9 @@ if &cp || exists("g:loaded_tar")
  finish
 endif
 let g:loaded_tar= "v32b"
-if v:version < 702
+if v:version < 900
  echohl WarningMsg
- echo "***warning*** this version of tar needs vim 7.2"
+ echo "***warning*** this version of tar needs vim 9.0"
  echohl Normal
  finish
 endif
@@ -46,10 +47,10 @@ set cpo&vim
 " ---------------------------------------------------------------------
 "  Default Settings: {{{1
 if !exists("g:tar_browseoptions")
- let g:tar_browseoptions= "Ptf"
+ let g:tar_browseoptions= "tf"
 endif
 if !exists("g:tar_readoptions")
- let g:tar_readoptions= "pPxf"
+ let g:tar_readoptions= "pxf"
 endif
 if !exists("g:tar_cmd")
  let g:tar_cmd= "tar"
@@ -58,6 +59,7 @@ if !exists("g:tar_writeoptions")
  let g:tar_writeoptions= "uf"
 endif
 if !exists("g:tar_delfile")
+ " Note: not supported on BSD
  let g:tar_delfile="--delete -f"
 endif
 if !exists("g:netrw_cygwin")
@@ -106,10 +108,26 @@ if !exists("g:tar_shq")
  endif
 endif
 
+let g:tar_secure=' -- '
+let g:tar_leading_pat='^\%([.]\{,2\}/\)\+'
+
 " ----------------
 "  Functions: {{{1
 " ----------------
 
+" ---------------------------------------------------------------------
+" s:Msg: {{{2
+fun! s:Msg(func, severity, msg)
+  redraw!
+  if a:severity =~? 'error'
+    echohl Error 
+  else
+    echohl WarningMsg
+  endif
+  echo $"***{a:severity}*** ({a:func}) {a:msg}"
+  echohl None
+endfunc
+
 " ---------------------------------------------------------------------
 " tar#Browse: {{{2
 fun! tar#Browse(tarfile)
@@ -118,16 +136,14 @@ fun! tar#Browse(tarfile)
 
   " sanity checks
   if !executable(g:tar_cmd)
-   redraw!
-   echohl Error | echo '***error*** (tar#Browse) "'.g:tar_cmd.'" not available on your system'
+   call s:Msg('tar#Browse', 'error', $"{g:tar_cmd} not available on your system")
    let &report= repkeep
    return
   endif
   if !filereadable(a:tarfile)
    if a:tarfile !~# '^\a\+://'
     " if it's an url, don't complain, let url-handlers such as vim do its thing
-    redraw!
-    echohl Error | echo "***error*** (tar#Browse) File not readable<".a:tarfile.">" | echohl None
+    call s:Msg('tar#Browse', 'error', $"File not readable<{a:tarfile}>")
    endif
    let &report= repkeep
    return
@@ -203,28 +219,18 @@ fun! tar#Browse(tarfile)
    exe "sil! r! ".g:tar_cmd." -".g:tar_browseoptions." ".shellescape(tarfile,1)
   endif
   if v:shell_error != 0
-   redraw!
-   echohl WarningMsg | echo "***warning*** (tar#Browse) please check your g:tar_browseoptions<".g:tar_browseoptions.">"
+   call s:Msg('tar#Browse', 'warning', $"please check your g:tar_browseoptions '<{g:tar_browseoptions}>'")
    return
   endif
-  "
-  " The following should not be neccessary, since in case of errors the
-  " previous if statement should have caught the problem (because tar exited
-  " with a non-zero exit code).
-  " if line("$") == curlast || ( line("$") == (curlast + 1) &&
-  "       \ getline("$") =~# '\c\<\%(warning\|error\|inappropriate\|unrecognized\)\>' &&
-  "       \ getline("$") =~  '\s' )
-  "  redraw!
-  "  echohl WarningMsg | echo "***warning*** (tar#Browse) ".a:tarfile." doesn't appear to be a tar file" | echohl None
-  "  keepj sil! %d
-  "  let eikeep= &ei
-  "  set ei=BufReadCmd,FileReadCmd
-  "  exe "r ".fnameescape(a:tarfile)
-  "  let &ei= eikeep
-  "  keepj sil! 1d
-  "   call Dret("tar#Browse : a:tarfile<".a:tarfile.">")
-  "  return
-  " endif
+
+  " remove tar: Removing leading '/' from member names
+  " Note: the message could be localized
+  if search('^tar: ') > 0 || search(g:tar_leading_pat) > 0
+    call append(3,'" Note: Path Traversal Attack detected!')
+    let b:leading_slash = 1
+    " remove the message output
+    sil g/^tar: /d
+  endif
 
   " set up maps supported for tar
   setlocal noma nomod ro
@@ -243,12 +249,7 @@ fun! s:TarBrowseSelect()
   let repkeep= &report
   set report=10
   let fname= getline(".")
-
-  if !exists("g:tar_secure") && fname =~ '^\s*-\|\s\+-'
-   redraw!
-   echohl WarningMsg | echo '***warning*** (tar#BrowseSelect) rejecting tarfile member<'.fname.'> because of embedded "-"'
-   return
-  endif
+  let ls= get(b:, 'leading_slash', 0)
 
   " sanity check
   if fname =~ '^"'
@@ -270,7 +271,8 @@ fun! s:TarBrowseSelect()
    wincmd _
   endif
   let s:tblfile_{winnr()}= curfile
-  call tar#Read("tarfile:".tarfile.'::'.fname,1)
+  let b:leading_slash= ls
+  call tar#Read("tarfile:".tarfile.'::'.fname)
   filetype detect
   set nomod
   exe 'com! -buffer -nargs=? -complete=file TarDiff    :call tar#Diff(<q-args>,"'.fnameescape(fname).'")'
@@ -280,26 +282,18 @@ endfun
 
 " ---------------------------------------------------------------------
 " tar#Read: {{{2
-fun! tar#Read(fname,mode)
+fun! tar#Read(fname)
   let repkeep= &report
   set report=10
   let tarfile = substitute(a:fname,'tarfile:\(.\{-}\)::.*$','\1','')
   let fname   = substitute(a:fname,'tarfile:.\{-}::\(.*\)$','\1','')
   " be careful not to execute special crafted files
-  let escape_file = fname->fnameescape()
-
-  " changing the directory to the temporary earlier to allow tar to extract the file with permissions intact
-  if !exists("*mkdir")
-   redraw!
-   echohl Error | echo "***error*** (tar#Write) sorry, mkdir() doesn't work on your system" | echohl None
-   let &report= repkeep
-   return
-  endif
+  let escape_file = fname->substitute(g:tar_leading_pat, '', '')->fnameescape()
 
   let curdir= getcwd()
+  let b:curdir= curdir
   let tmpdir= tempname()
-  let b:curdir= tmpdir
-  let b:tmpdir= curdir
+  let b:tmpdir= tmpdir
   if tmpdir =~ '\.'
    let tmpdir= substitute(tmpdir,'\.[^.]*$','','e')
   endif
@@ -309,8 +303,7 @@ fun! tar#Read(fname,mode)
   try
    exe "lcd ".fnameescape(tmpdir)
   catch /^Vim\%((\a\+)\)\=:E344/
-   redraw!
-   echohl Error | echo "***error*** (tar#Write) cannot lcd to temporary directory" | Echohl None
+   call s:Msg('tar#Read', 'error', "cannot lcd to temporary directory")
    let &report= repkeep
    return
   endtry
@@ -333,7 +326,7 @@ fun! tar#Read(fname,mode)
   elseif  fname =~ '\.bz3$' && executable("bz3cat")
    let decmp= "|bz3cat"
    let doro = 1
-  elseif      fname =~ '\.t\=gz$'  && executable("zcat")
+  elseif  fname =~ '\.t\=gz$'  && executable("zcat")
    let decmp= "|zcat"
    let doro = 1
   elseif  fname =~ '\.lzma$' && executable("lzcat")
@@ -356,68 +349,66 @@ fun! tar#Read(fname,mode)
    endif
   endif
 
-  if exists("g:tar_secure")
-   let tar_secure= " -- "
-  else
-   let tar_secure= " "
-  endif
 
   if tarfile =~# '\.bz2$'
-   exe "sil! r! bzip2 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp
+   exe "sil! r! bzip2 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp
    exe "read ".escape_file
   elseif tarfile =~# '\.bz3$'
-   exe "sil! r! bzip3 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp
+   exe "sil! r! bzip3 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp
    exe "read ".escape_file
   elseif tarfile =~# '\.\(gz\)$'
-   exe "sil! r! gzip -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp
+   exe "sil! r! gzip -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp
    exe "read ".escape_file
   elseif tarfile =~# '\(\.tgz\|\.tbz\|\.txz\)'
    let filekind= s:Header(tarfile)
    if filekind =~? "bzip2"
-    exe "sil! r! bzip2 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp
+    exe "sil! r! bzip2 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp
     exe "read ".escape_file
    elseif filekind =~ "bzip3"
-    exe "sil! r! bzip3 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp
+    exe "sil! r! bzip3 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp
     exe "read ".escape_file
    elseif filekind =~? "xz"
-    exe "sil! r! xz -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp
+    exe "sil! r! xz -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp
     exe "read ".escape_file
    elseif filekind =~? "zstd"
-    exe "sil! r! zstd --decompress --stdout -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp
+    exe "sil! r! zstd --decompress --stdout -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp
     exe "read ".escape_file
    elseif filekind =~? "gzip"
-    exe "sil! r! gzip -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp
+    exe "sil! r! gzip -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp
     exe "read ".escape_file
    endif
 
   elseif tarfile =~# '\.lrp$'
-   exe "sil! r! cat -- ".shellescape(tarfile,1)." | gzip -d -c - | ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp
+   exe "sil! r! cat -- ".shellescape(tarfile,1)." | gzip -d -c - | ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp
    exe "read ".escape_file
   elseif tarfile =~# '\.lzma$'
-   exe "sil! r! lzma -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp
+   exe "sil! r! lzma -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp
    exe "read ".escape_file
   elseif tarfile =~# '\.\(xz\|txz\)$'
-   exe "sil! r! xz --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp
+   exe "sil! r! xz --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp
    exe "read ".escape_file
   elseif tarfile =~# '\.\(lz4\|tlz4\)$'
-   exe "sil! r! lz4 --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp
+   exe "sil! r! lz4 --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp
    exe "read ".escape_file
   else
    if tarfile =~ '^\s*-'
     " A file name starting with a dash is taken as an option.  Prepend ./ to avoid that.
     let tarfile = substitute(tarfile, '-', './-', '')
    endif
-   exe "silent r! ".g:tar_cmd." -".g:tar_readoptions.shellescape(tarfile,1)." ".tar_secure.shellescape(fname,1).decmp
+   exe "silent r! ".g:tar_cmd." -".g:tar_readoptions.shellescape(tarfile,1)." ".g:tar_secure.shellescape(fname,1).decmp
    exe "read ".escape_file
   endif
+  if get(b:, 'leading_slash', 0)
+    sil g/^tar: /d
+  endif
 
    redraw!
 
-if v:shell_error != 0
+  if v:shell_error != 0
    lcd ..
    call s:Rmdir("_ZIPVIM_")
    exe "lcd ".fnameescape(curdir)
-   echohl Error | echo "***error*** (tar#Read) sorry, unable to open or extract ".tarfile." with ".fname | echohl None
+   call s:Msg('tar#Read', 'error', $"sorry, unable to open or extract {tarfile} with {fname}")
   endif
 
   if doro
@@ -426,7 +417,6 @@ if v:shell_error != 0
   endif
 
   let b:tarfile= a:fname
-  exe "file tarfile::".fnameescape(fname)
 
   " cleanup
   keepj sil! 0d
@@ -434,7 +424,7 @@ if v:shell_error != 0
 
   let &report= repkeep
   exe "lcd ".fnameescape(curdir)
-  silent exe "file tarfile::".escape_file
+  silent exe "file tarfile::". fname->fnameescape()
 endfun
 
 " ---------------------------------------------------------------------
@@ -446,22 +436,35 @@ fun! tar#Write(fname)
   let curdir= b:curdir
   let tmpdir= b:tmpdir
 
-  if !exists("g:tar_secure") && a:fname =~ '^\s*-\|\s\+-'
-   redraw!
-   echohl WarningMsg | echo '***warning*** (tar#Write) rejecting tarfile member<'.a:fname.'> because of embedded "-"'
-   return
-  endif
-
   " sanity checks
   if !executable(g:tar_cmd)
    redraw!
    let &report= repkeep
    return
   endif
-
   let tarfile = substitute(b:tarfile,'tarfile:\(.\{-}\)::.*$','\1','')
   let fname   = substitute(b:tarfile,'tarfile:.\{-}::\(.*\)$','\1','')
 
+  if get(b:, 'leading_slash', 0)
+   call s:Msg('tar#Write', 'error', $"sorry, not attempting to update {tarfile} with {fname}")
+   let &report= repkeep
+   return
+  endif
+
+  if !isdirectory(fnameescape(tmpdir))
+    call mkdir(fnameescape(tmpdir), 'p')
+  endif
+  exe $"lcd {fnameescape(tmpdir)}"
+  if isdirectory("_ZIPVIM_")
+    call s:Rmdir("_ZIPVIM_")
+  endif
+  call mkdir("_ZIPVIM_")
+  lcd _ZIPVIM_
+  let dir = fnamemodify(fname, ':p:h')
+  if dir !~# '_ZIPVIM_$'
+    call mkdir(dir)
+  endif
+
   " handle compressed archives
   if tarfile =~# '\.bz2'
    call system("bzip2 -d -- ".shellescape(tarfile,0))
@@ -500,8 +503,7 @@ fun! tar#Write(fname)
   " Note: no support for name.tar.tbz/.txz/.tgz/.tlz4/.tzst
 
   if v:shell_error != 0
-   redraw!
-   echohl Error | echo "***error*** (tar#Write) sorry, unable to update ".tarfile." with ".fname | echohl None
+   call s:Msg('tar#Write', 'error', $"sorry, unable to update {tarfile} with {fname}")
   else
 
    if fname =~ '/'
@@ -519,28 +521,22 @@ fun! tar#Write(fname)
     let tarfile = substitute(tarfile, '-', './-', '')
    endif
 
-   if exists("g:tar_secure")
-    let tar_secure= " -- "
-   else
-    let tar_secure= " "
-   endif
-   exe "w! ".fnameescape(fname)
+   " don't overwrite a file forcefully
+   exe "w ".fnameescape(fname)
    if has("win32unix") && executable("cygpath")
     let tarfile = substitute(system("cygpath ".shellescape(tarfile,0)),'\n','','e')
    endif
 
    " delete old file from tarfile
-   call system(g:tar_cmd." ".g:tar_delfile." ".shellescape(tarfile,0).tar_secure.shellescape(fname,0))
+   " Note: BSD tar does not support --delete flag
+   call system(g:tar_cmd." ".g:tar_delfile." ".shellescape(tarfile,0).g:tar_secure.shellescape(fname,0))
    if v:shell_error != 0
-    redraw!
-    echohl Error | echo "***error*** (tar#Write) sorry, unable to update ".fnameescape(tarfile)." with ".fnameescape(fname) | echohl None
+    call s:Msg('tar#Write', 'error', $"sorry, unable to update {fnameescape(tarfile)} with {fnameescape(fname)} --delete not supported?")
    else
-
     " update tarfile with new file
-    call system(g:tar_cmd." -".g:tar_writeoptions." ".shellescape(tarfile,0).tar_secure.shellescape(fname,0))
+    call system(g:tar_cmd." -".g:tar_writeoptions." ".shellescape(tarfile,0).g:tar_secure.shellescape(fname,0))
     if v:shell_error != 0
-     redraw!
-     echohl Error | echo "***error*** (tar#Write) sorry, unable to update ".fnameescape(tarfile)." with ".fnameescape(fname) | echohl None
+     call s:Msg('tar#Write', 'error', $"sorry, unable to update {fnameescape(tarfile)} with {fnameescape(fname)}")
     elseif exists("compress")
      call system(compress)
      if exists("tgz")
@@ -581,6 +577,7 @@ fun! tar#Diff(userfname,fname)
   if a:userfname != ""
    let fname= a:userfname
   endif
+  exe "lcd ".fnameescape(b:tmpdir). '/_ZIPVIM_'
   if filereadable(fname)
    " sets current file (from tarball) for diff'ing
    " splits window vertically
@@ -604,12 +601,6 @@ fun! tar#Extract()
   set report=10
   let fname= getline(".")
 
-  if !exists("g:tar_secure") && fname =~ '^\s*-\|\s\+-'
-   redraw!
-   echohl WarningMsg | echo '***warning*** (tar#BrowseSelect) rejecting tarfile member<'.fname.'> because of embedded "-"'
-   return
-  endif
-
   " sanity check
   if fname =~ '^"'
    let &report= repkeep
@@ -623,16 +614,16 @@ fun! tar#Extract()
   if filereadable(tarbase.".tar")
    call system(extractcmd." ".shellescape(tarbase).".tar ".shellescape(fname))
    if v:shell_error != 0
-    echohl Error | echo "***error*** ".extractcmd." ".tarbase.".tar ".fname.": failed!" | echohl NONE
+    call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar {fname}: failed!")
    else
-    echo "***note*** successfully extracted ".fname
+    echo "***note*** successfully extracted ". fname
    endif
 
   elseif filereadable(tarbase.".tgz")
    let extractcmd= substitute(extractcmd,"-","-z","")
    call system(extractcmd." ".shellescape(tarbase).".tgz ".shellescape(fname))
    if v:shell_error != 0
-    echohl Error | echo "***error*** ".extractcmd." ".tarbase.".tgz ".fname.": failed!" | echohl NONE
+    call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tgz {fname}: failed!")
    else
     echo "***note*** successfully extracted ".fname
    endif
@@ -641,7 +632,7 @@ fun! tar#Extract()
    let extractcmd= substitute(extractcmd,"-","-z","")
    call system(extractcmd." ".shellescape(tarbase).".tar.gz ".shellescape(fname))
    if v:shell_error != 0
-    echohl Error | echo "***error*** ".extractcmd." ".tarbase.".tar.gz ".fname.": failed!" | echohl NONE
+    call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.gz {fname}: failed!")
    else
     echo "***note*** successfully extracted ".fname
    endif
@@ -650,7 +641,7 @@ fun! tar#Extract()
    let extractcmd= substitute(extractcmd,"-","-j","")
    call system(extractcmd." ".shellescape(tarbase).".tbz ".shellescape(fname))
    if v:shell_error != 0
-    echohl Error | echo "***error*** ".extractcmd."j ".tarbase.".tbz ".fname.": failed!" | echohl NONE
+    call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tbz {fname}: failed!")
    else
     echo "***note*** successfully extracted ".fname
    endif
@@ -659,7 +650,7 @@ fun! tar#Extract()
    let extractcmd= substitute(extractcmd,"-","-j","")
    call system(extractcmd." ".shellescape(tarbase).".tar.bz2 ".shellescape(fname))
    if v:shell_error != 0
-    echohl Error | echo "***error*** ".extractcmd."j ".tarbase.".tar.bz2 ".fname.": failed!" | echohl NONE
+    call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.bz2 {fname}: failed!")
    else
     echo "***note*** successfully extracted ".fname
    endif
@@ -668,7 +659,7 @@ fun! tar#Extract()
    let extractcmd= substitute(extractcmd,"-","-j","")
    call system(extractcmd." ".shellescape(tarbase).".tar.bz3 ".shellescape(fname))
    if v:shell_error != 0
-    echohl Error | echo "***error*** ".extractcmd."j ".tarbase.".tar.bz3 ".fname.": failed!" | echohl NONE
+    call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.bz3 {fname}: failed!")
    else
     echo "***note*** successfully extracted ".fname
    endif
@@ -677,7 +668,7 @@ fun! tar#Extract()
    let extractcmd= substitute(extractcmd,"-","-J","")
    call system(extractcmd." ".shellescape(tarbase).".txz ".shellescape(fname))
    if v:shell_error != 0
-    echohl Error | echo "***error*** ".extractcmd." ".tarbase.".txz ".fname.": failed!" | echohl NONE
+    call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.txz {fname}: failed!")
    else
     echo "***note*** successfully extracted ".fname
    endif
@@ -686,7 +677,7 @@ fun! tar#Extract()
    let extractcmd= substitute(extractcmd,"-","-J","")
    call system(extractcmd." ".shellescape(tarbase).".tar.xz ".shellescape(fname))
    if v:shell_error != 0
-    echohl Error | echo "***error*** ".extractcmd." ".tarbase.".tar.xz ".fname.": failed!" | echohl NONE
+    call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.xz {fname}: failed!")
    else
     echo "***note*** successfully extracted ".fname
    endif
@@ -695,7 +686,7 @@ fun! tar#Extract()
    let extractcmd= substitute(extractcmd,"-","--zstd","")
    call system(extractcmd." ".shellescape(tarbase).".tzst ".shellescape(fname))
    if v:shell_error != 0
-    echohl Error | echo "***error*** ".extractcmd." ".tarbase.".tzst ".fname.": failed!" | echohl NONE
+    call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tzst {fname}: failed!")
    else
     echo "***note*** successfully extracted ".fname
    endif
@@ -704,7 +695,7 @@ fun! tar#Extract()
    let extractcmd= substitute(extractcmd,"-","--zstd","")
    call system(extractcmd." ".shellescape(tarbase).".tar.zst ".shellescape(fname))
    if v:shell_error != 0
-    echohl Error | echo "***error*** ".extractcmd." ".tarbase.".tar.zst ".fname.": failed!" | echohl NONE
+    call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.zst {fname}: failed!")
    else
     echo "***note*** successfully extracted ".fname
    endif
@@ -713,7 +704,7 @@ fun! tar#Extract()
    let extractcmd= substitute(extractcmd,"-","-I lz4","")
    call system(extractcmd." ".shellescape(tarbase).".tlz4 ".shellescape(fname))
    if v:shell_error != 0
-    echohl Error | echo "***error*** ".extractcmd." ".tarbase.".tlz4 ".fname.": failed!" | echohl NONE
+    call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tlz4 {fname}: failed!")
    else
     echo "***note*** successfully extracted ".fname
    endif
@@ -722,7 +713,7 @@ fun! tar#Extract()
    let extractcmd= substitute(extractcmd,"-","-I lz4","")
    call system(extractcmd." ".shellescape(tarbase).".tar.lz4".shellescape(fname))
    if v:shell_error != 0
-    echohl Error | echo "***error*** ".extractcmd." ".tarbase.".tar.lz4 ".fname.": failed!" | echohl NONE
+    call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.lz4 {fname}: failed!")
    else
     echo "***note*** successfully extracted ".fname
    endif
@@ -735,15 +726,7 @@ endfun
 " ---------------------------------------------------------------------
 " s:Rmdir: {{{2
 fun! s:Rmdir(fname)
-  if has("unix")
-   call system("/bin/rm -rf -- ".shellescape(a:fname,0))
-  elseif has("win32") || has("win95") || has("win64") || has("win16")
-   if &shell =~? "sh$"
-    call system("/bin/rm -rf -- ".shellescape(a:fname,0))
-   else
-    call system("del /S ".shellescape(a:fname,0))
-   endif
-  endif
+  call delete(a:fname, 'rf')
 endfun
 
 " s:FileHeader: {{{2
index 6d49928dcbc85f540ea168fcc06f626bf8b33bda..52706e93efe1e6c02aa0962c49fd73d5bfdc6910 100644 (file)
@@ -1,11 +1,10 @@
-*pi_tar.txt*   For Vim version 9.1.  Last change: 2025 Mar 16
+*pi_tar.txt*   For Vim version 9.1.  Last change: 2025 Jul 15
 
                       +====================+
                       | Tar File Interface |
                       +====================+
 
-Author:  Charles E. Campbell  <NcampObell@SdrPchip.AorgM-NOSPAM>
-         (remove NOSPAM from Campbell's email first)
+Original Author:  Charles E. Campbell
 Copyright 2005-2017:                                   *tar-copyright*
        The VIM LICENSE (see |copyright|) applies to the files in this
        package, including tarPlugin.vim, tar.vim, and pi_tar.txt.  Like
@@ -61,7 +60,7 @@ Copyright 2005-2017:                                  *tar-copyright*
        the file mentioned in the tarball.  If the current directory is not
        correct for that path, :TarDiff will fail to find the associated file.
 
-       If the [filename] is given, that that filename (and path) will be used
+       If the [filename] is given, that filename (and path) will be used
        to specify the associated file.
 
 
@@ -95,24 +94,25 @@ Copyright 2005-2017:                                        *tar-copyright*
    *g:tar_readoptions*   "OPxf"  used to extract a file from a tarball
    *g:tar_cmd*           "tar"   the name of the tar program
    *g:tar_nomax*           0     if true, file window will not be maximized
-   *g:tar_secure*        undef   if exists:
-                                       "--"s will be used to prevent unwanted
-                                       option expansion in tar commands.
-                                       Please be sure that your tar command
-                                       accepts "--"; Posix compliant tar
-                                       utilities do accept them.
-                                 if not exists:
-                                       The tar plugin will reject any tar
-                                       files or member files that begin with
-                                       "-"
-                                 Not all tar's support the "--" which is why
-                                 it isn't default.
    *g:tar_writeoptions*          "uf"    used to update/replace a file
 
 
 ==============================================================================
 4. History                                             *tar-history*
 
+       unreleased:
+               Jul 13, 2025    * drop leading /
+               May 19, 2025    * restore working directory after read/write
+               Apr 16, 2025    * decouple from netrw by adding s:WinPath()
+                               instead of shelling out to file(1)
+               Mar 02, 2025    * determine the compression using readblob()
+               Mar 02, 2025    * escape the filename before using :read
+               Mar 01, 2025    * fix syntax error in tar#Read()
+               Feb 28, 2025    * add support for bzip3 (#16755)
+               Feb 06, 2025    * add support for lz4 (#16591)
+               Nov 11, 2024    * support permissions (#7379)
+               Feb 19, 2024    * announce adoption
+               Jan 08, 2024    * fix a few problems (#138331, #12637, #8109)
        v31     Apr 02, 2017    * (klartext) reported that browsing encrypted
                                  files in a zip archive created unencrypted
                                  swap files.  I am applying a similar fix
index 384b27aa6257a9440653d803cdebc534de567c50..8251c73d412583e54c0dcc33d50ed8f54102d1a6 100644 (file)
@@ -7857,7 +7857,6 @@ g:tar_copycmd     pi_tar.txt      /*g:tar_copycmd*
 g:tar_extractcmd       pi_tar.txt      /*g:tar_extractcmd*
 g:tar_nomax    pi_tar.txt      /*g:tar_nomax*
 g:tar_readoptions      pi_tar.txt      /*g:tar_readoptions*
-g:tar_secure   pi_tar.txt      /*g:tar_secure*
 g:tar_writeoptions     pi_tar.txt      /*g:tar_writeoptions*
 g:termdebug_config     terminal.txt    /*g:termdebug_config*
 g:termdebugger terminal.txt    /*g:termdebugger*
index 825b7ae17fb4f815d942b537aba72b8b9e381c32..e55a367854857c29d62ba84d4c160031c9b0b6a6 100644 (file)
@@ -23,14 +23,14 @@ set cpo&vim
 "  Public Interface: {{{1
 augroup tar
   au!
-  au BufReadCmd   tarfile::*   call tar#Read(expand("<amatch>"), 1)
-  au FileReadCmd  tarfile::*   call tar#Read(expand("<amatch>"), 0)
+  au BufReadCmd   tarfile::*   call tar#Read(expand("<amatch>"))
+  au FileReadCmd  tarfile::*   call tar#Read(expand("<amatch>"))
   au BufWriteCmd  tarfile::*   call tar#Write(expand("<amatch>"))
   au FileWriteCmd tarfile::*   call tar#Write(expand("<amatch>"))
 
   if has("unix")
-   au BufReadCmd   tarfile::*/*        call tar#Read(expand("<amatch>"), 1)
-   au FileReadCmd  tarfile::*/*        call tar#Read(expand("<amatch>"), 0)
+   au BufReadCmd   tarfile::*/*        call tar#Read(expand("<amatch>"))
+   au FileReadCmd  tarfile::*/*        call tar#Read(expand("<amatch>"))
    au BufWriteCmd  tarfile::*/*        call tar#Write(expand("<amatch>"))
    au FileWriteCmd tarfile::*/*        call tar#Write(expand("<amatch>"))
   endif
index 7d28c12df7a4eec788e9ac77323816b957507cd1..4a9a20bcde002a2d4366b2f38b6f0b5fd9d1165c 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-07-15 21:42+0200\n"
+"POT-Creation-Date: 2025-07-15 21:50+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -4257,327 +4257,327 @@ msgstr ""
 msgid "%s (%s, compiled %s)"
 msgstr ""
 
-#: ../version.c:4036
+#: ../version.c:4038
 msgid ""
 "\n"
 "MS-Windows ARM64 GUI/console version"
 msgstr ""
 
-#: ../version.c:4038
+#: ../version.c:4040
 msgid ""
 "\n"
 "MS-Windows 64-bit GUI/console version"
 msgstr ""
 
-#: ../version.c:4041
+#: ../version.c:4043
 msgid ""
 "\n"
 "MS-Windows 32-bit GUI/console version"
 msgstr ""
 
-#: ../version.c:4046
+#: ../version.c:4048
 msgid ""
 "\n"
 "MS-Windows ARM64 GUI version"
 msgstr ""
 
-#: ../version.c:4048
+#: ../version.c:4050
 msgid ""
 "\n"
 "MS-Windows 64-bit GUI version"
 msgstr ""
 
-#: ../version.c:4051
+#: ../version.c:4053
 msgid ""
 "\n"
 "MS-Windows 32-bit GUI version"
 msgstr ""
 
-#: ../version.c:4055
+#: ../version.c:4057
 msgid " with OLE support"
 msgstr ""
 
-#: ../version.c:4060
+#: ../version.c:4062
 msgid ""
 "\n"
 "MS-Windows ARM64 console version"
 msgstr ""
 
-#: ../version.c:4062
+#: ../version.c:4064
 msgid ""
 "\n"
 "MS-Windows 64-bit console version"
 msgstr ""
 
-#: ../version.c:4065
+#: ../version.c:4067
 msgid ""
 "\n"
 "MS-Windows 32-bit console version"
 msgstr ""
 
-#: ../version.c:4071
+#: ../version.c:4073
 msgid ""
 "\n"
 "macOS version"
 msgstr ""
 
-#: ../version.c:4073
+#: ../version.c:4075
 msgid ""
 "\n"
 "macOS version w/o darwin feat."
 msgstr ""
 
-#: ../version.c:4083
+#: ../version.c:4085
 msgid ""
 "\n"
 "OpenVMS version"
 msgstr ""
 
-#: ../version.c:4098
+#: ../version.c:4100
 msgid ""
 "\n"
 "Included patches: "
 msgstr ""
 
-#: ../version.c:4123
+#: ../version.c:4125
 msgid ""
 "\n"
 "Extra patches: "
 msgstr ""
 
-#: ../version.c:4135 ../version.c:4446
+#: ../version.c:4137 ../version.c:4448
 msgid "Modified by "
 msgstr ""
 
-#: ../version.c:4142
+#: ../version.c:4144
 msgid ""
 "\n"
 "Compiled "
 msgstr ""
 
-#: ../version.c:4145
+#: ../version.c:4147
 msgid "by "
 msgstr ""
 
-#: ../version.c:4157
+#: ../version.c:4159
 msgid ""
 "\n"
 "Huge version "
 msgstr ""
 
-#: ../version.c:4159
+#: ../version.c:4161
 msgid ""
 "\n"
 "Normal version "
 msgstr ""
 
-#: ../version.c:4161
+#: ../version.c:4163
 msgid ""
 "\n"
 "Tiny version "
 msgstr ""
 
-#: ../version.c:4164
+#: ../version.c:4166
 msgid "without GUI."
 msgstr ""
 
-#: ../version.c:4167
+#: ../version.c:4169
 msgid "with GTK3 GUI."
 msgstr ""
 
-#: ../version.c:4169
+#: ../version.c:4171
 msgid "with GTK2-GNOME GUI."
 msgstr ""
 
-#: ../version.c:4171
+#: ../version.c:4173
 msgid "with GTK2 GUI."
 msgstr ""
 
-#: ../version.c:4174
+#: ../version.c:4176
 msgid "with X11-Motif GUI."
 msgstr ""
 
-#: ../version.c:4176
+#: ../version.c:4178
 msgid "with Haiku GUI."
 msgstr ""
 
-#: ../version.c:4178
+#: ../version.c:4180
 msgid "with Photon GUI."
 msgstr ""
 
-#: ../version.c:4180
+#: ../version.c:4182
 msgid "with GUI."
 msgstr ""
 
-#: ../version.c:4182
+#: ../version.c:4184
 msgid "  Features included (+) or not (-):\n"
 msgstr ""
 
-#: ../version.c:4189
+#: ../version.c:4191
 msgid "   system vimrc file: \""
 msgstr ""
 
-#: ../version.c:4194
+#: ../version.c:4196
 msgid "     user vimrc file: \""
 msgstr ""
 
-#: ../version.c:4199
+#: ../version.c:4201
 msgid " 2nd user vimrc file: \""
 msgstr ""
 
-#: ../version.c:4204 ../version.c:4211 ../version.c:4215
+#: ../version.c:4206 ../version.c:4213 ../version.c:4217
 msgid " 3rd user vimrc file: \""
 msgstr ""
 
-#: ../version.c:4207
+#: ../version.c:4209
 msgid " 4th user vimrc file: \""
 msgstr ""
 
-#: ../version.c:4220
+#: ../version.c:4222
 msgid "      user exrc file: \""
 msgstr ""
 
-#: ../version.c:4225
+#: ../version.c:4227
 msgid "  2nd user exrc file: \""
 msgstr ""
 
-#: ../version.c:4231
+#: ../version.c:4233
 msgid "  system gvimrc file: \""
 msgstr ""
 
-#: ../version.c:4235
+#: ../version.c:4237
 msgid "    user gvimrc file: \""
 msgstr ""
 
-#: ../version.c:4239
+#: ../version.c:4241
 msgid "2nd user gvimrc file: \""
 msgstr ""
 
-#: ../version.c:4244
+#: ../version.c:4246
 msgid "3rd user gvimrc file: \""
 msgstr ""
 
-#: ../version.c:4249
+#: ../version.c:4251
 msgid "       defaults file: \""
 msgstr ""
 
-#: ../version.c:4254
+#: ../version.c:4256
 msgid "    system menu file: \""
 msgstr ""
 
-#: ../version.c:4262
+#: ../version.c:4264
 msgid "  fall-back for $VIM: \""
 msgstr ""
 
-#: ../version.c:4268
+#: ../version.c:4270
 msgid " f-b for $VIMRUNTIME: \""
 msgstr ""
 
-#: ../version.c:4272
+#: ../version.c:4274
 msgid "Compilation: "
 msgstr ""
 
-#: ../version.c:4278
+#: ../version.c:4280
 msgid "Compiler: "
 msgstr ""
 
-#: ../version.c:4283
+#: ../version.c:4285
 msgid "Linking: "
 msgstr ""
 
-#: ../version.c:4288
+#: ../version.c:4290
 msgid "  DEBUG BUILD"
 msgstr ""
 
-#: ../version.c:4324
+#: ../version.c:4326
 msgid "VIM - Vi IMproved"
 msgstr ""
 
-#: ../version.c:4326
+#: ../version.c:4328
 msgid "version "
 msgstr ""
 
-#: ../version.c:4327
+#: ../version.c:4329
 msgid "by Bram Moolenaar et al."
 msgstr ""
 
-#: ../version.c:4331
+#: ../version.c:4333
 msgid "Vim is open source and freely distributable"
 msgstr ""
 
-#: ../version.c:4333
+#: ../version.c:4335
 msgid "Help poor children in Uganda!"
 msgstr ""
 
-#: ../version.c:4334
+#: ../version.c:4336
 msgid "type  :help iccf<Enter>       for information "
 msgstr ""
 
-#: ../version.c:4336
+#: ../version.c:4338
 msgid "type  :q<Enter>               to exit         "
 msgstr ""
 
-#: ../version.c:4337
+#: ../version.c:4339
 msgid "type  :help<Enter>  or  <F1>  for on-line help"
 msgstr ""
 
-#: ../version.c:4338
+#: ../version.c:4340
 msgid "type  :help version9<Enter>   for version info"
 msgstr ""
 
-#: ../version.c:4341
+#: ../version.c:4343
 msgid "Running in Vi compatible mode"
 msgstr ""
 
-#: ../version.c:4342
+#: ../version.c:4344
 msgid "type  :set nocp<Enter>        for Vim defaults"
 msgstr ""
 
-#: ../version.c:4343
+#: ../version.c:4345
 msgid "type  :help cp-default<Enter> for info on this"
 msgstr ""
 
-#: ../version.c:4358
+#: ../version.c:4360
 msgid "menu  Help->Orphans           for information    "
 msgstr ""
 
-#: ../version.c:4360
+#: ../version.c:4362
 msgid "Running modeless, typed text is inserted"
 msgstr ""
 
-#: ../version.c:4361
+#: ../version.c:4363
 msgid "menu  Edit->Global Settings->Toggle Insert Mode  "
 msgstr ""
 
-#: ../version.c:4362
+#: ../version.c:4364
 msgid "                              for two modes      "
 msgstr ""
 
-#: ../version.c:4366
+#: ../version.c:4368
 msgid "menu  Edit->Global Settings->Toggle Vi Compatible"
 msgstr ""
 
-#: ../version.c:4367
+#: ../version.c:4369
 msgid "                              for Vim defaults   "
 msgstr ""
 
-#: ../version.c:4408
+#: ../version.c:4410
 msgid "Sponsor Vim development!"
 msgstr ""
 
-#: ../version.c:4409
+#: ../version.c:4411
 msgid "Become a registered Vim user!"
 msgstr ""
 
-#: ../version.c:4412
+#: ../version.c:4414
 msgid "type  :help sponsor<Enter>    for information "
 msgstr ""
 
-#: ../version.c:4413
+#: ../version.c:4415
 msgid "type  :help register<Enter>   for information "
 msgstr ""
 
-#: ../version.c:4415
+#: ../version.c:4417
 msgid "menu  Help->Sponsor/Register  for information    "
 msgstr ""
 
index c0e3e60d31b8092ccf554d7faf70ecf286b2a972..e61bf2b50e8b59993fa0ad405f606dd19ea3550b 100644 (file)
@@ -245,6 +245,7 @@ NEW_TESTS = \
        test_plugin_helptoc \
        test_plugin_man \
        test_plugin_matchparen \
+       test_plugin_tar \
        test_plugin_termdebug \
        test_plugin_tohtml \
        test_plugin_tutor \
@@ -517,6 +518,7 @@ NEW_TESTS_RES = \
        test_plugin_helptoc.res \
        test_plugin_man.res \
        test_plugin_matchparen.res \
+       test_plugin_tar.res \
        test_plugin_termdebug.res \
        test_plugin_tohtml.res \
        test_plugin_tutor.res \
diff --git a/src/testdir/samples/evil.tar b/src/testdir/samples/evil.tar
new file mode 100644 (file)
index 0000000..8cbc061
Binary files /dev/null and b/src/testdir/samples/evil.tar differ
diff --git a/src/testdir/samples/sample.tar b/src/testdir/samples/sample.tar
new file mode 100644 (file)
index 0000000..4da3bf3
Binary files /dev/null and b/src/testdir/samples/sample.tar differ
diff --git a/src/testdir/test_plugin_tar.vim b/src/testdir/test_plugin_tar.vim
new file mode 100644 (file)
index 0000000..ebf74d7
--- /dev/null
@@ -0,0 +1,128 @@
+vim9script
+
+CheckExecutable tar
+CheckNotMSWindows
+
+runtime plugin/tarPlugin.vim
+
+def CopyFile(source: string)
+  if !filecopy($"samples/{source}", "X.tar")
+    assert_report($"Can't copy samples/{source}")
+  endif
+enddef
+
+def g:Test_tar_basic()
+  CopyFile("sample.tar")
+  defer delete("X.tar")
+  defer delete("./testtar", 'rf')
+  e X.tar
+
+  ### Check header
+  assert_match('^" tar\.vim version v\d\+', getline(1))
+  assert_match('^" Browsing tarfile .*/X.tar', getline(2))
+  assert_match('^" Select a file with cursor and press ENTER, "x" to extract a file', getline(3))
+  assert_match('^$', getline(4))
+  assert_match('testtar/', getline(5))
+  assert_match('testtar/file1.txt', getline(6))
+
+  ### Check ENTER on header
+  :1
+  exe ":normal \<cr>"
+  assert_equal("X.tar", @%)
+
+  ### Check ENTER on file
+  :6
+  exe ":normal \<cr>"
+  assert_equal("tarfile::testtar/file1.txt", @%)
+
+
+  ### Check editing file
+  ### Note: deleting entries not supported on BSD
+  if has("mac")
+    return
+  endif
+  if has("bsd")
+    return
+  endif
+  s/.*/some-content/
+  assert_equal("some-content", getline(1))
+  w!
+  assert_equal("tarfile::testtar/file1.txt", @%)
+  bw!
+  close
+  bw!
+
+  e X.tar
+  :6
+  exe "normal \<cr>"
+  assert_equal("some-content", getline(1))
+  bw!
+  close
+
+  ### Check extracting file
+  :5
+  normal x
+  assert_true(filereadable("./testtar/file1.txt"))
+  bw!
+enddef
+
+def g:Test_tar_evil()
+  CopyFile("evil.tar")
+  defer delete("X.tar")
+  defer delete("./etc", 'rf')
+  e X.tar
+
+  ### Check header
+  assert_match('^" tar\.vim version v\d\+', getline(1))
+  assert_match('^" Browsing tarfile .*/X.tar', getline(2))
+  assert_match('^" Select a file with cursor and press ENTER, "x" to extract a file', getline(3))
+  assert_match('^" Note: Path Traversal Attack detected', getline(4))
+  assert_match('^$', getline(5))
+  assert_match('/etc/ax-pwn', getline(6))
+
+  ### Check ENTER on header
+  :1
+  exe ":normal \<cr>"
+  assert_equal("X.tar", @%)
+  assert_equal(1, b:leading_slash)
+
+  ### Check ENTER on file
+  :6
+  exe ":normal \<cr>"
+  assert_equal(1, b:leading_slash)
+  assert_equal("tarfile::/etc/ax-pwn", @%)
+
+
+  ### Check editing file
+  ### Note: deleting entries not supported on BSD
+  if has("mac")
+    return
+  endif
+  if has("bsd")
+    return
+  endif
+  s/.*/none/
+  assert_equal("none", getline(1))
+  w!
+  assert_equal(1, b:leading_slash)
+  assert_equal("tarfile::/etc/ax-pwn", @%)
+  bw!
+  close
+  bw!
+
+  # Writing was aborted
+  e X.tar
+  assert_match('^" Note: Path Traversal Attack detected', getline(4))
+  :6
+  exe "normal \<cr>"
+  assert_equal("something", getline(1))
+  bw!
+  close
+
+  ### Check extracting file
+  :5
+  normal x
+  assert_true(filereadable("./etc/ax-pwn"))
+
+  bw!
+enddef
index 40c383a4b13c333ee43fe2830d7866c6553ccee0..98c397cb610cc5d1bf394bb8705bf0295de0ec32 100644 (file)
@@ -719,6 +719,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1552,
 /**/
     1551,
 /**/