mirror of
https://github.com/neovim/neovim.git
synced 2025-12-16 11:25:33 +00:00
Merge pull request #20956 from gpanders/lua-readsecure
feat: implement vim.secure.read() and use it for 'exrc'
This commit is contained in:
@@ -128,10 +128,6 @@ NORMAL COMMANDS
|
|||||||
OPTIONS
|
OPTIONS
|
||||||
- *cpo-<* *:menu-<special>* *:menu-special* *:map-<special>* *:map-special*
|
- *cpo-<* *:menu-<special>* *:menu-special* *:map-<special>* *:map-special*
|
||||||
`<>` notation is always enabled.
|
`<>` notation is always enabled.
|
||||||
- *'exrc'* *'ex'* Security risk: downloaded files could include
|
|
||||||
a malicious .nvimrc or .exrc file. See 'secure'.
|
|
||||||
Recommended alternative: define an autocommand in your
|
|
||||||
|vimrc| to set options for a matching directory.
|
|
||||||
- 'gdefault' Enables the |:substitute| flag 'g' by default.
|
- 'gdefault' Enables the |:substitute| flag 'g' by default.
|
||||||
- *'fe'* 'fenc'+'enc' before Vim 6.0; no longer used.
|
- *'fe'* 'fenc'+'enc' before Vim 6.0; no longer used.
|
||||||
- *'highlight'* *'hl'* Names of builtin |highlight-groups| cannot be changed.
|
- *'highlight'* *'hl'* Names of builtin |highlight-groups| cannot be changed.
|
||||||
|
|||||||
@@ -2354,4 +2354,20 @@ parents({start}) *vim.fs.parents()*
|
|||||||
Return: ~
|
Return: ~
|
||||||
(function) Iterator
|
(function) Iterator
|
||||||
|
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
Lua module: secure *lua-secure*
|
||||||
|
|
||||||
|
read({path}) *vim.secure.read()*
|
||||||
|
Attempt to read the file at {path} prompting the user if the file should
|
||||||
|
be trusted. The user's choice is persisted in a trust database at
|
||||||
|
$XDG_STATE_HOME/nvim/trust.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
• {path} (string) Path to a file to read.
|
||||||
|
|
||||||
|
Return: ~
|
||||||
|
(string|nil) The contents of the given file if it exists and is
|
||||||
|
trusted, or nil otherwise.
|
||||||
|
|
||||||
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:
|
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ NEW FEATURES *news-features*
|
|||||||
|
|
||||||
The following new APIs or features were added.
|
The following new APIs or features were added.
|
||||||
|
|
||||||
|
• |vim.secure.read()| reads a file and prompts the user if it should be
|
||||||
|
trusted and, if so, returns the file's contents.
|
||||||
|
|
||||||
• When using Nvim inside tmux 3.2 or later, the default clipboard provider
|
• When using Nvim inside tmux 3.2 or later, the default clipboard provider
|
||||||
will now copy to the system clipboard. |provider-clipboard|
|
will now copy to the system clipboard. |provider-clipboard|
|
||||||
|
|
||||||
@@ -57,6 +60,8 @@ CHANGED FEATURES *news-changes*
|
|||||||
|
|
||||||
The following changes to existing APIs or features add new behavior.
|
The following changes to existing APIs or features add new behavior.
|
||||||
|
|
||||||
|
• 'exrc' is no longer marked deprecated.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
REMOVED FEATURES *news-removed*
|
REMOVED FEATURES *news-removed*
|
||||||
|
|
||||||
|
|||||||
@@ -2264,6 +2264,20 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
This option is reset when the 'paste' option is set and restored when
|
This option is reset when the 'paste' option is set and restored when
|
||||||
the 'paste' option is reset.
|
the 'paste' option is reset.
|
||||||
|
|
||||||
|
*'exrc'* *'ex'* *'noexrc'* *'noex'*
|
||||||
|
'exrc' 'ex' boolean (default off)
|
||||||
|
global
|
||||||
|
Enables the reading of .nvimrc and .exrc files in the current
|
||||||
|
directory.
|
||||||
|
|
||||||
|
The file is only sourced if the user indicates the file is trusted. If
|
||||||
|
it is, the SHA256 hash of the file contents and the full path of the
|
||||||
|
file are persisted to a trust database. The user is only prompted
|
||||||
|
again if the file contents change. See |vim.secure.read()|.
|
||||||
|
|
||||||
|
This option cannot be set from a |modeline| or in the |sandbox|, for
|
||||||
|
security reasons.
|
||||||
|
|
||||||
*'fileencoding'* *'fenc'* *E213*
|
*'fileencoding'* *'fenc'* *E213*
|
||||||
'fileencoding' 'fenc' string (default: "")
|
'fileencoding' 'fenc' string (default: "")
|
||||||
local to buffer
|
local to buffer
|
||||||
@@ -5115,19 +5129,6 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
two letters (See |object-motions|). The default makes a section start
|
two letters (See |object-motions|). The default makes a section start
|
||||||
at the nroff macros ".SH", ".NH", ".H", ".HU", ".nh" and ".sh".
|
at the nroff macros ".SH", ".NH", ".H", ".HU", ".nh" and ".sh".
|
||||||
|
|
||||||
*'secure'* *'nosecure'* *E523*
|
|
||||||
'secure' boolean (default off)
|
|
||||||
global
|
|
||||||
When on, ":autocmd", shell and write commands are not allowed in
|
|
||||||
".nvimrc" and ".exrc" in the current directory and map commands are
|
|
||||||
displayed. Switch it off only if you know that you will not run into
|
|
||||||
problems, or when the 'exrc' option is off. On Unix this option is
|
|
||||||
only used if the ".nvimrc" or ".exrc" is not owned by you. This can be
|
|
||||||
dangerous if the systems allows users to do a "chown". You better set
|
|
||||||
'secure' at the end of your |init.vim| then.
|
|
||||||
This option cannot be set from a |modeline| or in the |sandbox|, for
|
|
||||||
security reasons.
|
|
||||||
|
|
||||||
*'selection'* *'sel'*
|
*'selection'* *'sel'*
|
||||||
'selection' 'sel' string (default "inclusive")
|
'selection' 'sel' string (default "inclusive")
|
||||||
global
|
global
|
||||||
|
|||||||
@@ -417,6 +417,8 @@ Options:
|
|||||||
'jumpoptions' "view" tries to restore the |mark-view| when moving through
|
'jumpoptions' "view" tries to restore the |mark-view| when moving through
|
||||||
the |jumplist|, |changelist|, |alternate-file| or using |mark-motions|.
|
the |jumplist|, |changelist|, |alternate-file| or using |mark-motions|.
|
||||||
'shortmess' the "F" flag does not affect output from autocommands
|
'shortmess' the "F" flag does not affect output from autocommands
|
||||||
|
'exrc' searches for ".nvimrc" or ".exrc" files. The user is prompted whether
|
||||||
|
to trust the file.
|
||||||
|
|
||||||
Shell:
|
Shell:
|
||||||
Shell output (|:!|, |:make|, …) is always routed through the UI, so it
|
Shell output (|:!|, |:make|, …) is always routed through the UI, so it
|
||||||
@@ -639,6 +641,9 @@ Options:
|
|||||||
*'prompt'* *'noprompt'*
|
*'prompt'* *'noprompt'*
|
||||||
*'remap'* *'noremap'*
|
*'remap'* *'noremap'*
|
||||||
*'restorescreen'* *'rs'* *'norestorescreen'* *'nors'*
|
*'restorescreen'* *'rs'* *'norestorescreen'* *'nors'*
|
||||||
|
*'secure'*
|
||||||
|
Everything is allowed in 'exrc' files since they must be explicitly marked
|
||||||
|
trusted.
|
||||||
*'shelltype'*
|
*'shelltype'*
|
||||||
*'shortname'* *'sn'* *'noshortname'* *'nosn'*
|
*'shortname'* *'sn'* *'noshortname'* *'nosn'*
|
||||||
*'swapsync'* *'sws'*
|
*'swapsync'* *'sws'*
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ for k, v in pairs({
|
|||||||
ui = true,
|
ui = true,
|
||||||
health = true,
|
health = true,
|
||||||
fs = true,
|
fs = true,
|
||||||
|
secure = true,
|
||||||
}) do
|
}) do
|
||||||
vim._submodules[k] = v
|
vim._submodules[k] = v
|
||||||
end
|
end
|
||||||
|
|||||||
106
runtime/lua/vim/secure.lua
Normal file
106
runtime/lua/vim/secure.lua
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
--- Attempt to read the file at {path} prompting the user if the file should be
|
||||||
|
--- trusted. The user's choice is persisted in a trust database at
|
||||||
|
--- $XDG_STATE_HOME/nvim/trust.
|
||||||
|
---
|
||||||
|
---@param path (string) Path to a file to read.
|
||||||
|
---
|
||||||
|
---@return (string|nil) The contents of the given file if it exists and is
|
||||||
|
--- trusted, or nil otherwise.
|
||||||
|
function M.read(path)
|
||||||
|
vim.validate({ path = { path, 's' } })
|
||||||
|
local fullpath = vim.loop.fs_realpath(vim.fs.normalize(path))
|
||||||
|
if not fullpath then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local trust = {}
|
||||||
|
do
|
||||||
|
local f = io.open(vim.fn.stdpath('state') .. '/trust', 'r')
|
||||||
|
if f then
|
||||||
|
local contents = f:read('*a')
|
||||||
|
if contents then
|
||||||
|
for line in vim.gsplit(contents, '\n') do
|
||||||
|
local hash, file = string.match(line, '^(%S+) (.+)$')
|
||||||
|
if hash and file then
|
||||||
|
trust[file] = hash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if trust[fullpath] == '!' then
|
||||||
|
-- File is denied
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local contents
|
||||||
|
do
|
||||||
|
local f = io.open(fullpath, 'r')
|
||||||
|
if not f then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
contents = f:read('*a')
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local hash = vim.fn.sha256(contents)
|
||||||
|
if trust[fullpath] == hash then
|
||||||
|
-- File already exists in trust database
|
||||||
|
return contents
|
||||||
|
end
|
||||||
|
|
||||||
|
-- File either does not exist in trust database or the hash does not match
|
||||||
|
local choice = vim.fn.confirm(
|
||||||
|
string.format('%s is not trusted.', fullpath),
|
||||||
|
'&ignore\n&view\n&deny\n&allow',
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
if choice == 0 or choice == 1 then
|
||||||
|
-- Cancelled or ignored
|
||||||
|
return nil
|
||||||
|
elseif choice == 2 then
|
||||||
|
-- View
|
||||||
|
vim.cmd('new')
|
||||||
|
local buf = vim.api.nvim_get_current_buf()
|
||||||
|
local lines = vim.split(string.gsub(contents, '\n$', ''), '\n')
|
||||||
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
||||||
|
vim.bo[buf].bufhidden = 'hide'
|
||||||
|
vim.bo[buf].buftype = 'nofile'
|
||||||
|
vim.bo[buf].swapfile = false
|
||||||
|
vim.bo[buf].modeline = false
|
||||||
|
vim.bo[buf].buflisted = false
|
||||||
|
vim.bo[buf].readonly = true
|
||||||
|
vim.bo[buf].modifiable = false
|
||||||
|
return nil
|
||||||
|
elseif choice == 3 then
|
||||||
|
-- Deny
|
||||||
|
trust[fullpath] = '!'
|
||||||
|
contents = nil
|
||||||
|
elseif choice == 4 then
|
||||||
|
-- Allow
|
||||||
|
trust[fullpath] = hash
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local f, err = io.open(vim.fn.stdpath('state') .. '/trust', 'w')
|
||||||
|
if not f then
|
||||||
|
error(err)
|
||||||
|
end
|
||||||
|
|
||||||
|
local t = {}
|
||||||
|
for p, h in pairs(trust) do
|
||||||
|
t[#t + 1] = string.format('%s %s\n', h, p)
|
||||||
|
end
|
||||||
|
f:write(table.concat(t))
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
return contents
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -131,6 +131,7 @@ CONFIG = {
|
|||||||
'filetype.lua',
|
'filetype.lua',
|
||||||
'keymap.lua',
|
'keymap.lua',
|
||||||
'fs.lua',
|
'fs.lua',
|
||||||
|
'secure.lua',
|
||||||
],
|
],
|
||||||
'files': [
|
'files': [
|
||||||
'runtime/lua/vim/_editor.lua',
|
'runtime/lua/vim/_editor.lua',
|
||||||
@@ -140,6 +141,7 @@ CONFIG = {
|
|||||||
'runtime/lua/vim/filetype.lua',
|
'runtime/lua/vim/filetype.lua',
|
||||||
'runtime/lua/vim/keymap.lua',
|
'runtime/lua/vim/keymap.lua',
|
||||||
'runtime/lua/vim/fs.lua',
|
'runtime/lua/vim/fs.lua',
|
||||||
|
'runtime/lua/vim/secure.lua',
|
||||||
],
|
],
|
||||||
'file_patterns': '*.lua',
|
'file_patterns': '*.lua',
|
||||||
'fn_name_prefix': '',
|
'fn_name_prefix': '',
|
||||||
@@ -166,6 +168,7 @@ CONFIG = {
|
|||||||
'filetype': 'vim.filetype',
|
'filetype': 'vim.filetype',
|
||||||
'keymap': 'vim.keymap',
|
'keymap': 'vim.keymap',
|
||||||
'fs': 'vim.fs',
|
'fs': 'vim.fs',
|
||||||
|
'secure': 'vim.secure',
|
||||||
},
|
},
|
||||||
'append_only': [
|
'append_only': [
|
||||||
'shared.lua',
|
'shared.lua',
|
||||||
|
|||||||
@@ -1134,8 +1134,7 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
|
|||||||
int scroll_save = msg_scroll;
|
int scroll_save = msg_scroll;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Disallow shell commands from .exrc and .vimrc in current directory for
|
// Disallow shell commands in secure mode
|
||||||
// security reasons.
|
|
||||||
//
|
//
|
||||||
if (check_secure()) {
|
if (check_secure()) {
|
||||||
return;
|
return;
|
||||||
@@ -1477,8 +1476,7 @@ filterend:
|
|||||||
/// @param flags may be SHELL_DOOUT when output is redirected
|
/// @param flags may be SHELL_DOOUT when output is redirected
|
||||||
void do_shell(char *cmd, int flags)
|
void do_shell(char *cmd, int flags)
|
||||||
{
|
{
|
||||||
// Disallow shell commands from .exrc and .vimrc in current directory for
|
// Disallow shell commands in secure mode
|
||||||
// security reasons.
|
|
||||||
if (check_secure()) {
|
if (check_secure()) {
|
||||||
msg_end();
|
msg_end();
|
||||||
return;
|
return;
|
||||||
@@ -3215,8 +3213,7 @@ void ex_z(exarg_T *eap)
|
|||||||
ex_no_reprint = true;
|
ex_no_reprint = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @return true if the secure flag is set (.exrc or .vimrc in current directory)
|
/// @return true if the secure flag is set and also give an error message.
|
||||||
/// and also give an error message.
|
|
||||||
/// Otherwise, return false.
|
/// Otherwise, return false.
|
||||||
bool check_secure(void)
|
bool check_secure(void)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4223,8 +4223,7 @@ theend:
|
|||||||
|
|
||||||
static void ex_autocmd(exarg_T *eap)
|
static void ex_autocmd(exarg_T *eap)
|
||||||
{
|
{
|
||||||
// Disallow autocommands from .exrc and .vimrc in current
|
// Disallow autocommands in secure mode.
|
||||||
// directory for security reasons.
|
|
||||||
if (secure) {
|
if (secure) {
|
||||||
secure = 2;
|
secure = 2;
|
||||||
eap->errmsg = _(e_curdir);
|
eap->errmsg = _(e_curdir);
|
||||||
|
|||||||
@@ -2211,8 +2211,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disallow writing from .exrc and .vimrc in current directory for
|
// Disallow writing in secure mode.
|
||||||
// security reasons.
|
|
||||||
if (check_secure()) {
|
if (check_secure()) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -489,8 +489,7 @@ EXTERN int stdin_fd INIT(= -1);
|
|||||||
// true when doing full-screen output, otherwise only writing some messages.
|
// true when doing full-screen output, otherwise only writing some messages.
|
||||||
EXTERN int full_screen INIT(= false);
|
EXTERN int full_screen INIT(= false);
|
||||||
|
|
||||||
/// Non-zero when only "safe" commands are allowed, e.g. when sourcing .exrc or
|
/// Non-zero when only "safe" commands are allowed
|
||||||
/// .vimrc in current directory.
|
|
||||||
EXTERN int secure INIT(= 0);
|
EXTERN int secure INIT(= 0);
|
||||||
|
|
||||||
/// Non-zero when changing text and jumping to another window or editing another buffer is not
|
/// Non-zero when changing text and jumping to another window or editing another buffer is not
|
||||||
@@ -864,7 +863,7 @@ EXTERN char e_api_spawn_failed[] INIT(= N_("E903: Could not spawn API job"));
|
|||||||
EXTERN char e_argreq[] INIT(= N_("E471: Argument required"));
|
EXTERN char e_argreq[] INIT(= N_("E471: Argument required"));
|
||||||
EXTERN char e_backslash[] INIT(= N_("E10: \\ should be followed by /, ? or &"));
|
EXTERN char e_backslash[] INIT(= N_("E10: \\ should be followed by /, ? or &"));
|
||||||
EXTERN char e_cmdwin[] INIT(= N_("E11: Invalid in command-line window; <CR> executes, CTRL-C quits"));
|
EXTERN char e_cmdwin[] INIT(= N_("E11: Invalid in command-line window; <CR> executes, CTRL-C quits"));
|
||||||
EXTERN char e_curdir[] INIT(= N_("E12: Command not allowed from exrc/vimrc in current dir or tag search"));
|
EXTERN char e_curdir[] INIT(= N_("E12: Command not allowed in secure mode in current dir or tag search"));
|
||||||
EXTERN char e_command_too_recursive[] INIT(= N_("E169: Command too recursive"));
|
EXTERN char e_command_too_recursive[] INIT(= N_("E169: Command too recursive"));
|
||||||
EXTERN char e_endif[] INIT(= N_("E171: Missing :endif"));
|
EXTERN char e_endif[] INIT(= N_("E171: Missing :endif"));
|
||||||
EXTERN char e_endtry[] INIT(= N_("E600: Missing :endtry"));
|
EXTERN char e_endtry[] INIT(= N_("E600: Missing :endtry"));
|
||||||
|
|||||||
@@ -2193,3 +2193,27 @@ plain:
|
|||||||
kv_printf(str, "<Lua %d>", ref);
|
kv_printf(str, "<Lua %d>", ref);
|
||||||
return str.items;
|
return str.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *nlua_read_secure(const char *path)
|
||||||
|
{
|
||||||
|
lua_State *const lstate = global_lstate;
|
||||||
|
lua_getglobal(lstate, "vim");
|
||||||
|
lua_getfield(lstate, -1, "secure");
|
||||||
|
lua_getfield(lstate, -1, "read");
|
||||||
|
lua_pushstring(lstate, path);
|
||||||
|
lua_call(lstate, 1, 1);
|
||||||
|
|
||||||
|
size_t len = 0;
|
||||||
|
const char *contents = lua_tolstring(lstate, -1, &len);
|
||||||
|
char *buf = NULL;
|
||||||
|
if (contents != NULL) {
|
||||||
|
// Add one to include trailing null byte
|
||||||
|
buf = xcalloc(len + 1, sizeof(char));
|
||||||
|
memcpy(buf, contents, len + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop return value, "vim", and "secure"
|
||||||
|
lua_pop(lstate, 3);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1989,35 +1989,22 @@ static void source_startup_scripts(const mparm_T *const parmp)
|
|||||||
do_system_initialization();
|
do_system_initialization();
|
||||||
|
|
||||||
if (do_user_initialization()) {
|
if (do_user_initialization()) {
|
||||||
// Read initialization commands from ".vimrc" or ".exrc" in current
|
// Read initialization commands from ".nvimrc" or ".exrc" in current
|
||||||
// directory. This is only done if the 'exrc' option is set.
|
// directory. This is only done if the 'exrc' option is set.
|
||||||
// Because of security reasons we disallow shell and write commands
|
|
||||||
// now, except for unix if the file is owned by the user or 'secure'
|
|
||||||
// option has been reset in environment of global "exrc" or "vimrc".
|
|
||||||
// Only do this if VIMRC_FILE is not the same as vimrc file sourced in
|
// Only do this if VIMRC_FILE is not the same as vimrc file sourced in
|
||||||
// do_user_initialization.
|
// do_user_initialization.
|
||||||
#if defined(UNIX)
|
char *str = nlua_read_secure(VIMRC_FILE);
|
||||||
// If vimrc file is not owned by user, set 'secure' mode.
|
if (str != NULL) {
|
||||||
if (!os_file_owned(VIMRC_FILE)) // NOLINT(readability/braces)
|
do_source_str(str, VIMRC_FILE);
|
||||||
#endif
|
xfree(str);
|
||||||
secure = p_secure;
|
} else {
|
||||||
|
str = nlua_read_secure(EXRC_FILE);
|
||||||
if (do_source(VIMRC_FILE, true, DOSO_VIMRC) == FAIL) {
|
if (str != NULL) {
|
||||||
#if defined(UNIX)
|
do_source_str(str, EXRC_FILE);
|
||||||
// if ".exrc" is not owned by user set 'secure' mode
|
xfree(str);
|
||||||
if (!os_file_owned(EXRC_FILE)) {
|
|
||||||
secure = p_secure;
|
|
||||||
} else {
|
|
||||||
secure = 0;
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
(void)do_source(EXRC_FILE, false, DOSO_NONE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (secure == 2) {
|
|
||||||
need_wait_return = true;
|
|
||||||
}
|
|
||||||
secure = 0;
|
|
||||||
}
|
}
|
||||||
TIME_MSG("sourcing vimrc file(s)");
|
TIME_MSG("sourcing vimrc file(s)");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2446,8 +2446,7 @@ void ex_abbreviate(exarg_T *eap)
|
|||||||
/// ":map" and friends.
|
/// ":map" and friends.
|
||||||
void ex_map(exarg_T *eap)
|
void ex_map(exarg_T *eap)
|
||||||
{
|
{
|
||||||
// If we are sourcing .exrc or .vimrc in current directory we
|
// If we are in a secure mode we print the mappings for security reasons.
|
||||||
// print the mappings for security reasons.
|
|
||||||
if (secure) {
|
if (secure) {
|
||||||
secure = 2;
|
secure = 2;
|
||||||
msg_outtrans(eap->cmd);
|
msg_outtrans(eap->cmd);
|
||||||
|
|||||||
@@ -2007,7 +2007,7 @@ return {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
full_name='secure',
|
full_name='secure',
|
||||||
short_desc=N_("mode for reading .vimrc in current dir"),
|
short_desc=N_("No description"),
|
||||||
type='bool', scope={'global'},
|
type='bool', scope={'global'},
|
||||||
secure=true,
|
secure=true,
|
||||||
varname='p_secure',
|
varname='p_secure',
|
||||||
|
|||||||
@@ -1930,7 +1930,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
|
|||||||
|
|
||||||
cookie.fp = fopen_noinh_readbin(fname_exp);
|
cookie.fp = fopen_noinh_readbin(fname_exp);
|
||||||
if (cookie.fp == NULL && check_other) {
|
if (cookie.fp == NULL && check_other) {
|
||||||
// Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
|
// Try again, replacing file name ".nvimrc" by "_nvimrc" or vice versa,
|
||||||
// and ".exrc" by "_exrc" or vice versa.
|
// and ".exrc" by "_exrc" or vice versa.
|
||||||
p = path_tail(fname_exp);
|
p = path_tail(fname_exp);
|
||||||
if ((*p == '.' || *p == '_')
|
if ((*p == '.' || *p == '_')
|
||||||
|
|||||||
@@ -1024,6 +1024,7 @@ endfunc
|
|||||||
|
|
||||||
" Test for using the 'exrc' option
|
" Test for using the 'exrc' option
|
||||||
func Test_exrc()
|
func Test_exrc()
|
||||||
|
throw 'Skipped: Nvim requires user input for the exrc option'
|
||||||
let after =<< trim [CODE]
|
let after =<< trim [CODE]
|
||||||
call assert_equal(1, &exrc)
|
call assert_equal(1, &exrc)
|
||||||
call assert_equal(1, &secure)
|
call assert_equal(1, &secure)
|
||||||
|
|||||||
171
test/functional/lua/secure_spec.lua
Normal file
171
test/functional/lua/secure_spec.lua
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
local Screen = require('test.functional.ui.screen')
|
||||||
|
|
||||||
|
local eq = helpers.eq
|
||||||
|
local clear = helpers.clear
|
||||||
|
local command = helpers.command
|
||||||
|
local pathsep = helpers.get_pathsep()
|
||||||
|
local iswin = helpers.iswin()
|
||||||
|
local curbufmeths = helpers.curbufmeths
|
||||||
|
local exec_lua = helpers.exec_lua
|
||||||
|
local feed_command = helpers.feed_command
|
||||||
|
local feed = helpers.feed
|
||||||
|
local funcs = helpers.funcs
|
||||||
|
local pcall_err = helpers.pcall_err
|
||||||
|
|
||||||
|
describe('vim.secure', function()
|
||||||
|
describe('read()', function()
|
||||||
|
local xstate = 'Xstate'
|
||||||
|
|
||||||
|
setup(function()
|
||||||
|
helpers.mkdir_p(xstate .. pathsep .. (iswin and 'nvim-data' or 'nvim'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
teardown(function()
|
||||||
|
helpers.rmdir(xstate)
|
||||||
|
end)
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
helpers.write_file('Xfile', [[
|
||||||
|
let g:foobar = 42
|
||||||
|
]])
|
||||||
|
clear{env={XDG_STATE_HOME=xstate}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
after_each(function()
|
||||||
|
os.remove('Xfile')
|
||||||
|
helpers.rmdir(xstate)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('works', function()
|
||||||
|
local screen = Screen.new(80, 8)
|
||||||
|
screen:attach()
|
||||||
|
screen:set_default_attr_ids({
|
||||||
|
[1] = {bold = true, foreground = Screen.colors.Blue1},
|
||||||
|
[2] = {bold = true, reverse = true},
|
||||||
|
[3] = {bold = true, foreground = Screen.colors.SeaGreen},
|
||||||
|
[4] = {reverse = true},
|
||||||
|
})
|
||||||
|
|
||||||
|
local cwd = funcs.getcwd()
|
||||||
|
|
||||||
|
-- Need to use feed_command instead of exec_lua because of the confirmation prompt
|
||||||
|
feed_command([[lua vim.secure.read('Xfile')]])
|
||||||
|
screen:expect{grid=[[
|
||||||
|
|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{2: }|
|
||||||
|
:lua vim.secure.read('Xfile') |
|
||||||
|
{3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}|
|
||||||
|
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ |
|
||||||
|
]]}
|
||||||
|
feed('d')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
|
||||||
|
eq(string.format('! %s', cwd .. pathsep .. 'Xfile'), vim.trim(trust))
|
||||||
|
eq(helpers.NIL, exec_lua([[return vim.secure.read('Xfile')]]))
|
||||||
|
|
||||||
|
os.remove(funcs.stdpath('state') .. pathsep .. 'trust')
|
||||||
|
|
||||||
|
feed_command([[lua vim.secure.read('Xfile')]])
|
||||||
|
screen:expect{grid=[[
|
||||||
|
|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{2: }|
|
||||||
|
:lua vim.secure.read('Xfile') |
|
||||||
|
{3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}|
|
||||||
|
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ |
|
||||||
|
]]}
|
||||||
|
feed('a')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
local hash = funcs.sha256(helpers.read_file('Xfile'))
|
||||||
|
trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
|
||||||
|
eq(string.format('%s %s', hash, cwd .. pathsep .. 'Xfile'), vim.trim(trust))
|
||||||
|
eq(helpers.NIL, exec_lua([[vim.secure.read('Xfile')]]))
|
||||||
|
|
||||||
|
os.remove(funcs.stdpath('state') .. pathsep .. 'trust')
|
||||||
|
|
||||||
|
feed_command([[lua vim.secure.read('Xfile')]])
|
||||||
|
screen:expect{grid=[[
|
||||||
|
|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{2: }|
|
||||||
|
:lua vim.secure.read('Xfile') |
|
||||||
|
{3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}|
|
||||||
|
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ |
|
||||||
|
]]}
|
||||||
|
feed('i')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
-- Trust database is not updated
|
||||||
|
trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
|
||||||
|
eq(nil, trust)
|
||||||
|
|
||||||
|
feed_command([[lua vim.secure.read('Xfile')]])
|
||||||
|
screen:expect{grid=[[
|
||||||
|
|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{2: }|
|
||||||
|
:lua vim.secure.read('Xfile') |
|
||||||
|
{3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}|
|
||||||
|
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ |
|
||||||
|
]]}
|
||||||
|
feed('v')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ let g:foobar = 42 |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{2:]] .. cwd .. pathsep .. [[Xfile [RO]{MATCH:%s+}|
|
||||||
|
|
|
||||||
|
{1:~ }|
|
||||||
|
{4:[No Name] }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
-- Trust database is not updated
|
||||||
|
trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
|
||||||
|
eq(nil, trust)
|
||||||
|
|
||||||
|
-- Cannot write file
|
||||||
|
pcall_err(command, 'write')
|
||||||
|
eq(false, curbufmeths.get_option('modifiable'))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
Reference in New Issue
Block a user