]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0299: runtime(zip): may write using absolute paths v9.2.0299
authorChristian Brabandt <cb@256bit.org>
Sun, 5 Apr 2026 15:58:00 +0000 (15:58 +0000)
committerChristian Brabandt <cb@256bit.org>
Sun, 5 Apr 2026 15:58:00 +0000 (15:58 +0000)
Problem:  runtime(zip): may write using absolute paths
          (syndicate)
Solution: Detect this case and abort on Unix, warn in the documentation
          about possible issues

Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/autoload/zip.vim
runtime/doc/pi_zip.txt
src/testdir/samples/evil.zip
src/testdir/test_plugin_zip.vim
src/version.c

index 1ce9cfc2f75c15eaddd2a0bd16aba564240d7866..f4482fd7fc83294c8fe74528e84d4bd5c27b2364 100644 (file)
@@ -21,6 +21,7 @@
 " 2026 Feb 08 by Vim Project: use system() instead of :!
 " 2026 Mar 08 by Vim Project: Make ZipUpdatePS() check for powershell
 " 2026 Apr 01 by Vim Project: Detect more path traversal attacks
+" 2026 Apr 05 by Vim Project: Detect more path traversal attacks
 " License:     Vim License  (see vim's :help license)
 " Copyright:   Copyright (C) 2005-2019 Charles E. Campbell {{{1
 "              Permission is hereby granted to use and distribute this code,
@@ -395,9 +396,16 @@ fun! zip#Write(fname)
   if has("unix")
     let zipfile = substitute(a:fname,'zipfile://\(.\{-}\)::[^\\].*$','\1','')
     let fname   = substitute(a:fname,'zipfile://.\{-}::\([^\\].*\)$','\1','')
+    " fname should not start with a leading slash to avoid writing anywhere into the system
+    if fname =~ '^/'
+      call s:Mess('Error', "***error*** (zip#Write) Path Traversal Attack detected, not writing!")
+      call s:ChgDir(curdir,s:WARNING,"(zip#Write) unable to return to ".curdir."!")
+      return
+    endif
   else
     let zipfile = substitute(a:fname,'^.\{-}zipfile://\(.\{-}\)::[^\\].*$','\1','')
     let fname   = substitute(a:fname,'^.\{-}zipfile://.\{-}::\([^\\].*\)$','\1','')
+    " TODO: what to check on MS-Windows to avoid writing absolute paths?
   endif
   if fname =~ '^[.]\{1,2}/'
     let gnu_cmd = g:zip_zipcmd . ' -d ' . s:Escape(fnamemodify(zipfile,":p"),0) . ' ' . s:Escape(fname,0)
index b1bc7fd7dcbdd4fdc4f900282636331234ed7238..e9294b40591d0c7c7c70ac96c7f5ed8783955bf6 100644 (file)
@@ -1,4 +1,4 @@
-*pi_zip.txt*   For Vim version 9.2.  Last change: 2026 Feb 14
+*pi_zip.txt*   For Vim version 9.2.  Last change: 2026 Apr 05
 
                                +====================+
                                | Zip File Interface |
@@ -33,6 +33,10 @@ Copyright: Copyright (C) 2005-2015 Charles E Campbell         *zip-copyright*
    also write to the file.  Currently, one may not make a new file in
    zip archives via the plugin.
 
+   The zip plugin tries to detect some common path traversal attack
+   patterns, but it may not catch all possible cases.  Please be very
+   careful when using this plugin with untrusted input.
+
    COMMANDS~
                                                                *zip-x*
    x : extract a listed file when the cursor is atop it
index 17cffadf934580090ebe2b3d3876edec14767658..8361710b9bd032d8c4ec094bef18f5884f893fa2 100644 (file)
Binary files a/src/testdir/samples/evil.zip and b/src/testdir/samples/evil.zip differ
index 53b6120834fcce56f8644ed06777f6c5eb8ef9cc..6d77643482fe26de1e1c7f3f2f746e9b417e8194 100644 (file)
@@ -296,3 +296,22 @@ def g:Test_zip_fname_evil_path2()
   assert_match('zipfile://.*::.*tmp/foobar', @%)
   bw!
 enddef
+
+def g:Test_zip_fname_evil_path3()
+  CheckNotMSWindows
+  # needed for writing the zip file
+  CheckExecutable zip
+
+  CopyZipFile("evil.zip")
+  defer delete("X.zip")
+  e X.zip
+
+  :1
+  var fname = 'payload.txt'
+  search('\V' .. fname)
+  exe "normal \<cr>"
+  :w!
+  var mess  = execute(':mess')
+  assert_match('Path Traversal Attack', mess)
+  bw!
+enddef
index e3dee3b3a58db33be377a1e10c8c63aab77ad6a8..4d0ec69447f6f117adab29f673b0daa43299baa6 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    299,
 /**/
     298,
 /**/