diff options
author | Gregory Anders <greg@gpanders.com> | 2022-11-05 13:37:05 -0600 |
---|---|---|
committer | Gregory Anders <greg@gpanders.com> | 2022-11-17 08:23:41 -0700 |
commit | f1922e78a1df1b1d32779769432fb5586edf5fbb (patch) | |
tree | c80eece970f5b42cf04ad2161a655f33638ec7ce /runtime/lua/vim/secure.lua | |
parent | 9736605672e8648bbe8739a6fdd1c315183bce40 (diff) | |
download | rneovim-f1922e78a1df1b1d32779769432fb5586edf5fbb.tar.gz rneovim-f1922e78a1df1b1d32779769432fb5586edf5fbb.tar.bz2 rneovim-f1922e78a1df1b1d32779769432fb5586edf5fbb.zip |
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.
Diffstat (limited to 'runtime/lua/vim/secure.lua')
-rw-r--r-- | runtime/lua/vim/secure.lua | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua new file mode 100644 index 0000000000..341ff8df05 --- /dev/null +++ b/runtime/lua/vim/secure.lua @@ -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 |