Emacs automatically applies project-specific configurations through file-local and directory-local (.dir-locals.el) variables when opening a file or directory. While this feature ensures consistent settings across environments, it can cause security risks and persistent prompt fatigue when editing source code. Malicious .dir-locals.el files or file-local variables containing eval forms can execute arbitrary Lisp code if Emacs is configured to evaluate them, or if the user approves the relevant prompt by mistake.
This article outlines configurations for securing file-local and directory-local variables while reducing prompts.
Allowing safe local variables
The enable-local-variables variable controls how Emacs handles file-local variables. File-local variables are configurations embedded directly within a file. Here is an example of a file-local variables:
;; Local variables:
;; fill-column: 100
;; byte-compile-warnings: (not free-vars)
;; End:
To mitigate security risks and prevent interactive prompts, set enable-local-variables to :safe by adding the following to your init file:
(setq enable-local-variables :safe)
The benefit is that Emacs applies only verified safe values while silently dropping risky ones, reducing prompt fatigue without requiring manual confirmation. The tradeoff is that non-standard but safe project variables will be ignored unless their values are explicitly recognized as safe, for example through safe-local-variable-values or the safe-local-variable property.
Note: Setting enable-local-variables to nil disables normal processing of local variables entirely. (Specific settings, such as lexical-binding, remain active via the permanently-enabled-local-variables list.)
Whitelisting specific local variables
When you set enable-local-variables to :safe, Emacs silently ignores unverified variables. To prevent custom project variables from being ignored, you can explicitly mark specific variable/value pairs as safe using safe-local-variable-values, or define a predicate with the safe-local-variable property:
;; Whitelist a specific variable and value: Add the pair directly to the list.
(add-to-list 'safe-local-variable-values '(my-custom-variable . "expected-value"))
;; Whitelist a variable based on a predicate type: Define a property that
;; validates the variable type to accept any matching value.
(put 'my-custom-variable 'safe-local-variable #'stringp)
;; Whitelist a variable with a predicate: Only allow "hello" or "world"
;; as safe values.
(put 'my-custom-variable 'safe-local-variable
(lambda (val)
"Return t if VAL is 'hello' or 'world'."
(member val '("hello" "world"))))
Whitelisting specific directories
The safe-local-variable-directories variable allows you to designate specific paths where all directory-local variables are automatically considered safe. When a directory is added to this list, Emacs trusts every variable defined in its .dir-locals.el file without prompting. Note that this setting applies exclusively to directory-local variables and completely ignores file-local variables.
(add-to-list 'safe-local-variable-directories "/path/to/trusted/project/")
Disabling Local Eval Forms
The enable-local-eval variable controls whether Emacs processes eval forms in file-local and directory-local variable definitions. Unlike ordinary local variable assignments, eval can cause Emacs to evaluate arbitrary Lisp code when processing the local variables.
If your workflow does not require dynamic project configurations, you should disable this feature by setting this variable to nil:
(setq enable-local-eval nil)
The tradeoff is that useful evaluation forms, such as project-specific configurations dynamically generated through eval, will not execute.
Keeping default values for security
Emacs comes with the following defaults for local environment handling. Sticking with these defaults is already a good choice:
(setq enable-dir-local-variables t) ; Apply directory-local variables.
(setq enable-remote-dir-locals nil) ; Prevent loading dir-locals over TRAMP.
enable-dir-local-variables: This enables the use of directory-local variables (.dir-locals.el). Note that Emacs also reads.dir-locals-2.elif present, and non-file buffers like Dired can inherit these settings.enable-remote-dir-locals: Leaving this asnilprevents Emacs from loading directory-local variables from remote filesystems. This avoids applying potentially untrusted remote.dir-locals.elsettings and avoids the additional work required to search for them.
I really liked to read your articles, until you made the default text bold, which makes reading for me so tiring that since then I have been trying to skip as much of the text as possible. Is there a reason you switched to bold text ?
Hello E3D3,
Thank you for your feedback.
I changed the font-weight because a reader reported that the text appeared too thin on iPad.
I adjusted it based on your feedback. Does it look better now?
Hello James,
It looks good now, for me. I'm honored and thank you for the change, but don't think that I should be/set the norm, so think that you should ask other people about the prefered styling. Best regards.
Thanks for confirming!