runtime: don't execute external commands when loading ftplugins

This is a followup to 816fbcc262 (patch
9.0.1833: [security] runtime file fixes)

It basically disables that external commands are run on loading of the
filetype plugin, **unless** the user has set the `g:plugin_exec = 1`
global variable in their configuration or for a specific filetype the
variable g:<filetype>_exec=1.

There are a few more plugins, that may execute system commands like
debchangelog, gitcommit, sh, racket, zsh, ps1 but those do at least
do not run those commands by default during loading of the filetype plugin
(there the command is mostly run as convenience for auto-completion or
to provide documentation lookup).

closes: #13034

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Tim Pope <vim@tpope.org>
This commit is contained in:
Christian Brabandt
2023-09-06 20:41:25 +02:00
parent 1689e847ff
commit f7ac0ef509
6 changed files with 46 additions and 18 deletions

View File

@ -1,4 +1,4 @@
*filetype.txt* For Vim version 9.0. Last change: 2023 Apr 29 *filetype.txt* For Vim version 9.0. Last change: 2023 Sep 06
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -419,11 +419,24 @@ ways to change this:
3. Docs for the default filetype plugins. *ftplugin-docs* 3. Docs for the default filetype plugins. *ftplugin-docs*
*plugin_exec* *g:plugin_exec*
Enable executing of external commands. This was done historically for e.g.
the perl filetype plugin (and a few others) to set the search path.
Disabled by default for security reasons: >
:let g:plugin_exec = 1
It is also possible to enable this only for certain filetypes: >
:let g:<filetype>_exec = 1
So to enable this only for ruby, set the following variable: >
:let g:ruby_exec = 1
If both, the global `plugin_exec` and the `<filetype>_exec` specific variable
are set, the filetpe specific variable should have precedent.
AWK *ft-awk-plugin* AWK *ft-awk-plugin*
Support for features specific to GNU Awk, like @include, can be enabled by Support for features specific to GNU Awk, like @include, can be enabled by
setting: > setting: >
let g:awk_is_gawk = 1 :let g:awk_is_gawk = 1
CHANGELOG *ft-changelog-plugin* CHANGELOG *ft-changelog-plugin*

View File

@ -37,11 +37,14 @@ if exists("g:awk_is_gawk")
let b:undo_ftplugin .= " | setl fp<" let b:undo_ftplugin .= " | setl fp<"
endif endif
let path = system("gawk 'BEGIN { printf ENVIRON[\"AWKPATH\"] }'") " Disabled by default for security reasons.
let path = substitute(path, '^\.\=:\|:\.\=$\|:\.\=:', ',,', 'g') " POSIX cwd if get(g:, 'awk_exec', get(g:, 'plugin_exec', 0))
let path = substitute(path, ':', ',', 'g') let path = system("gawk 'BEGIN { printf ENVIRON[\"AWKPATH\"] }'")
let path = substitute(path, '^\.\=:\|:\.\=$\|:\.\=:', ',,', 'g') " POSIX cwd
let path = substitute(path, ':', ',', 'g')
let &l:path = path let &l:path = path
endif
let b:undo_ftplugin .= " | setl inc< path<" let b:undo_ftplugin .= " | setl inc< path<"
endif endif

View File

@ -55,13 +55,19 @@ if &filetype == 'changelog'
elseif $EMAIL_ADDRESS != "" elseif $EMAIL_ADDRESS != ""
return $EMAIL_ADDRESS return $EMAIL_ADDRESS
endif endif
let s:default_login = 'unknown'
let login = s:login() " Disabled by default for security reasons.
if get(g:, 'changelog_exec', get(g:, 'plugin_exec', 0))
let login = s:login()
else
let login = s:default_login
endif
return printf('%s <%s@%s>', s:name(login), login, s:hostname()) return printf('%s <%s@%s>', s:name(login), login, s:hostname())
endfunction endfunction
function! s:login() function! s:login()
return s:trimmed_system_with_default('whoami', 'unknown') return s:trimmed_system_with_default('whoami', s:default_login)
endfunction endfunction
function! s:trimmed_system_with_default(command, default) function! s:trimmed_system_with_default(command, default)
@ -71,7 +77,7 @@ if &filetype == 'changelog'
function! s:system_with_default(command, default) function! s:system_with_default(command, default)
let output = system(a:command) let output = system(a:command)
if v:shell_error if v:shell_error
return default return a:default
endif endif
return output return output
endfunction endfunction

View File

@ -54,10 +54,12 @@ endif
" Set this once, globally. " Set this once, globally.
if !exists("perlpath") if !exists("perlpath")
" safety check: don't execute perl from current directory
let s:tmp_cwd = getcwd() let s:tmp_cwd = getcwd()
if executable("perl") && (fnamemodify(exepath("perl"), ":p:h") != s:tmp_cwd " safety check: don't execute perl binary by default
\ || (index(split($PATH,has("win32")? ';' : ':'), s:tmp_cwd) != -1 && s:tmp_cwd != '.')) if executable("perl") && get(g:, 'perl_exec', get(g:, 'plugin_exec', 0))
\ && (fnamemodify(exepath("perl"), ":p:h") != s:tmp_cwd
\ || (index(split($PATH, has("win32") ? ';' : ':'), s:tmp_cwd) != -1
\ && s:tmp_cwd != '.'))
try try
if &shellxquote != '"' if &shellxquote != '"'
let perlpath = system('perl -e "print join(q/,/,@INC)"') let perlpath = system('perl -e "print join(q/,/,@INC)"')
@ -73,7 +75,7 @@ if !exists("perlpath")
" current directory and the directory of the current file. " current directory and the directory of the current file.
let perlpath = ".,," let perlpath = ".,,"
endif endif
unlet s:tmp_cwd unlet! s:tmp_cwd
endif endif
" Append perlpath to the existing path value, if it is set. Since we don't " Append perlpath to the existing path value, if it is set. Since we don't

View File

@ -61,6 +61,10 @@ if !exists('g:ruby_version_paths')
endif endif
function! s:query_path(root) abort function! s:query_path(root) abort
" Disabled by default for security reasons.
if !get(g:, 'ruby_exec', get(g:, 'plugin_exec', 0))
return []
endif
let code = "print $:.join %q{,}" let code = "print $:.join %q{,}"
if &shell =~# 'sh' && empty(&shellxquote) if &shell =~# 'sh' && empty(&shellxquote)
let prefix = 'env PATH='.shellescape($PATH).' ' let prefix = 'env PATH='.shellescape($PATH).' '
@ -84,7 +88,7 @@ function! s:query_path(root) abort
else else
let path = split(system(path_check),',') let path = split(system(path_check),',')
endif endif
unlet s:tmp_cwd unlet! s:tmp_cwd
exe cd cwd exe cd cwd
return path return path
finally finally

View File

@ -40,17 +40,17 @@ endif
let &l:define='\v(<fn>|<const>|<var>|^\s*\#\s*define)' let &l:define='\v(<fn>|<const>|<var>|^\s*\#\s*define)'
" Safety check: don't execute zip from current directory " Safety check: don't execute zip from current directory
let s:tmp_cwd = getcwd()
if !exists('g:zig_std_dir') && exists('*json_decode') && if !exists('g:zig_std_dir') && exists('*json_decode') &&
\ executable('zig') && (fnamemodify(exepath("zig"), ":p:h") != s:tmp_cwd \ executable('zig') && get(g:, 'zig_exec', get(g:, 'plugin_exec', 0))
\ || (index(split($PATH,has("win32")? ';' : ':'), s:tmp_cwd) != -1 && s:tmp_cwd != '.')) \ && (fnamemodify(exepath("zig"), ":p:h") != s:tmp_cwd
\ || (index(split($PATH,has("win32")? ';' : ':'), s:tmp_cwd) != -1 && s:tmp_cwd != '.'))
silent let s:env = system('zig env') silent let s:env = system('zig env')
if v:shell_error == 0 if v:shell_error == 0
let g:zig_std_dir = json_decode(s:env)['std_dir'] let g:zig_std_dir = json_decode(s:env)['std_dir']
endif endif
unlet! s:env unlet! s:env
endif endif
unlet s:tmp_cwd unlet! s:tmp_cwd
if exists('g:zig_std_dir') if exists('g:zig_std_dir')
let &l:path = &l:path . ',' . g:zig_std_dir let &l:path = &l:path . ',' . g:zig_std_dir