Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add missing calls to the shellescape() function #1099

Merged
merged 9 commits into from
Apr 10, 2020

Conversation

lifecrisis
Copy link
Contributor

@lifecrisis lifecrisis commented Apr 9, 2020

Description of Changes

This is a security fix for nerdtree_plugin/fs_menu.vim that adds several missing calls to shellescape() that are actually required. It also adds missing shellescape() calls in two other places. This is necessary to prevent shell command injection.

It is quite an unlikely exploit, however.

N.B. I suggest that other calls to system() throughout the NERDTree code be audited for correct usage of shellescape(). Some of them seem to use a custom escaping function that may or may not be correct. These probably should be converted to use the shellescape() function.

See commit messages for more details about some of the ancillary changes.


New Version Info

Author's Instructions

  • Derive a new MAJOR.MINOR.PATCH version number. Increment the:
    • MAJOR version when you make incompatible API changes
    • MINOR version when you add functionality in a backwards-compatible manner
    • PATCH version when you make backwards-compatible bug fixes
  • Update CHANGELOG.md, following the established pattern.

Collaborator's Instructions

  • Review CHANGELOG.md, suggesting a different version number if necessary.
  • After merge, tag the merge commit, e.g. git tag -a 3.1.4 -m "v3.1.4" && git push origin --tags

The following improvements were made...

  - Use variable sigils
  - Shorten a local variable name
  - Prefer an early return over testing for a negative
  - Switch to single quotes
  - Call "shellescape()" to pass a command argument [IMPORTANT!]

The final change is a critical fix for the security and reliability
of this function (see ":h system()").

Similar fixes for the other functions in this script will follow.
This commit makes several style improvements and adds a missing call
to the "shellescape()" function.

See also: 56cfbcf
This commit handles the edge case where a user invokes the "reveal"
function on "/" on a Linux box.  There is nothing to do but open the
root directory itself since "/" has no parent.
@lifecrisis lifecrisis requested a review from PhilRunninger April 9, 2020 00:41
I initially thought that there were several more locations where
a call to "shellescape()" was required but omitted.  However, there
are only two.  I suppose I should have taken the time to look.

Fixing these was easy.  I would be surprised if this change breaks
anything on the user side.
@lifecrisis
Copy link
Contributor Author

lifecrisis commented Apr 9, 2020

Note: I went ahead and added the remaining shellescape() calls that were missing. There were only two, so it was easy.

I have a few more improvements I want to make that are related to this, so be on the look-out for future PRs.

As far as I can tell, though, this PR is done. Since this is security related, please review carefully.

Use a more fitting description of the change...
@lifecrisis lifecrisis changed the title Add missing shellescape() calls in "fs_menu.vim" Add missing calls to the shellescape() function Apr 9, 2020
@@ -388,44 +388,67 @@ endfunction

" FUNCTION: NERDTreeQuickLook() {{{1
function! NERDTreeQuickLook()
let treenode = g:NERDTreeFileNode.GetSelected()
if treenode !=# {}
call system("qlmanage -p 2>/dev/null '" . treenode.path.str() . "'")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lifecrisis , do you have a way to test this? These single quotes are thwarting my attempts to do the command injection in the old code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of these will allow injection when operating on a file that looks like...

'; touch FOOBAR ; echo '

Opening this file on Linux with mo, and the file FOOBAR will appear in Vim's current working directory. This snippet provides a simplified example of the mechanism:

func! g:Foo()
    return "'; touch FOOBAR ; echo '" 
endfunc

call system("xdg-open '" . g:Foo() . "' &")

This problem seems like it would never happen, but you can do some funky things in the terminal... like concealing characters that actually are contained in the path using terminal escape codes. It's definitely better to use shellescape().

@@ -199,7 +199,7 @@ function! s:Path.copy(dest)
let cmd_prefix = (self.isDirectory ? g:NERDTreeCopyDirCmd : g:NERDTreeCopyFileCmd)
endif

let cmd = cmd_prefix . ' ' . escape(self.str(), self._escChars()) . ' ' . escape(a:dest, self._escChars())
let cmd = cmd_prefix . ' ' . shellescape(self.str()) . ' ' . shellescape(a:dest)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does shellescape() take into account the OS and the &shellslash setting?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know.

Because this is security related, though, I would recommend using it and only reverting it if people complain.

call system("open '" . treenode.path.str() . "'")
let l:node = g:NERDTreeFileNode.GetSelected()

if empty(l:node)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you following a certain pattern here, instead of

if !empty()
    do the thing
endif

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always prefer early returns. They seem cleaner to me; i.e., I think they're simpler and read better.

Strictly speaking, though, we should probably be using try statements here. I always try to follow EAFP when possible.

Copy link
Member

@PhilRunninger PhilRunninger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. 👍

@PhilRunninger PhilRunninger merged commit f767dd3 into preservim:master Apr 10, 2020
@lifecrisis lifecrisis deleted the shellescape branch April 10, 2020 10:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants