vim-patch:9.2.0523: tests: no test for using shellescape() in combination with :! (#39972)

Problem:  tests: no test for using shellescape() in combination with :!
Solution: Add a test that checks runtime files for using wrong
          combination of shellescape() with ! ex command

This has lead to a few security relevant issues, so add a test that
checks all runtime files for any ! followed by a shellescape() that does
not use the {special} arg.

related: Commit: 3fb5e58fbc63d86a3e65f1a141b0d67af2 (patch 9.2.0479:
         [security]: runtime(tar): command injection in tar plugin)

closes: vim/vim#20286

Supported by AI

fccc2adc98

Co-authored-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
zeertzjq
2026-05-24 09:29:50 +08:00
committed by GitHub
parent 1a064abb0a
commit 61eddb3fe7
2 changed files with 40 additions and 1 deletions

View File

@@ -117,7 +117,7 @@ else " No jobs
endfunction
function! s:typeset(path)
execute '!' . context#command() . ' ' . shellescape(fnamemodify(a:path, ":t"))
execute '!' . context#command() . ' ' . shellescape(fnamemodify(a:path, ":t"), 1)
call call(get(b:, 'context_callback', get(g:, 'context_callback', 'context#callback')),
\ [a:path, 0, v:shell_error])
endfunction

View File

@@ -6,6 +6,22 @@ func s:ReportError(fname, lnum, msg)
endif
endfunc
func s:PerformCheck(fname, pattern, msg, skip)
let prev_lnum = 1
let lnum = 1
while (lnum > 0)
call cursor(lnum, 1)
let lnum = search(a:pattern, 'W', 0, 0, a:skip)
if (prev_lnum == lnum)
break
endif
let prev_lnum = lnum
if (lnum > 0)
call s:ReportError(a:fname, lnum, a:msg)
endif
endwhile
endfunc
func Test_test_files()
for fname in glob('*.vim', 0, 1)
let g:ignoreSwapExists = 'e'
@@ -99,4 +115,27 @@ func Test_help_files()
endfunc
func Test_runtime_wrong_shellescape()
" Check that shellescape() is called with the {special} argument (a second,
" non-zero argument) when its result is used in a ":!" ex command.
" This could cause code injection!
let pattern = '\<shellescape(\%([^,()]\|([^()]*)\)\+)'
let q = "['" .. '"]'
let bang_exe = '\<\%(exe\%[cute]\|sil\%[ent]\)\>.*' .. q .. '[^"' .. "']*!"
let skip = 'getline(".") !~ ' .. string(bang_exe)
\ .. ' || getline(".") =~ ' .. string('\<system\%(list\)\=(')
\ .. ' || getline(".") =~ ' .. string('^\s*"')
for fpath in glob('../../../runtime/**/*.vim', 0, 1)
let g:ignoreSwapExists = 'e'
exe 'edit ' .. fpath
call s:PerformCheck(fpath, pattern,
\ 'shellescape() without {special} flag used in ":!" command', skip)
endfor
:%bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab