Mercurial > vim
annotate runtime/autoload/gzip.vim @ 33096:828bcb1a37e7 v9.0.1833
patch 9.0.1833: [security] runtime file fixes
Commit: https://github.com/vim/vim/commit/816fbcc262687b81fc46f82f7bbeb1453addfe0c
Author: Christian Brabandt <cb@256bit.org>
Date: Thu Aug 31 23:52:30 2023 +0200
patch 9.0.1833: [security] runtime file fixes
Problem: runtime files may execute code in current dir
Solution: only execute, if not run from current directory
The perl, zig and ruby filetype plugins and the zip and gzip autoload
plugins may try to load malicious executable files from the current
working directory. This is especially a problem on windows, where the
current directory is implicitly in your $PATH and windows may even run a
file with the extension `.bat` because of $PATHEXT.
So make sure that we are not trying to execute a file from the current
directory. If this would be the case, error out (for the zip and gzip)
plugins or silently do not run those commands (for the ftplugins).
This assumes, that only the current working directory is bad. For all
other directories, it is assumed that those directories were
intentionally set to the $PATH by the user.
Signed-off-by: Christian Brabandt <cb@256bit.org>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Fri, 01 Sep 2023 00:00:02 +0200 |
parents | 4027cefc2aab |
children | 8bc48ca90534 |
rev | line source |
---|---|
446 | 1 " Vim autoload file for editing compressed files. |
32770
4027cefc2aab
Farewell to Bram and dedicate upcoming Vim 9.1 to him (#12749)
Christian Brabandt <cb@256bit.org>
parents:
10244
diff
changeset
|
2 " Maintainer: The Vim Project <https://github.com/vim/vim> |
4027cefc2aab
Farewell to Bram and dedicate upcoming Vim 9.1 to him (#12749)
Christian Brabandt <cb@256bit.org>
parents:
10244
diff
changeset
|
3 " Last Change: 2023 Aug 10 |
4027cefc2aab
Farewell to Bram and dedicate upcoming Vim 9.1 to him (#12749)
Christian Brabandt <cb@256bit.org>
parents:
10244
diff
changeset
|
4 " Former Maintainer: Bram Moolenaar <Bram@vim.org> |
446 | 5 |
6 " These functions are used by the gzip plugin. | |
7 | |
8 " Function to check that executing "cmd [-f]" works. | |
9 " The result is cached in s:have_"cmd" for speed. | |
10 fun s:check(cmd) | |
11 let name = substitute(a:cmd, '\(\S*\).*', '\1', '') | |
12 if !exists("s:have_" . name) | |
33096
828bcb1a37e7
patch 9.0.1833: [security] runtime file fixes
Christian Brabandt <cb@256bit.org>
parents:
32770
diff
changeset
|
13 " safety check, don't execute anything from the current directory |
828bcb1a37e7
patch 9.0.1833: [security] runtime file fixes
Christian Brabandt <cb@256bit.org>
parents:
32770
diff
changeset
|
14 let f = fnamemodify(exepath(name), ":p:h") !=# getcwd() |
828bcb1a37e7
patch 9.0.1833: [security] runtime file fixes
Christian Brabandt <cb@256bit.org>
parents:
32770
diff
changeset
|
15 if !f |
828bcb1a37e7
patch 9.0.1833: [security] runtime file fixes
Christian Brabandt <cb@256bit.org>
parents:
32770
diff
changeset
|
16 echoerr "Warning: NOT executing " .. name .. " from current directory!" |
828bcb1a37e7
patch 9.0.1833: [security] runtime file fixes
Christian Brabandt <cb@256bit.org>
parents:
32770
diff
changeset
|
17 endif |
446 | 18 let e = executable(name) |
19 if e < 0 | |
20 let r = system(name . " --version") | |
21 let e = (r !~ "not found" && r != "") | |
22 endif | |
33096
828bcb1a37e7
patch 9.0.1833: [security] runtime file fixes
Christian Brabandt <cb@256bit.org>
parents:
32770
diff
changeset
|
23 exe "let s:have_" . name . "=" . (e && f) |
446 | 24 endif |
25 exe "return s:have_" . name | |
26 endfun | |
27 | |
28 " Set b:gzip_comp_arg to the gzip argument to be used for compression, based on | |
29 " the flags in the compressed file. | |
30 " The only compression methods that can be detected are max speed (-1) and max | |
31 " compression (-9). | |
32 fun s:set_compression(line) | |
33 " get the Compression Method | |
34 let l:cm = char2nr(a:line[2]) | |
35 " if it's 8 (DEFLATE), we can check for the compression level | |
36 if l:cm == 8 | |
37 " get the eXtra FLags | |
38 let l:xfl = char2nr(a:line[8]) | |
39 " max compression | |
40 if l:xfl == 2 | |
41 let b:gzip_comp_arg = "-9" | |
42 " min compression | |
43 elseif l:xfl == 4 | |
44 let b:gzip_comp_arg = "-1" | |
45 endif | |
46 endif | |
47 endfun | |
48 | |
49 | |
50 " After reading compressed file: Uncompress text in buffer with "cmd" | |
51 fun gzip#read(cmd) | |
52 " don't do anything if the cmd is not supported | |
53 if !s:check(a:cmd) | |
54 return | |
55 endif | |
56 | |
57 " for gzip check current compression level and set b:gzip_comp_arg. | |
58 silent! unlet b:gzip_comp_arg | |
59 if a:cmd[0] == 'g' | |
60 call s:set_compression(getline(1)) | |
61 endif | |
62 | |
63 " make 'patchmode' empty, we don't want a copy of the written file | |
64 let pm_save = &pm | |
65 set pm= | |
66 " remove 'a' and 'A' from 'cpo' to avoid the alternate file changes | |
67 let cpo_save = &cpo | |
68 set cpo-=a cpo-=A | |
69 " set 'modifiable' | |
70 let ma_save = &ma | |
71 setlocal ma | |
10244
876fbdd84e52
commit https://github.com/vim/vim/commit/2ec618c9feac4573b154510236ad8121c77d0eca
Christian Brabandt <cb@256bit.org>
parents:
6336
diff
changeset
|
72 " set 'write' |
876fbdd84e52
commit https://github.com/vim/vim/commit/2ec618c9feac4573b154510236ad8121c77d0eca
Christian Brabandt <cb@256bit.org>
parents:
6336
diff
changeset
|
73 let write_save = &write |
876fbdd84e52
commit https://github.com/vim/vim/commit/2ec618c9feac4573b154510236ad8121c77d0eca
Christian Brabandt <cb@256bit.org>
parents:
6336
diff
changeset
|
74 set write |
1190 | 75 " Reset 'foldenable', otherwise line numbers get adjusted. |
76 if has("folding") | |
77 let fen_save = &fen | |
78 setlocal nofen | |
79 endif | |
80 | |
446 | 81 " when filtering the whole buffer, it will become empty |
82 let empty = line("'[") == 1 && line("']") == line("$") | |
83 let tmp = tempname() | |
84 let tmpe = tmp . "." . expand("<afile>:e") | |
1592 | 85 if exists('*fnameescape') |
86 let tmp_esc = fnameescape(tmp) | |
87 let tmpe_esc = fnameescape(tmpe) | |
88 else | |
89 let tmp_esc = escape(tmp, ' ') | |
90 let tmpe_esc = escape(tmpe, ' ') | |
91 endif | |
446 | 92 " write the just read lines to a temp file "'[,']w tmp.gz" |
1592 | 93 execute "silent '[,']w " . tmpe_esc |
446 | 94 " uncompress the temp file: call system("gzip -dn tmp.gz") |
985 | 95 call system(a:cmd . " " . s:escape(tmpe)) |
446 | 96 if !filereadable(tmp) |
97 " uncompress didn't work! Keep the compressed file then. | |
98 echoerr "Error: Could not read uncompressed file" | |
1190 | 99 let ok = 0 |
446 | 100 else |
1190 | 101 let ok = 1 |
102 " delete the compressed lines; remember the line number | |
103 let l = line("'[") - 1 | |
104 if exists(":lockmarks") | |
105 lockmarks '[,']d _ | |
106 else | |
107 '[,']d _ | |
108 endif | |
109 " read in the uncompressed lines "'[-1r tmp" | |
110 " Use ++edit if the buffer was empty, keep the 'ff' and 'fenc' options. | |
111 setlocal nobin | |
112 if exists(":lockmarks") | |
113 if empty | |
1592 | 114 execute "silent lockmarks " . l . "r ++edit " . tmp_esc |
1190 | 115 else |
1592 | 116 execute "silent lockmarks " . l . "r " . tmp_esc |
1190 | 117 endif |
118 else | |
1592 | 119 execute "silent " . l . "r " . tmp_esc |
1190 | 120 endif |
121 | |
122 " if buffer became empty, delete trailing blank line | |
819 | 123 if empty |
1190 | 124 silent $delete _ |
125 1 | |
819 | 126 endif |
1190 | 127 " delete the temp file and the used buffers |
128 call delete(tmp) | |
1592 | 129 silent! exe "bwipe " . tmp_esc |
130 silent! exe "bwipe " . tmpe_esc | |
446 | 131 endif |
6336 | 132 " Store the OK flag, so that we can use it when writing. |
133 let b:uncompressOk = ok | |
446 | 134 |
1190 | 135 " Restore saved option values. |
446 | 136 let &pm = pm_save |
137 let &cpo = cpo_save | |
138 let &l:ma = ma_save | |
10244
876fbdd84e52
commit https://github.com/vim/vim/commit/2ec618c9feac4573b154510236ad8121c77d0eca
Christian Brabandt <cb@256bit.org>
parents:
6336
diff
changeset
|
139 let &write = write_save |
1190 | 140 if has("folding") |
141 let &l:fen = fen_save | |
142 endif | |
143 | |
446 | 144 " When uncompressed the whole buffer, do autocommands |
1190 | 145 if ok && empty |
1592 | 146 if exists('*fnameescape') |
147 let fname = fnameescape(expand("%:r")) | |
148 else | |
149 let fname = escape(expand("%:r"), " \t\n*?[{`$\\%#'\"|!<") | |
150 endif | |
446 | 151 if &verbose >= 8 |
1592 | 152 execute "doau BufReadPost " . fname |
446 | 153 else |
1592 | 154 execute "silent! doau BufReadPost " . fname |
446 | 155 endif |
156 endif | |
157 endfun | |
158 | |
159 " After writing compressed file: Compress written file with "cmd" | |
160 fun gzip#write(cmd) | |
6336 | 161 if exists('b:uncompressOk') && !b:uncompressOk |
162 echomsg "Not compressing file because uncompress failed; reset b:uncompressOk to compress anyway" | |
446 | 163 " don't do anything if the cmd is not supported |
6336 | 164 elseif s:check(a:cmd) |
446 | 165 " Rename the file before compressing it. |
166 let nm = resolve(expand("<afile>")) | |
167 let nmt = s:tempname(nm) | |
168 if rename(nm, nmt) == 0 | |
169 if exists("b:gzip_comp_arg") | |
1668 | 170 call system(a:cmd . " " . b:gzip_comp_arg . " -- " . s:escape(nmt)) |
446 | 171 else |
1668 | 172 call system(a:cmd . " -- " . s:escape(nmt)) |
446 | 173 endif |
174 call rename(nmt . "." . expand("<afile>:e"), nm) | |
175 endif | |
176 endif | |
177 endfun | |
178 | |
179 " Before appending to compressed file: Uncompress file with "cmd" | |
180 fun gzip#appre(cmd) | |
181 " don't do anything if the cmd is not supported | |
182 if s:check(a:cmd) | |
183 let nm = expand("<afile>") | |
184 | |
185 " for gzip check current compression level and set b:gzip_comp_arg. | |
186 silent! unlet b:gzip_comp_arg | |
187 if a:cmd[0] == 'g' | |
188 call s:set_compression(readfile(nm, "b", 1)[0]) | |
189 endif | |
190 | |
191 " Rename to a weird name to avoid the risk of overwriting another file | |
192 let nmt = expand("<afile>:p:h") . "/X~=@l9q5" | |
193 let nmte = nmt . "." . expand("<afile>:e") | |
194 if rename(nm, nmte) == 0 | |
195 if &patchmode != "" && getfsize(nm . &patchmode) == -1 | |
196 " Create patchmode file by creating the decompressed file new | |
1668 | 197 call system(a:cmd . " -c -- " . s:escape(nmte) . " > " . s:escape(nmt)) |
446 | 198 call rename(nmte, nm . &patchmode) |
199 else | |
1668 | 200 call system(a:cmd . " -- " . s:escape(nmte)) |
446 | 201 endif |
202 call rename(nmt, nm) | |
203 endif | |
204 endif | |
205 endfun | |
206 | |
207 " find a file name for the file to be compressed. Use "name" without an | |
208 " extension if possible. Otherwise use a weird name to avoid overwriting an | |
209 " existing file. | |
210 fun s:tempname(name) | |
211 let fn = fnamemodify(a:name, ":r") | |
212 if !filereadable(fn) && !isdirectory(fn) | |
213 return fn | |
214 endif | |
215 return fnamemodify(a:name, ":p:h") . "/X~=@l9q5" | |
216 endfun | |
217 | |
985 | 218 fun s:escape(name) |
219 " shellescape() was added by patch 7.0.111 | |
1132 | 220 if exists("*shellescape") |
985 | 221 return shellescape(a:name) |
222 endif | |
223 return "'" . a:name . "'" | |
224 endfun | |
225 | |
446 | 226 " vim: set sw=2 : |