From 8bb35eb81a48f14c4a1ef480c2bbb87ceb7cd8bb Mon Sep 17 00:00:00 2001 From: Kevin Oberlies Date: Wed, 17 Apr 2024 13:19:10 -0700 Subject: [PATCH] fix(ssh): escape all file paths for the ssh adapter (#353) * Escape all paths for ssh file changes using `vim.fn.shellescape()` * Change away from `vim.fn.shellescape` to custom implementation Really, only escape `'` with `'\\''` so that it will: - exit the single quote mode - escape out a single quote character - and get back into the single quote mode Also format long line so linter doesn't complain * Adding doc comments to the shellescape function * Adding actual words to the doc comment --- lua/oil/adapters/ssh/sshfs.lua | 53 ++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/lua/oil/adapters/ssh/sshfs.lua b/lua/oil/adapters/ssh/sshfs.lua index 2591749..c7cc4be 100644 --- a/lua/oil/adapters/ssh/sshfs.lua +++ b/lua/oil/adapters/ssh/sshfs.lua @@ -61,6 +61,12 @@ local function parse_ls_line(line) return name, type, meta end +---@param str string String to escape +---@return string Escaped string +local function shellescape(str) + return "'" .. str:gsub("'", "'\\''") .. "'" +end + ---@param url oil.sshUrl ---@return oil.sshFs function SSHFS.new(url) @@ -80,7 +86,7 @@ end ---@param callback fun(err: nil|string) function SSHFS:chmod(value, path, callback) local octal = permissions.mode_to_octal_str(value) - self.conn:run(string.format("chmod %s '%s'", octal, path), callback) + self.conn:run(string.format("chmod %s %s", octal, shellescape(path)), callback) end function SSHFS:open_terminal() @@ -105,21 +111,24 @@ function SSHFS:realpath(path, callback) if vim.endswith(abspath, ".") then abspath = abspath:sub(1, #abspath - 1) end - self.conn:run(string.format("ls -ald --color=never '%s'", abspath), function(ls_err, ls_lines) - local type - if ls_err then - -- If the file doesn't exist, treat it like a not-yet-existing directory - type = "directory" - else - assert(ls_lines) - local _ - _, type = parse_ls_line(ls_lines[1]) + self.conn:run( + string.format("ls -ald --color=never %s", shellescape(abspath)), + function(ls_err, ls_lines) + local type + if ls_err then + -- If the file doesn't exist, treat it like a not-yet-existing directory + type = "directory" + else + assert(ls_lines) + local _ + _, type = parse_ls_line(ls_lines[1]) + end + if type == "directory" then + abspath = util.addslash(abspath) + end + callback(nil, abspath) end - if type == "directory" then - abspath = util.addslash(abspath) - end - callback(nil, abspath) - end) + ) end) end @@ -131,7 +140,7 @@ local dir_meta = {} function SSHFS:list_dir(url, path, callback) local path_postfix = "" if path ~= "" then - path_postfix = string.format(" '%s'", path) + path_postfix = string.format(" %s", shellescape(path)) end self.conn:run("LANG=C ls -al --color=never" .. path_postfix, function(err, lines) if err then @@ -197,40 +206,40 @@ end ---@param path string ---@param callback fun(err: nil|string) function SSHFS:mkdir(path, callback) - self.conn:run(string.format("mkdir -p '%s'", path), callback) + self.conn:run(string.format("mkdir -p %s", shellescape(path)), callback) end ---@param path string ---@param callback fun(err: nil|string) function SSHFS:touch(path, callback) - self.conn:run(string.format("touch '%s'", path), callback) + self.conn:run(string.format("touch %s", shellescape(path)), callback) end ---@param path string ---@param link string ---@param callback fun(err: nil|string) function SSHFS:mklink(path, link, callback) - self.conn:run(string.format("ln -s '%s' '%s'", link, path), callback) + self.conn:run(string.format("ln -s %s %s", shellescape(link), shellescape(path)), callback) end ---@param path string ---@param callback fun(err: nil|string) function SSHFS:rm(path, callback) - self.conn:run(string.format("rm -rf '%s'", path), callback) + self.conn:run(string.format("rm -rf %s", shellescape(path)), callback) end ---@param src string ---@param dest string ---@param callback fun(err: nil|string) function SSHFS:mv(src, dest, callback) - self.conn:run(string.format("mv '%s' '%s'", src, dest), callback) + self.conn:run(string.format("mv %s %s", shellescape(src), shellescape(dest)), callback) end ---@param src string ---@param dest string ---@param callback fun(err: nil|string) function SSHFS:cp(src, dest, callback) - self.conn:run(string.format("cp -r '%s' '%s'", src, dest), callback) + self.conn:run(string.format("cp -r %s %s", shellescape(src), shellescape(dest)), callback) end function SSHFS:get_dir_meta(url)