embear / vim-localvimrc

Search local vimrc files (".lvimrc") in the tree (root dir up to current dir) and load them.
http://www.vim.org/scripts/script.php?script_id=441
GNU General Public License v3.0
484 stars 28 forks source link

Errors with non-sandboxed scripts when the script triggers a recursive lvimrc load #72

Closed bew closed 6 years ago

bew commented 6 years ago

Repro steps:

I modified the plugin to add more debug bew/vim-localvimrc@da04133.

Minimal bug.vimrc (using vim-plug):

set nocompatible
call plug#begin('~/.config/nvim/plugged')
Plug 'embear/vim-localvimrc'
call plug#end()

let g:localvimrc_enable = 0
let g:localvimrc_debug = 1

Minimal .lvimrc:

call <SNR>12_LocalVimRCDebug(1, "from .lvimrc: before `bufdo write`")
bufdo write
call <SNR>12_LocalVimRCDebug(1, "from .lvimrc: after `bufdo write`")

Notice that I call the s:LocalVimRCDebug from the executed script, the script number is 12 for me on vim (it is 9 on neovim), but it might differ for you.

Open vim using: vim -u bug.vimrc file1 file2 Now run: :LocalVimRCEnable

It'll ask twice to load the .lvimrc file and accept the non-sandbox run. (not sure if it's intended) Edit: With the fix I describe at the end, it asks only once, so I guess it was not intended.

Then you'll see the errors:

Error detected while processing function <SNR>12_LocalVimRCEnable[6]..<SNR>12_LocalVimRC:
line  343:
E108: No such variable: "g:localvimrc_file"
line  344:
E108: No such variable: "g:localvimrc_file_dir"
line  345:
E108: No such variable: "g:localvimrc_script"
line  346:
E108: No such variable: "g:localvimrc_script_dir"
line  347:
E108: No such variable: "g:localvimrc_script_unresolved"
line  348:
E108: No such variable: "g:localvimrc_script_dir_unresolved"
line  349:
E108: No such variable: "g:localvimrc_sourced_once"
line  350:
E108: No such variable: "g:localvimrc_sourced_once_for_file"

The debug logs (from :LocalVimRCDebugShow):

== START settings ================================
localvimrc_enable = "0"
localvimrc_name = "['.lvimrc']"
localvimrc_event = "['BufWinEnter']"
localvimrc_event_pattern = "'*'"
localvimrc_reverse = "0"
localvimrc_count = "-1"
localvimrc_file_directory_only = "0"
localvimrc_sandbox = "1"
localvimrc_ask = "1"
localvimrc_whitelist = "['^$']"
localvimrc_blacklist = "['^$']"
localvimrc_persistent = "0"
localvimrc_persistence_file = "'/home/lesell_b/.localvimrc_persistent'"
localvimrc_autocmd = "1"
localvimrc_python2_enable = "1"
localvimrc_python3_enable = "1"
localvimrc_debug = "1"
localvimrc_debug_lines = "100"
localvimrc_checksum_func = "function('sha256')"
localvimrc_python_command = "'not checked for python'"
== END settings ==================================
autocommand triggered on event BufWinEnter
== START LocalVimRC() ============================
localvimrc.vim 1
== END LocalVimRC() (localvimrc is disabled) =====

enable processing of local vimrc files
== START LocalVimRC() ============================
localvimrc.vim 1
found files: [{'resolved': '/tmp/lvimrc_bug/.lvimrc', 'unresolved': '/tmp/lvimrc_bug/.lvimrc'}]
candidate files: [{'resolved': '/tmp/lvimrc_bug/.lvimrc', 'unresolved': '/tmp/lvimrc_bug/.lvimrc'}]
from .lvimrc: before `bufdo write`
unable to use sandbox on '/tmp/lvimrc_bug/.lvimrc': Vim(bufdo):E48: Not allowed in sandbox: bufdo write (/tmp/lvimrc_bug/.lvimrc, line 2)

foo before execute file without sandbox
foo command is: source /tmp/lvimrc_bug/.lvimrc

from .lvimrc: before `bufdo write`

autocommand triggered on event BufWinEnter
== START LocalVimRC() ============================
localvimrc.vim 1
found files: [{'resolved': '/tmp/lvimrc_bug/.lvimrc', 'unresolved': '/tmp/lvimrc_bug/.lvimrc'}]
candidate files: [{'resolved': '/tmp/lvimrc_bug/.lvimrc', 'unresolved': '/tmp/lvimrc_bug/.lvimrc'}]
from .lvimrc: before `bufdo write`
unable to use sandbox on '/tmp/lvimrc_bug/.lvimrc': Vim(bufdo):E48: Not allowed in sandbox: bufdo write (/tmp/lvimrc_bug/.lvimrc, line 2)

foo before execute file without sandbox
foo command is: source /tmp/lvimrc_bug/.lvimrc

from .lvimrc: before `bufdo write`
from .lvimrc: after `bufdo write`

foo after execute file without sandbox
sourced (without sandbox) /tmp/lvimrc_bug/.lvimrc
foo remove global vars
foo after remove global vars
== END LocalVimRC() ==============================

from .lvimrc: after `bufdo write`

foo after execute file without sandbox
sourced (without sandbox) /tmp/lvimrc_bug/.lvimrc
foo remove global vars
foo after remove global vars
== END LocalVimRC() ==============================

(I added some spaces for reading convenience)

You can see that there is 2 invocation of LocalVimRC()! This is visible in the logs: the second invocation is after from .lvimrc: before `bufdo write` and before the last from .lvimrc: after `bufdo write`.

What happens is that bufdo write will trigger the BufWinEnter event which will trigger the 2nd LocalVimRC(). I wonder why it only recurse once, maybe it is a security in autocmd

Solution?

I think a possible solution would be to add a guard around the .lvimrc script execution and at the beginning of LocalVimRC() to make sure it's not called (or it returned at the guard) during a .lvimrc execution.

WDYT?


Edit: With the mentionned fix it works perfectly, checkout my branch bew/vim-localvimrc@fix-recursive-call. I can make a PR if you think it's a good way to fix this

Also, the debug logs are much cleaner:

[...]
enable processing of local vimrc files
== START LocalVimRC() ============================
localvimrc.vim 1
found files: [{'resolved': '/tmp/lvimrc_bug/.lvimrc', 'unresolved': '/tmp/lvimrc_bug/.lvimrc'}]
candidate files: [{'resolved': '/tmp/lvimrc_bug/.lvimrc', 'unresolved': '/tmp/lvimrc_bug/.lvimrc'}]
Executing script, command is: sandbox source /tmp/lvimrc_bug/.lvimrc
from .lvimrc: before `bufdo write`
unable to use sandbox on '/tmp/lvimrc_bug/.lvimrc': Vim(bufdo):E48: Not allowed in sandbox: bufdo write (/tmp/lvimrc_bug/.lvimrc, line 2)
Executing script, command is: source /tmp/lvimrc_bug/.lvimrc
from .lvimrc: before `bufdo write`
autocommand triggered on event BufWinEnter
== START LocalVimRC() ============================
localvimrc.vim 1
== END LocalVimRC() (exec already running) =======
from .lvimrc: after `bufdo write`
sourced (without sandbox) /tmp/lvimrc_bug/.lvimrc
== END LocalVimRC() ==============================
embear commented 6 years ago

Your solution looks good to me at a first glance. I'll take a close look asap.

embear commented 6 years ago

I merged the two commits on your branch into my repo and did some code shuffling afterwards. Thanks for the report and the changes!

bew commented 6 years ago

You're welcome, I can't test right now, but if you took my commits it should be good, we can close this issue?

embear commented 6 years ago

I think everything works as expected. I reproduced the error and did check the correct behavior after my changes. If something is still not working we just reopen this issue. Closing for now.