From f1922e78a1df1b1d32779769432fb5586edf5fbb Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Sat, 5 Nov 2022 13:37:05 -0600 Subject: feat: add vim.secure.read() This function accepts a path to a file and prompts the user if the file is trusted. If the user confirms that the file is trusted, the contents of the file are returned. The user's decision is stored in a trust database at $XDG_STATE_HOME/nvim/trust. When this function is invoked with a path that is already marked as trusted in the trust database, the user is not prompted for a response. --- test/functional/lua/secure_spec.lua | 171 ++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 test/functional/lua/secure_spec.lua (limited to 'test/functional/lua/secure_spec.lua') diff --git a/test/functional/lua/secure_spec.lua b/test/functional/lua/secure_spec.lua new file mode 100644 index 0000000000..c348526d65 --- /dev/null +++ b/test/functional/lua/secure_spec.lua @@ -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) -- cgit From 5eb5f4948826e9d47685ea9e257409cc3e693614 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 22 Nov 2022 01:13:30 +0100 Subject: test: simplify platform detection (#21020) Extend the capabilities of is_os to detect more platforms such as freebsd and openbsd. Also remove `iswin()` helper function as it can be replaced by `is_os("win")`. --- test/functional/lua/secure_spec.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test/functional/lua/secure_spec.lua') diff --git a/test/functional/lua/secure_spec.lua b/test/functional/lua/secure_spec.lua index c348526d65..a5eeee8494 100644 --- a/test/functional/lua/secure_spec.lua +++ b/test/functional/lua/secure_spec.lua @@ -5,7 +5,7 @@ local eq = helpers.eq local clear = helpers.clear local command = helpers.command local pathsep = helpers.get_pathsep() -local iswin = helpers.iswin() +local is_os = helpers.is_os local curbufmeths = helpers.curbufmeths local exec_lua = helpers.exec_lua local feed_command = helpers.feed_command @@ -18,7 +18,7 @@ describe('vim.secure', function() local xstate = 'Xstate' setup(function() - helpers.mkdir_p(xstate .. pathsep .. (iswin and 'nvim-data' or 'nvim')) + helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) end) teardown(function() -- cgit From f004812b338340e5f5157aa68d09d3f0e5605c6c Mon Sep 17 00:00:00 2001 From: Jlll1 Date: Mon, 28 Nov 2022 20:23:04 +0100 Subject: 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 Co-authored-by: ii14 --- test/functional/lua/secure_spec.lua | 107 ++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) (limited to 'test/functional/lua/secure_spec.lua') diff --git a/test/functional/lua/secure_spec.lua b/test/functional/lua/secure_spec.lua index a5eeee8494..46ca2bba8f 100644 --- a/test/functional/lua/secure_spec.lua +++ b/test/functional/lua/secure_spec.lua @@ -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) -- cgit From 80b6edabe3e4203ee4bf50261af07a6a0495ef36 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Mon, 28 Nov 2022 15:40:50 -0700 Subject: refactor: rework parameter validation in vim.secure.trust() (#21223) --- test/functional/lua/secure_spec.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'test/functional/lua/secure_spec.lua') diff --git a/test/functional/lua/secure_spec.lua b/test/functional/lua/secure_spec.lua index 46ca2bba8f..6885253998 100644 --- a/test/functional/lua/secure_spec.lua +++ b/test/functional/lua/secure_spec.lua @@ -12,6 +12,7 @@ local feed_command = helpers.feed_command local feed = helpers.feed local funcs = helpers.funcs local pcall_err = helpers.pcall_err +local matches = helpers.matches describe('vim.secure', function() describe('read()', function() @@ -189,10 +190,15 @@ describe('vim.secure', function() end) it('returns error when passing both path and bufnr', function() - eq('path and bufnr are mutually exclusive', + matches('"path" and "bufnr" are mutually exclusive', pcall_err(exec_lua, [[vim.secure.trust({action='deny', bufnr=0, path='test_file'})]])) end) + it('returns error when passing neither path or bufnr', function() + matches('one of "path" or "bufnr" is required', + pcall_err(exec_lua, [[vim.secure.trust({action='deny'})]])) + 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')) -- cgit From f3bf1fbf600050fde155e6a1a766b6f848012208 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Mon, 5 Dec 2022 19:59:04 +0100 Subject: fix(secure): crash when hitting escape in prompt (#21283) - use pcall when calling vim.secure.read from C - catch keyboard interrupts in vim.secure.read, rethrow other errors - selecting "view" in prompt runs :view command - simplify lua stack cleanup with lua_gettop and lua_settop Co-authored-by: ii14 --- test/functional/lua/secure_spec.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test/functional/lua/secure_spec.lua') diff --git a/test/functional/lua/secure_spec.lua b/test/functional/lua/secure_spec.lua index 6885253998..2647b2be46 100644 --- a/test/functional/lua/secure_spec.lua +++ b/test/functional/lua/secure_spec.lua @@ -150,10 +150,10 @@ describe('vim.secure', function() ]]} feed('v') screen:expect{grid=[[ - ^ let g:foobar = 42 | + ^let g:foobar = 42 | {1:~ }| {1:~ }| - {2:]] .. cwd .. pathsep .. [[Xfile [RO]{MATCH:%s+}| + {2:]] .. funcs.fnamemodify(cwd, ':~') .. pathsep .. [[Xfile [RO]{MATCH:%s+}| | {1:~ }| {4:[No Name] }| @@ -166,7 +166,7 @@ describe('vim.secure', function() -- Cannot write file pcall_err(command, 'write') - eq(false, curbufmeths.get_option('modifiable')) + eq(true, curbufmeths.get_option('readonly')) end) end) -- cgit