neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 22c3e5802a7c8823a98f01649c0345a0e98cc39a
parent 6383123326483db401c09a5e1f4dc99f11c900d6
Author: zeertzjq <zeertzjq@outlook.com>
Date:   Fri,  5 Dec 2025 07:32:43 +0800

Merge pull request #36827 from zeertzjq/vim-9.1.1947

vim-patch:9.1.{1947,1948,1951}
Diffstat:
Mruntime/doc/vimfn.txt | 9++++++---
Mruntime/lua/vim/_meta/vimfn.lua | 9++++++---
Msrc/nvim/eval.lua | 9++++++---
Msrc/nvim/os/env.c | 16++++++++++++++++
Msrc/nvim/os/fs.c | 3++-
Msrc/nvim/os/shell.c | 23+++++++++++++++++++----
Mtest/functional/vimscript/executable_spec.lua | 4++--
Mtest/old/testdir/test_system.vim | 38++++++++++++++++++++++++++++++++++++++
8 files changed, 95 insertions(+), 16 deletions(-)

diff --git a/runtime/doc/vimfn.txt b/runtime/doc/vimfn.txt @@ -1875,9 +1875,12 @@ executable({expr}) *executable()* On MS-Windows an executable in the same directory as the Vim executable is always found (it's added to $PATH at |startup|). *NoDefaultCurrentDirectoryInExePath* - On MS-Windows an executable in Vim's current working directory - is also normally found, but this can be disabled by setting - the $NoDefaultCurrentDirectoryInExePath environment variable. + On MS-Windows when using cmd.exe as 'shell' an executable in + Vim's current working directory is also normally found, which + can be disabled by setting the + `$NoDefaultCurrentDirectoryInExePath` environment variable. + This is always done when executing external commands using + e.g. |:!|, |:make|, |system()| for security reasons. The result is a Number: 1 exists diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua @@ -1650,9 +1650,12 @@ function vim.fn.eventhandler() end --- On MS-Windows an executable in the same directory as the Vim --- executable is always found (it's added to $PATH at |startup|). --- *NoDefaultCurrentDirectoryInExePath* ---- On MS-Windows an executable in Vim's current working directory ---- is also normally found, but this can be disabled by setting ---- the $NoDefaultCurrentDirectoryInExePath environment variable. +--- On MS-Windows when using cmd.exe as 'shell' an executable in +--- Vim's current working directory is also normally found, which +--- can be disabled by setting the +--- `$NoDefaultCurrentDirectoryInExePath` environment variable. +--- This is always done when executing external commands using +--- e.g. |:!|, |:make|, |system()| for security reasons. --- --- The result is a Number: --- 1 exists diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua @@ -2161,9 +2161,12 @@ M.funcs = { On MS-Windows an executable in the same directory as the Vim executable is always found (it's added to $PATH at |startup|). *NoDefaultCurrentDirectoryInExePath* - On MS-Windows an executable in Vim's current working directory - is also normally found, but this can be disabled by setting - the $NoDefaultCurrentDirectoryInExePath environment variable. + On MS-Windows when using cmd.exe as 'shell' an executable in + Vim's current working directory is also normally found, which + can be disabled by setting the + `$NoDefaultCurrentDirectoryInExePath` environment variable. + This is always done when executing external commands using + e.g. |:!|, |:make|, |system()| for security reasons. The result is a Number: 1 exists diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c @@ -1291,3 +1291,19 @@ void vim_setenv_ext(const char *name, const char *val) didset_vimruntime = false; } } + +#ifdef MSWIN +/// Restore a previous environment variable value, or unset it if NULL. +/// "must_free" indicates whether "old_value" was allocated. +void restore_env_var(const char *name, char *old_value, bool must_free) +{ + if (old_value != NULL) { + os_setenv(name, old_value, true); + if (must_free) { + xfree(old_value); + } + return; + } + os_unsetenv(name); +} +#endif diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c @@ -355,7 +355,8 @@ static bool is_executable_in_path(const char *name, char **abspath) #ifdef MSWIN char *path = NULL; - if (!os_env_exists("NoDefaultCurrentDirectoryInExePath", false)) { + if (!os_env_exists("NoDefaultCurrentDirectoryInExePath", false) + && strstr(path_tail(p_sh), "cmd.exe") != NULL) { // Prepend ".;" to $PATH. size_t pathlen = strlen(path_env); path = xmallocz(pathlen + 2); diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c @@ -33,6 +33,7 @@ #include "nvim/message.h" #include "nvim/option_vars.h" #include "nvim/os/fs.h" +#include "nvim/os/os.h" #include "nvim/os/os_defs.h" #include "nvim/os/shell.h" #include "nvim/os/signal.h" @@ -857,6 +858,15 @@ int os_system(char **argv, const char *input, size_t len, char **output, static int do_os_system(char **argv, const char *input, size_t len, char **output, size_t *nread, bool silent, bool forward_output) { + int exitcode = -1; + +#ifdef MSWIN + // do not execute anything from the current directory by setting the + // environemnt variable $NoDefaultCurrentDirectoryInExePath + char *oldval = os_getenv("NoDefaultCurrentDirectoryInExePath"); + os_setenv("NoDefaultCurrentDirectoryInExePath", "1", true); +#endif + out_data_decide_throttle(0); // Initialize throttle decider. out_data_ring(NULL, 0); // Initialize output ring-buffer. bool has_input = (input != NULL && len > 0); @@ -894,8 +904,7 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu msg_outtrans(prog, 0, false); msg_putchar('\n'); } - multiqueue_free(events); - return -1; + goto end; } // Note: unlike process events, stream events are not queued, as we want to @@ -917,7 +926,7 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu if (!wstream_write(&proc->in, input_buffer)) { // couldn't write, stop the process and tell the user about it proc_stop(proc); - return -1; + goto end; } // close the input stream after everything is written wstream_set_write_cb(&proc->in, shell_write_cb, NULL); @@ -933,7 +942,7 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu msg_no_more = true; lines_left = -1; } - int exitcode = proc_wait(proc, -1, NULL); + exitcode = proc_wait(proc, -1, NULL); if (!got_int && out_data_decide_throttle(0)) { // Last chunk of output was skipped; display it now. out_data_ring(NULL, SIZE_MAX); @@ -965,8 +974,14 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu } assert(multiqueue_empty(events)); +end: multiqueue_free(events); +#ifdef MSWIN + // Restore original value of NoDefaultCurrentDirectoryInExePath + restore_env_var("NoDefaultCurrentDirectoryInExePath", oldval, true); +#endif + return exitcode; } diff --git a/test/functional/vimscript/executable_spec.lua b/test/functional/vimscript/executable_spec.lua @@ -202,9 +202,9 @@ describe('executable() (Windows)', function() clear({ env = { PATHEXT = '' } }) command('set shell=sh') for _, ext in ipairs(exts) do - eq(1, call('executable', 'test_executable_' .. ext .. '.' .. ext)) + eq(0, call('executable', 'test_executable_' .. ext .. '.' .. ext)) end - eq(1, call('executable', 'test_executable_zzz.zzz')) + eq(0, call('executable', 'test_executable_zzz.zzz')) end) it("relative path, Unix-style 'shell' (backslashes)", function() diff --git a/test/old/testdir/test_system.vim b/test/old/testdir/test_system.vim @@ -141,4 +141,42 @@ func Test_system_with_shell_quote() endtry endfunc +" Check that Vim does not execute anything from current directory +func Test_windows_external_cmd_in_cwd() + CheckMSWindows + + " just in case + call system('rd /S /Q Xfolder') + call mkdir('Xfolder', 'R') + cd Xfolder + + let contents = ['@echo off', 'echo filename1.txt:1:AAAA'] + call writefile(contents, 'findstr.cmd') + + let file1 = ['AAAA', 'THIS FILE SHOULD NOT BE FOUND'] + let file2 = ['BBBB', 'THIS FILE SHOULD BE FOUND'] + + call writefile(file1, 'filename1.txt') + call writefile(file2, 'filename2.txt') + + if has('quickfix') + " use silent to avoid hit-enter-prompt + sil grep BBBB filename*.txt + call assert_equal('filename2.txt', @%) + endif + + let output = system('findstr BBBB filename*') + " Match trailing newline byte + call assert_match('filename2.txt:BBBB.', output) + + if has('gui') + set guioptions+=! + let output = system('findstr BBBB filename*') + call assert_match('filename2.txt:BBBB.', output) + endif + + cd - + set guioptions& +endfunc + " vim: shiftwidth=2 sts=2 expandtab