mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	feat(secure): add :trust command  and vim.secure.trust() (#21107)
				
					
				
			Introduce vim.secure.trust() to programmatically manage the trust database. Use this function in a new :trust ex command which can be used as a simple frontend. Resolves: https://github.com/neovim/neovim/issues/21092 Co-authored-by: Gregory Anders <greg@gpanders.com> Co-authored-by: ii14 <ii14@users.noreply.github.com>
This commit is contained in:
		| @@ -1650,4 +1650,32 @@ There are three different types of searching: | ||||
|    currently work with 'path' items that contain a URL or use the double star | ||||
|    with depth limiter (/usr/**2) or upward search (;) notations. | ||||
|  | ||||
| ============================================================================== | ||||
| 11. Trusted Files						*trust* | ||||
|  | ||||
| Nvim has the ability to execute arbitrary code through the 'exrc' option. In | ||||
| order to prevent executing code from untrusted sources, Nvim has the concept of | ||||
| "trusted files". An untrusted file will not be executed without the user's | ||||
| consent, and a user can permanently mark a file as trusted or untrusted using | ||||
| the |:trust| command or the |vim.secure.read()| function. | ||||
|  | ||||
| 							*:trust* *E5570* | ||||
| :trust [++deny] [++remove] [{file}] | ||||
|  | ||||
| 			Manage files in the trust database. Without any options | ||||
| 			or arguments, :trust adds the file associated with the | ||||
| 			current buffer to the trust database, along with the | ||||
| 			SHA256 hash of its contents. | ||||
|  | ||||
| 			[++deny] marks the file associated with the current | ||||
| 			buffer (or {file}, if given) as denied; no prompts will | ||||
| 			be displayed to the user and the file will never be | ||||
| 			executed. | ||||
|  | ||||
| 			[++remove] removes the file associated with the current | ||||
| 			buffer (or {file}, if given) from the trust database. | ||||
| 			Future attempts to read the file in a secure setting | ||||
| 			(i.e. with 'exrc' or |vim.secure.read()|) will prompt | ||||
| 			the user if the file is trusted. | ||||
|  | ||||
|  vim:tw=78:ts=8:noet:ft=help:norl: | ||||
|   | ||||
| @@ -1633,6 +1633,7 @@ tag		command		action ~ | ||||
| |:topleft|	:to[pleft]	make split window appear at top or far left | ||||
| |:tprevious|	:tp[revious]	jump to previous matching tag | ||||
| |:trewind|	:tr[ewind]	jump to first matching tag | ||||
| |:trust|	:trust		add or remove file from trust database | ||||
| |:try|		:try		execute commands, abort on error or exception | ||||
| |:tselect|	:ts[elect]	list matching tags and select one | ||||
| |:tunmap|	:tunma[p]	like ":unmap" but for |Terminal-mode| | ||||
|   | ||||
| @@ -2371,4 +2371,28 @@ read({path})                                               *vim.secure.read()* | ||||
|         (string|nil) The contents of the given file if it exists and is | ||||
|         trusted, or nil otherwise. | ||||
|  | ||||
|     See also: ~ | ||||
|         |:trust| | ||||
|  | ||||
| trust({opts})                                             *vim.secure.trust()* | ||||
|     Manage the trust database. | ||||
|  | ||||
|     The trust database is located at |$XDG_STATE_HOME|/nvim/trust. | ||||
|  | ||||
|     Parameters: ~ | ||||
|       • {opts}  (table) | ||||
|                 • action (string): "allow" to add a file to the trust database | ||||
|                   and trust it, "deny" to add a file to the trust database and | ||||
|                   deny it, "remove" to remove file from the trust database | ||||
|                 • path (string|nil): Path to a file to update. Mutually | ||||
|                   exclusive with {bufnr}. Cannot be used when {action} is | ||||
|                   "allow". | ||||
|                 • bufnr (number|nil): Buffer number to update. Mutually | ||||
|                   exclusive with {path}. | ||||
|  | ||||
|     Return: ~ | ||||
|         (boolean, string) success, msg: | ||||
|         • true and full path of target file if operation was successful | ||||
|         • false and error message on failure | ||||
|  | ||||
|  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. | ||||
|  | ||||
| • |vim.secure.trust()|, |:trust| allows the user to manage files in trust | ||||
|   database. | ||||
|  | ||||
| • |vim.diagnostic.open_float()| (and therefore |vim.diagnostic.config()|) now | ||||
|   accepts a `suffix` option which, by default, renders LSP error codes. | ||||
|   Similarly, the `virtual_text` configuration in |vim.diagnostic.config()| now | ||||
|   | ||||
| @@ -2275,6 +2275,8 @@ A jump table for the options with a short description can be found at |Q_op|. | ||||
| 	file are persisted to a trust database. The user is only prompted | ||||
| 	again if the file contents change. See |vim.secure.read()|. | ||||
|  | ||||
| 	Use |:trust| to manage the trusted file database. | ||||
|  | ||||
| 	This option cannot be set from a |modeline| or in the |sandbox|, for | ||||
| 	security reasons. | ||||
|  | ||||
|   | ||||
| @@ -1,22 +1,11 @@ | ||||
| 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. | ||||
| ---@private | ||||
| --- Reads trust database from $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 | ||||
|  | ||||
| ---@return (table) Contents of trust database, if it exists. Empty table otherwise. | ||||
| local function read_trust() | ||||
|   local trust = {} | ||||
|   do | ||||
|   local f = io.open(vim.fn.stdpath('state') .. '/trust', 'r') | ||||
|   if f then | ||||
|     local contents = f:read('*a') | ||||
| @@ -30,8 +19,45 @@ function M.read(path) | ||||
|     end | ||||
|     f:close() | ||||
|   end | ||||
|   return trust | ||||
| end | ||||
|  | ||||
| ---@private | ||||
| --- Writes provided {trust} table to trust database at | ||||
| --- $XDG_STATE_HOME/nvim/trust. | ||||
| --- | ||||
| ---@param trust (table) Trust table to write | ||||
| local function write_trust(trust) | ||||
|   vim.validate({ trust = { trust, 't' } }) | ||||
|   local f = assert(io.open(vim.fn.stdpath('state') .. '/trust', 'w')) | ||||
|  | ||||
|   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 | ||||
|  | ||||
| --- 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. | ||||
| --- | ||||
| ---@see |: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 = read_trust() | ||||
|  | ||||
|   if trust[fullpath] == '!' then | ||||
|     -- File is denied | ||||
|     return nil | ||||
| @@ -86,21 +112,82 @@ function M.read(path) | ||||
|     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 | ||||
|   write_trust(trust) | ||||
|  | ||||
|   return contents | ||||
| end | ||||
|  | ||||
| --- Manage the trust database. | ||||
| --- | ||||
| --- The trust database is located at |$XDG_STATE_HOME|/nvim/trust. | ||||
| --- | ||||
| ---@param opts (table): | ||||
| ---    - action (string): "allow" to add a file to the trust database and trust it, | ||||
| ---      "deny" to add a file to the trust database and deny it, | ||||
| ---      "remove" to remove file from the trust database | ||||
| ---    - path (string|nil): Path to a file to update. Mutually exclusive with {bufnr}. | ||||
| ---      Cannot be used when {action} is "allow". | ||||
| ---    - bufnr (number|nil): Buffer number to update. Mutually exclusive with {path}. | ||||
| ---@return (boolean, string) success, msg: | ||||
| ---    - true and full path of target file if operation was successful | ||||
| ---    - false and error message on failure | ||||
| function M.trust(opts) | ||||
|   vim.validate({ | ||||
|     path = { opts.path, 's', true }, | ||||
|     bufnr = { opts.bufnr, 'n', true }, | ||||
|     action = { | ||||
|       opts.action, | ||||
|       function(m) | ||||
|         return m == 'allow' or m == 'deny' or m == 'remove' | ||||
|       end, | ||||
|       [["allow" or "deny" or "remove"]], | ||||
|     }, | ||||
|   }) | ||||
|  | ||||
|   local path = opts.path | ||||
|   local bufnr = opts.bufnr | ||||
|   local action = opts.action | ||||
|  | ||||
|   if path and bufnr then | ||||
|     error('path and bufnr are mutually exclusive', 2) | ||||
|   end | ||||
|  | ||||
|   local fullpath | ||||
|   if path then | ||||
|     fullpath = vim.loop.fs_realpath(vim.fs.normalize(path)) | ||||
|   else | ||||
|     local bufname = vim.api.nvim_buf_get_name(bufnr) | ||||
|     if bufname == '' then | ||||
|       return false, 'buffer is not associated with a file' | ||||
|     end | ||||
|     fullpath = vim.loop.fs_realpath(vim.fs.normalize(bufname)) | ||||
|   end | ||||
|  | ||||
|   if not fullpath then | ||||
|     return false, string.format('invalid path: %s', path) | ||||
|   end | ||||
|  | ||||
|   local trust = read_trust() | ||||
|  | ||||
|   if action == 'allow' then | ||||
|     assert(bufnr, 'bufnr is required when action is "allow"') | ||||
|  | ||||
|     local newline = vim.bo[bufnr].fileformat == 'unix' and '\n' or '\r\n' | ||||
|     local contents = table.concat(vim.api.nvim_buf_get_lines(bufnr, 0, -1, false), newline) | ||||
|     if vim.bo[bufnr].endofline then | ||||
|       contents = contents .. newline | ||||
|     end | ||||
|     local hash = vim.fn.sha256(contents) | ||||
|  | ||||
|     trust[fullpath] = hash | ||||
|   elseif action == 'deny' then | ||||
|     trust[fullpath] = '!' | ||||
|   elseif action == 'remove' then | ||||
|     trust[fullpath] = nil | ||||
|   end | ||||
|  | ||||
|   write_trust(trust) | ||||
|   return true, fullpath | ||||
| end | ||||
|  | ||||
| return M | ||||
|   | ||||
| @@ -53,6 +53,7 @@ | ||||
| #include "nvim/highlight_group.h" | ||||
| #include "nvim/indent.h" | ||||
| #include "nvim/input.h" | ||||
| #include "nvim/lua/executor.h" | ||||
| #include "nvim/macros.h" | ||||
| #include "nvim/main.h" | ||||
| #include "nvim/mark.h" | ||||
| @@ -4960,3 +4961,29 @@ void ex_oldfiles(exarg_T *eap) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void ex_trust(exarg_T *eap) | ||||
| { | ||||
|   const char *const p = skiptowhite(eap->arg); | ||||
|   char *arg1 = xmemdupz(eap->arg, (size_t)(p - eap->arg)); | ||||
|   const char *action = "allow"; | ||||
|   const char *path = skipwhite(p); | ||||
|  | ||||
|   if (strcmp(arg1, "++deny") == 0) { | ||||
|     action = "deny"; | ||||
|   } else if (strcmp(arg1, "++remove") == 0) { | ||||
|     action = "remove"; | ||||
|   } else if (*arg1 != '\0') { | ||||
|     semsg(e_invarg2, arg1); | ||||
|     goto theend; | ||||
|   } | ||||
|  | ||||
|   if (path[0] == '\0') { | ||||
|     path = NULL; | ||||
|   } | ||||
|  | ||||
|   nlua_trust(action, path); | ||||
|  | ||||
| theend: | ||||
|   xfree(arg1); | ||||
| } | ||||
|   | ||||
| @@ -2933,6 +2933,12 @@ module.cmds = { | ||||
|     addr_type='ADDR_OTHER', | ||||
|     func='ex_tag', | ||||
|   }, | ||||
|   { | ||||
|     command='trust', | ||||
|     flags=bit.bor(EXTRA, FILE1, TRLBAR, LOCK_OK), | ||||
|     addr_type='ADDR_NONE', | ||||
|     func='ex_trust', | ||||
|   }, | ||||
|   { | ||||
|     command='try', | ||||
|     flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), | ||||
|   | ||||
| @@ -1827,6 +1827,7 @@ static bool skip_cmd(const exarg_T *eap) | ||||
|     case CMD_throw: | ||||
|     case CMD_tilde: | ||||
|     case CMD_topleft: | ||||
|     case CMD_trust: | ||||
|     case CMD_unlet: | ||||
|     case CMD_unlockvar: | ||||
|     case CMD_verbose: | ||||
|   | ||||
| @@ -1016,6 +1016,8 @@ EXTERN char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group | ||||
| EXTERN char e_undobang_cannot_redo_or_move_branch[] | ||||
| INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch")); | ||||
|  | ||||
| EXTERN char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s")); | ||||
|  | ||||
| EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); | ||||
| EXTERN char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP")); | ||||
|  | ||||
|   | ||||
| @@ -2217,3 +2217,51 @@ char *nlua_read_secure(const char *path) | ||||
|  | ||||
|   return buf; | ||||
| } | ||||
|  | ||||
| bool nlua_trust(const char *action, const char *path) | ||||
| { | ||||
|   lua_State *const lstate = global_lstate; | ||||
|   lua_getglobal(lstate, "vim"); | ||||
|   lua_getfield(lstate, -1, "secure"); | ||||
|   lua_getfield(lstate, -1, "trust"); | ||||
|  | ||||
|   lua_newtable(lstate); | ||||
|   lua_pushstring(lstate, "action"); | ||||
|   lua_pushstring(lstate, action); | ||||
|   lua_settable(lstate, -3); | ||||
|   if (path == NULL) { | ||||
|     lua_pushstring(lstate, "bufnr"); | ||||
|     lua_pushnumber(lstate, 0); | ||||
|     lua_settable(lstate, -3); | ||||
|   } else { | ||||
|     lua_pushstring(lstate, "path"); | ||||
|     lua_pushstring(lstate, path); | ||||
|     lua_settable(lstate, -3); | ||||
|   } | ||||
|  | ||||
|   if (nlua_pcall(lstate, 1, 2)) { | ||||
|     nlua_error(lstate, _("Error executing vim.secure.trust: %.*s")); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   bool success = lua_toboolean(lstate, -2); | ||||
|   const char *msg = lua_tostring(lstate, -1); | ||||
|   if (msg != NULL) { | ||||
|     if (success) { | ||||
|       if (strcmp(action, "allow") == 0) { | ||||
|         smsg("Allowed \"%s\" in trust database.", msg); | ||||
|       } else if (strcmp(action, "deny") == 0) { | ||||
|         smsg("Denied \"%s\" in trust database.", msg); | ||||
|       } else if (strcmp(action, "remove") == 0) { | ||||
|         smsg("Removed \"%s\" from trust database.", msg); | ||||
|       } | ||||
|     } else { | ||||
|       semsg(e_trustfile, msg); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Pop return values, "vim" and "secure" | ||||
|   lua_pop(lstate, 4); | ||||
|  | ||||
|   return success; | ||||
| } | ||||
|   | ||||
							
								
								
									
										176
									
								
								test/functional/ex_cmds/trust_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								test/functional/ex_cmds/trust_spec.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | ||||
| 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 is_os = helpers.is_os | ||||
| local funcs = helpers.funcs | ||||
|  | ||||
| describe(':trust', function() | ||||
|   local xstate = 'Xstate' | ||||
|  | ||||
|   setup(function() | ||||
|     helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) | ||||
|   end) | ||||
|  | ||||
|   teardown(function() | ||||
|     helpers.rmdir(xstate) | ||||
|   end) | ||||
|  | ||||
|   before_each(function() | ||||
|     helpers.write_file('test_file', 'test') | ||||
|     clear{env={XDG_STATE_HOME=xstate}} | ||||
|   end) | ||||
|  | ||||
|   after_each(function() | ||||
|     os.remove('test_file') | ||||
|   end) | ||||
|  | ||||
|   it('trust then deny then remove a file using current buffer', function() | ||||
|     local screen = Screen.new(80, 8) | ||||
|     screen:attach() | ||||
|     screen:set_default_attr_ids({ | ||||
|       [1] = {bold = true, foreground = Screen.colors.Blue1}, | ||||
|     }) | ||||
|  | ||||
|     local cwd = funcs.getcwd() | ||||
|     local hash = funcs.sha256(helpers.read_file('test_file')) | ||||
|  | ||||
|     command('edit test_file') | ||||
|     command('trust') | ||||
|     screen:expect([[ | ||||
|       ^test                                                                            | | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       "]] .. cwd .. pathsep .. [[test_file" trusted.{MATCH:%s+}| | ||||
|     ]]) | ||||
|     local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|     eq(string.format('%s %s', hash, cwd .. pathsep .. 'test_file'), vim.trim(trust)) | ||||
|  | ||||
|     command('trust ++deny') | ||||
|     screen:expect([[ | ||||
|       ^test                                                                            | | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       "]] .. cwd .. pathsep .. [[test_file" denied.{MATCH:%s+}| | ||||
|     ]]) | ||||
|     trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|     eq(string.format('! %s', cwd .. pathsep .. 'test_file'), vim.trim(trust)) | ||||
|  | ||||
|     command('trust ++remove') | ||||
|     screen:expect([[ | ||||
|       ^test                                                                            | | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       "]] .. cwd .. pathsep .. [[test_file" removed.{MATCH:%s+}| | ||||
|     ]]) | ||||
|     trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|     eq(string.format(''), vim.trim(trust)) | ||||
|   end) | ||||
|  | ||||
|   it('deny then trust then remove a file using current buffer', function() | ||||
|     local screen = Screen.new(80, 8) | ||||
|     screen:attach() | ||||
|     screen:set_default_attr_ids({ | ||||
|       [1] = {bold = true, foreground = Screen.colors.Blue1}, | ||||
|     }) | ||||
|  | ||||
|     local cwd = funcs.getcwd() | ||||
|     local hash = funcs.sha256(helpers.read_file('test_file')) | ||||
|  | ||||
|     command('edit test_file') | ||||
|     command('trust ++deny') | ||||
|     screen:expect([[ | ||||
|       ^test                                                                            | | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       "]] .. cwd .. pathsep .. [[test_file" denied.{MATCH:%s+}| | ||||
|     ]]) | ||||
|     local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|     eq(string.format('! %s', cwd .. pathsep .. 'test_file'), vim.trim(trust)) | ||||
|  | ||||
|     command('trust') | ||||
|     screen:expect([[ | ||||
|       ^test                                                                            | | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       "]] .. cwd .. pathsep .. [[test_file" trusted.{MATCH:%s+}| | ||||
|     ]]) | ||||
|     trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|     eq(string.format('%s %s', hash, cwd .. pathsep .. 'test_file'), vim.trim(trust)) | ||||
|  | ||||
|     command('trust ++remove') | ||||
|     screen:expect([[ | ||||
|       ^test                                                                            | | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       "]] .. cwd .. pathsep .. [[test_file" removed.{MATCH:%s+}| | ||||
|     ]]) | ||||
|     trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|     eq(string.format(''), vim.trim(trust)) | ||||
|   end) | ||||
|  | ||||
|   it('deny then remove a file using file path', function() | ||||
|     local screen = Screen.new(80, 8) | ||||
|     screen:attach() | ||||
|     screen:set_default_attr_ids({ | ||||
|       [1] = {bold = true, foreground = Screen.colors.Blue1}, | ||||
|     }) | ||||
|  | ||||
|     local cwd = funcs.getcwd() | ||||
|  | ||||
|     command('trust ++deny test_file') | ||||
|     screen:expect([[ | ||||
|       ^                                                                                | | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       "]] .. cwd .. pathsep .. [[test_file" denied.{MATCH:%s+}| | ||||
|     ]]) | ||||
|     local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|     eq(string.format('! %s', cwd .. pathsep .. 'test_file'), vim.trim(trust)) | ||||
|  | ||||
|     command('trust ++remove test_file') | ||||
|     screen:expect([[ | ||||
|       ^                                                                                | | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       {1:~                                                                               }| | ||||
|       "]] .. cwd .. pathsep .. [[test_file" removed.{MATCH:%s+}| | ||||
|     ]]) | ||||
|     trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|     eq(string.format(''), vim.trim(trust)) | ||||
|   end) | ||||
| end) | ||||
| @@ -168,4 +168,111 @@ describe('vim.secure', function() | ||||
|       eq(false, curbufmeths.get_option('modifiable')) | ||||
|     end) | ||||
|   end) | ||||
|  | ||||
|   describe('trust()', function() | ||||
|     local xstate = 'Xstate' | ||||
|  | ||||
|     setup(function() | ||||
|       helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) | ||||
|     end) | ||||
|  | ||||
|     teardown(function() | ||||
|       helpers.rmdir(xstate) | ||||
|     end) | ||||
|  | ||||
|     before_each(function() | ||||
|       helpers.write_file('test_file', 'test') | ||||
|     end) | ||||
|  | ||||
|     after_each(function() | ||||
|       os.remove('test_file') | ||||
|     end) | ||||
|  | ||||
|     it('returns error when passing both path and bufnr', function() | ||||
|       eq('path and bufnr are mutually exclusive', | ||||
|         pcall_err(exec_lua, [[vim.secure.trust({action='deny', bufnr=0, path='test_file'})]])) | ||||
|     end) | ||||
|  | ||||
|     it('trust then deny then remove a file using bufnr', function() | ||||
|       local cwd = funcs.getcwd() | ||||
|       local hash = funcs.sha256(helpers.read_file('test_file')) | ||||
|       local full_path = cwd .. pathsep .. 'test_file' | ||||
|  | ||||
|       command('edit test_file') | ||||
|       eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) | ||||
|       local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|       eq(string.format('%s %s', hash, full_path), vim.trim(trust)) | ||||
|  | ||||
|       eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='deny', bufnr=0})}]])) | ||||
|       trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|       eq(string.format('! %s', full_path), vim.trim(trust)) | ||||
|  | ||||
|       eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='remove', bufnr=0})}]])) | ||||
|       trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|       eq('', vim.trim(trust)) | ||||
|     end) | ||||
|  | ||||
|     it('deny then trust then remove a file using bufnr', function() | ||||
|       local cwd = funcs.getcwd() | ||||
|       local hash = funcs.sha256(helpers.read_file('test_file')) | ||||
|       local full_path = cwd .. pathsep .. 'test_file' | ||||
|  | ||||
|       command('edit test_file') | ||||
|       eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='deny', bufnr=0})}]])) | ||||
|       local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|       eq(string.format('! %s', full_path), vim.trim(trust)) | ||||
|  | ||||
|       eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) | ||||
|       trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|       eq(string.format('%s %s', hash, full_path), vim.trim(trust)) | ||||
|  | ||||
|       eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='remove', bufnr=0})}]])) | ||||
|       trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|       eq('', vim.trim(trust)) | ||||
|     end) | ||||
|  | ||||
|     it('trust using bufnr then deny then remove a file using path', function() | ||||
|       local cwd = funcs.getcwd() | ||||
|       local hash = funcs.sha256(helpers.read_file('test_file')) | ||||
|       local full_path = cwd .. pathsep .. 'test_file' | ||||
|  | ||||
|       command('edit test_file') | ||||
|       eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) | ||||
|       local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|       eq(string.format('%s %s', hash, full_path), vim.trim(trust)) | ||||
|  | ||||
|       eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='deny', path='test_file'})}]])) | ||||
|       trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|       eq(string.format('! %s', full_path), vim.trim(trust)) | ||||
|  | ||||
|       eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='remove', path='test_file'})}]])) | ||||
|       trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|       eq('', vim.trim(trust)) | ||||
|     end) | ||||
|  | ||||
|     it('deny then trust then remove a file using bufnr', function() | ||||
|       local cwd = funcs.getcwd() | ||||
|       local hash = funcs.sha256(helpers.read_file('test_file')) | ||||
|       local full_path = cwd .. pathsep .. 'test_file' | ||||
|  | ||||
|       command('edit test_file') | ||||
|       eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='deny', path='test_file'})}]])) | ||||
|       local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|       eq(string.format('! %s', full_path), vim.trim(trust)) | ||||
|  | ||||
|       eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) | ||||
|       trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|       eq(string.format('%s %s', hash, full_path), vim.trim(trust)) | ||||
|  | ||||
|       eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='remove', path='test_file'})}]])) | ||||
|       trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') | ||||
|       eq('', vim.trim(trust)) | ||||
|     end) | ||||
|  | ||||
|     it('trust returns error when buffer not associated to file', function() | ||||
|       command('new') | ||||
|       eq({false, 'buffer is not associated with a file'}, | ||||
|         exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) | ||||
|     end) | ||||
|   end) | ||||
| end) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jlll1
					Jlll1