3

So first, I realize that this question is slightly out of the ordinary for this site. I will explain the backstory to avoid the XY problem.

Over on Programming Puzzles and Code Golf, lots of users (especially me) like to answer programming challenges using vim, and since vim is great at manipulating strings, it does pretty well as a "Programming language" of sorts. However, testing these answers is a little bit of a pain. I've written 100+ keystroke answers in vim before, and actually typing this out to test it is a pain. Also, sometimes users who aren't as familiar with vim will make mistakes like pressing <C-a> instead of ctrl+a, or not starting vim with vim -u NONE -i NONE which can cause issues with mappings and registers.

So to remedy this, I'm working on a "vim interpreter" that you can run in your browser. You can enter in a sequence of keystrokes, such as iHello<esc>Yp, and the browser will display the text

Hello
Hello

But this creates a security hole. A malicious user could enter something like

:r !ls

to get a listing of files and directories on the server, and then something like

:e /foo/somefile.txt
dG:wq<cr>

To wipe out files. To get around this, initially I tried sourcing the following script before running the interpreter:

au BufReadPre * :q!
au BufWritePre * :q!

and calling neovim with

nvim -Z

to disable shell access.

However, then I found out the user could still enter

:noautocmd e /foo/somefile.txt

So I added this to the script:

cmap noautocmd q!<cr>
cmap noa q!<cr>

But, a user can still get around this with

:cunmap nog<bs>a

(The g<bs> to get around 'noa' being remapped as you type it)

So I added

cmap cu q!<cr>
cmap cunmap q!<cr>

This works as far as I know, but it feels like I'm just polishing poop and adding duct tape. So my two questions are

  1. Is my current method secure, or are there ways to get around what I have right now?

  2. Is there a better way to accomplish this? If this is a linux-based solution (the server is running ubuntu), rather than a vim-based solution, that's okay I guess, but since I don't own the server it will be running on, I'd rather have it be a vim-based solution, since I'm more in control of that and it's less work on the server-owner's part.

  • 1
    You probably should go with something *nix like. For example: chroot --userspec vim:vim /var/myvimjail/, after creating a user called vim and allowing him only that directory. I'm not adding this as an answer because it requires user creation and you said you do not own the server. – grochmal Jun 29 '16 at 18:02
  • You can safely forget it. VimL is Turing complete, and what you want to do is equivalent to the halting problem. Not going to work. – Sato Katsura Jun 29 '16 at 18:55
  • @SatoKatsura I don't see how that's relevant. Brainfuck is turing complete, but has no security issues or file access. – DJMcMayhem Jun 29 '16 at 18:57
  • (1) Brainfuck can't do what Vim can. (2) How do you know Brainfuck doesn't have security issues? (3) Feel free to ignore me. I'm just an old fart that has been around way too long after all. :) – Sato Katsura Jun 29 '16 at 19:12
1

Have you looked at :h :sandbox?

These items are not allowed in the sandbox:
  - changing the buffer text
  - defining or changing mapping, autocommands, functions, user commands
  - setting certain options (see |option-summary|)
  - setting certain v: variables (see |v:var|)  *E794*
  - executing a shell command
  - reading or writing a file
  - jumping to another buffer or editing a file
  - executing Python, Perl, etc. commands

If that's too restrictive, I would use a chroot like @grochmal said. But, I suppose you have other options:

  • Fork Vim and neuter file reading functions so they fail as if a file wasn't found.
  • Fork Vim and alter the sandbox behavior to allow changing buffer text.
  • Create a docker container that has no volumes and is destroyed after a session.
  • 1
    ... Except :sandbox only applies to expressions. – Sato Katsura Jun 29 '16 at 19:38
  • @SatoKatsura what's your point? – Tommy A Jun 29 '16 at 20:14
  • My point is, the OP wants to sandbox a script. :sandbox doesn't work for scripts. It only works for expressions. – Sato Katsura Jun 29 '16 at 20:21
  • I pointed out the sandbox feature as a way to restrict access. Loading files and scripts would be an implementation detail left to him. One method I can think of is: execute 'sandbox' join(readfile('script.vim'), '|') – Tommy A Jun 29 '16 at 20:29
  • ... Which would only protect the join operation. – Sato Katsura Jun 29 '16 at 20:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.