exrc is now secure in neovim
This PR adds a prompt to neovim 0.9 before loading a local
.nvimrc file, when
exrc is enabled.
If you are using regular vim or stuck on an older neovim version, this post might still be interesting for you. The rest of this post is unchanged.
Why do you want per-project vim configs?
Usually, I have all my vim configurations stored in my dotfiles. These global configs are easy, as they don't change too often, and are applicable for all my personal projects. So when they do change, I do a git pull on all my devices and be done.
However, I don't only contribute to my own projects but also to other peoples' projects. So from time to time it happens, unfortunately, that some projects' style guides disagree with certain things in my config.
Up to now, I used to have
set exrc in my
init.vim file, for these cases. This setting enables reading of local
.nvimrc config files in the current directory. The major downside of this solution is that it reads and executes everything from (1) a hidden file (2) on editor startup. That is a nasty combination that only waits for a desaster to happen. If I carelessly clone a malicious repository and open vim in one of its directories, it is already too late.
set secure not enough?
There are a lot of stackoverflow answers and blog posts recommending
set exrc as a solution to the per-project configuration. Usually they recommend to set the 'secure' setting additionally, to make it a safe solution. Unfortunately, this is not sufficient. The neovim help has the following to say:
Note that there is a pretty strong restriction on the usefulness of this setting hidden in this sentence. Specifically:
On Unix this option is only used if the ".nvimrc" or ".exrc" is not owned by you.
This does not cover our threat model of a malicious git repository, at all! When cloning a repository, we own all files in there, of course, so this setting does absolutely nothing.
Neovim has decided to deprecate the exrc option. So for neovim users the exrc setting will not even work anymore in the future.
Since I use direnv on many projects anyways, I decided to make it manage my per-project vim configs, too. This is convenient, because direnv needs to solve the security implications of loading random files into the shell anyways, so it should solve the 'malicious git repo' use case automatically. And to nobody's surprise, it does:
In some target folder, create an .envrc file and add some export(1) and unset(1) directives in it.
On the next prompt you will notice that direnv complains about the .envrc being blocked. This is the security mechanism to avoid loading new files automatically. Otherwise any git repo that you pull, or tar archive that you unpack, would be able to wipe your hard drive once you cd into it.
So here we are pretty sure that it won’t do anything bad. Type direnv allow . and watch direnv loading your new environment
So to make vim load my project's
.init.vim file, I simply added the following
.envrc to my projects:
export VIMINIT='source $MYVIMRC'
This loads the custom
.init.vim whenever I open vim in the project's directory. However, it will only be loaded if I specifically ran
direnv allow . in that project. This is a sufficient safeguard against malicious repos, since it is not fully automatic. I can decide if I want to load other people's
.envrc files at all, and if I do, I carefully review all
.envrc files in the project.
After allowing it once, direnv will load the variables automatically, until the
.envrc file changes or is specifically disallowed.
To extend my global
init.vim, I source it and overwrite only the project-specific settings:
I hope this helps. If someone has an easier method, please let me know.