;; macro-expansion involves arbitrary code execution,
;; only attempt it in trusted buffers.
(cons (current-buffer)
- (when (trusted-content-p)
+ (when (trust-trusted-content-p)
(find-function--search-by-expanding-macros
(current-buffer) symbol type)))))))))))
(or (eq scope-safe-macros t)
(memq macro scope-safe-macros)
(get macro 'safe-macro)
- (trusted-content-p))))
+ (trust-trusted-content-p))))
(defvar warning-minimum-log-level)
special-variable-p local-variable-p
local-variable-if-set-p
default-value set-default make-local-variable
- buffer-local-value add-to-list add-to-history
+ buffer-local-value add-to-list add-to-history find-buffer
add-hook remove-hook run-hook-with-args run-hook-wrapped))
(put sym 'scope-analyzer #'scope--analyze-boundp))
Some modes may wish to set this to nil to prevent directory-local
settings being applied, but still respect file-local ones.")
-(defvar-local untrusted-content nil
- "Non-nil means that current buffer originated from an untrusted source.
-Email clients and some other modes may set this non-nil to mark the
-buffer contents as untrusted.
-
-This variable might be subject to change without notice.")
-(put 'untrusted-content 'permanent-local t)
-
-(defcustom trusted-content nil
- "List of files and directories whose content we trust.
-Be extra careful here since trusting means that Emacs might execute the
-code contained within those files and directories without an explicit
-request by the user.
-One important case when this might happen is when `flymake-mode' is
-enabled (for example, when it is added to a mode hook).
-Each element of the list should be a string:
-- If it ends in \"/\", it is considered as a directory name and means that
- Emacs should trust all the files whose name has this directory as a prefix.
-- Otherwise, it is considered a file name.
-Use abbreviated file names. For example, an entry \"~/mycode/\" means
-that Emacs will trust all the files in your directory \"mycode\".
-This variable can also be set to `:all', in which case Emacs will trust
-all files, which opens a gaping security hole. Emacs Lisp authors
-should note that this value must never be set by a major or minor mode."
- :type '(choice (repeat :tag "List" file)
- (const :tag "Trust everything (DANGEROUS!)" :all))
- :version "30.1")
-(put 'trusted-content 'risky-local-variable t)
-
-(defun trusted-content-p ()
- "Return non-nil if we trust the contents of the current buffer.
-Here, \"trust\" means that we are willing to run code found inside of it.
-See also `trusted-content'."
- (and (not untrusted-content)
- (or
- (eq trusted-content :all)
- (and
- (or vc-followed-link buffer-file-name)
- (with-demoted-errors "trusted-content-p: %S"
- (let* ((file (expand-file-name (or vc-followed-link buffer-file-name)))
- (exists (file-exists-p file)))
- (catch 'ball
- (dolist (tf trusted-content)
- (let ((ef (expand-file-name tf)))
- (and
- (or (if exists (file-equal-p ef file) (equal ef file))
- ;; We don't use `file-in-directory-p' here,
- ;; because we want to err on the conservative
- ;; side: "guilty until proven innocent".
- (and (string-suffix-p "/" ef)
- (string-prefix-p ef file)))
- (throw 'ball t)))))))))))
-
(defcustom enable-local-eval nil
"Control processing of the \"variable\" `eval' in a file's local variables.
The value can be t, nil or something else.
(setq coding-system (mm-find-buffer-file-coding-system)))
(setq text (buffer-string))))
(with-temp-buffer
- (setq untrusted-content t)
+ (trust-set-buffer-trust)
(insert (cond ((eq charset 'gnus-decoded)
(with-current-buffer (mm-handle-buffer handle)
(buffer-string)))
(with-current-buffer (get-buffer-create buf-name)
(unless (zerop (buffer-size)) (setq old-point (point)))
(inferior-emacs-lisp-mode)
- (setq-local trusted-content :all)))
+ (trust-set-buffer-trust nil t)))
(pop-to-buffer-same-window buf-name)
(when old-point (push-mark old-point))))
:package-version '(Org . "8.0")
:type 'boolean)
-(unless (boundp 'untrusted-content)
- (defvar untrusted-content nil))
-(defvar untrusted-content) ; defined in files.el since Emacs 29.3
(defvar org--latex-preview-when-risky nil
"If non-nil, enable LaTeX preview in Org buffers from unsafe source.
when Org mode is unable to determine the origin of the Org buffer.
An Org buffer is considered to be from unsafe source when the
-variable `untrusted-content' has a non-nil value in the buffer.
+`trust-trusted-content-p' returns nil in the buffer.
If this variable is non-nil, LaTeX previews are rendered unconditionally.
(interactive "P")
(cond
((not (display-graphic-p)) nil)
- ((and untrusted-content (not org--latex-preview-when-risky)) nil)
+ ((not (or (trust-trusted-content-p) org--latex-preview-when-risky)) nil)
;; Clear whole buffer.
((equal arg '(64))
(org-clear-latex-preview (point-min) (point-max))
(2 font-lock-function-name-face prepend t)))
"Highlighting patterns for Emacs Lisp mode.")
+(defvar elisp-mode-line-trusted-buffer
+ `(:eval
+ (if (trust-trusted-content-p)
+ ,(propertize "!"
+ 'face 'success
+ 'help-echo "mouse-2: Stop trusting this buffer\nBuffer is trusted"
+ 'mouse-face 'mode-line-highlight
+ 'follow-link t
+ 'keymap
+ (let ((map (make-sparse-keymap)))
+ (define-key map [mode-line follow-link] 'mouse-face)
+ (define-key map [mode-line mouse-2]
+ (lambda (event)
+ (interactive "e")
+ (with-current-buffer (window-buffer (posn-window (event-start event)))
+ (trust-set-buffer-trust)
+ (message "Buffer %S is now UNTRUSTED" (current-buffer))
+ (force-mode-line-update))))
+ map))
+ ,(propertize "?"
+ 'face 'error
+ 'help-echo "mouse-2: Trust this buffer\nBuffer is untrusted"
+ 'mouse-face 'mode-line-highlight
+ 'follow-link t
+ 'keymap
+ (let ((map (make-sparse-keymap)))
+ (define-key map [mode-line follow-link] 'mouse-face)
+ (define-key map [mode-line mouse-2]
+ (lambda (event)
+ (interactive "e")
+ (with-current-buffer (window-buffer (posn-window (event-start event)))
+ (trust-set-buffer-trust nil t)
+ (message "Buffer %S is now TRUSTED" (current-buffer))
+ (force-mode-line-update))))
+ map)))))
+
+(put 'elisp-mode-line-trusted-buffer 'risky-local-variable t)
+
;;;###autoload
(define-derived-mode emacs-lisp-mode lisp-data-mode
`("ELisp"
face warning
mouse-face mode-line-highlight
local-map ,elisp--dynlex-modeline-map))
- (:eval (if (trusted-content-p)
- ,(propertize "!"
- 'face 'success
- 'help-echo "mouse-2: Stop trusting this buffer\nBuffer is trusted"
- 'mouse-face 'mode-line-highlight
- 'follow-link t
- 'keymap
- (let ((map (make-sparse-keymap)))
- (define-key map [mode-line follow-link] 'mouse-face)
- (define-key map [mode-line mouse-2]
- (lambda (event)
- (interactive "e")
- (with-current-buffer (window-buffer (posn-window (event-start event)))
- (setq-local trusted-content nil)
- (message "Buffer %S is now UNTRUSTED" (current-buffer))
- (force-mode-line-update))))
- map))
- ,(propertize "?"
- 'face 'error
- 'help-echo "mouse-2: Trust this buffer\nBuffer is untrusted"
- 'mouse-face 'mode-line-highlight
- 'follow-link t
- 'keymap
- (let ((map (make-sparse-keymap)))
- (define-key map [mode-line follow-link] 'mouse-face)
- (define-key map [mode-line mouse-2]
- (lambda (event)
- (interactive "e")
- (with-current-buffer (window-buffer (posn-window (event-start event)))
- (setq-local trusted-content :all)
- (message "Buffer %S is now TRUSTED" (current-buffer))
- (force-mode-line-update))))
- map)))))
+ elisp-mode-line-trusted-buffer)
"Major mode for editing Lisp code to run in Emacs.
\\<emacs-lisp-mode-map>
- \\[backward-delete-char-untabify] converts tabs to spaces as it moves back.
(add-hook 'flymake-diagnostic-functions #'elisp-flymake-checkdoc nil t)
(add-hook 'flymake-diagnostic-functions
#'elisp-flymake-byte-compile nil t)
+ (add-hook
+ 'trust-buffer-hook
+ (lambda (arg)
+ (when (and arg (bound-and-true-p flymake-mode))
+ (flymake-enable-backend 'elisp-flymake-byte-compile)))
+ nil t)
(add-hook 'refactor-backend-functions #'elisp-refactor-backend nil t)
(add-hook 'context-menu-functions #'elisp-context-menu 10 t)
(setq-local imenu-create-index-function #'elisp-create-imenu-index)
["Evaluate Defun" eval-defun
:help "Evaluate the top-level form containing point, or after point"]))
-(define-derived-mode lisp-interaction-mode emacs-lisp-mode "Lisp Interaction"
+(define-derived-mode lisp-interaction-mode emacs-lisp-mode
+ '("Lisp Interaction" elisp-mode-line-trusted-buffer)
"Major mode for typing and evaluating Lisp forms.
Like Lisp mode except that \\[eval-print-last-sexp] evals the Lisp expression
before point, and prints its value into the buffer, advancing point.
"A Flymake backend for elisp byte compilation.
Spawn an Emacs process that byte-compiles a file representing the
current buffer state and calls REPORT-FN when done."
- (unless (trusted-content-p)
+ (unless (trust-trusted-content-p)
;; FIXME: Use `bwrap' and friends to compile untrusted content.
- ;; FIXME: We emit a message *and* signal an error, because by default
- ;; Flymake doesn't display the warning it puts into "*flmake log*".
- (message "Disabling elisp-flymake-byte-compile in %s (untrusted content)"
- (buffer-name))
(user-error "Disabling elisp-flymake-byte-compile in %s (untrusted content)"
(buffer-name)))
(when elisp-flymake--byte-compile-process
(flymake--state-disabled state) explanation
(flymake--state-reported-p state) t)))
+;;;###autoload
+(defun flymake-enable-backend (backend)
+ "Enable Flymake BACKEND."
+ (flymake--with-backend-state backend state
+ (setf (flymake--state-disabled state) nil)))
+
(defun flymake--run-backend (backend &optional args)
"Run the backend BACKEND, re-enabling if necessary.
ARGS is a keyword-value plist passed to the backend along
#'elisp-completion-at-point nil t)
(add-hook 'minibuffer-exit-hook
#'minibuffer-kill-completions-buffer nil t)
- (setq-local trusted-content :all)
+ (trust-set-buffer-trust nil t)
(run-hooks 'eval-expression-minibuffer-setup-hook))
(read-from-minibuffer prompt initial-contents
read--expression-map t
(when initial-scratch-message
(insert (substitute-command-keys initial-scratch-message))
(set-buffer-modified-p nil))
- (funcall initial-major-mode))
+ (funcall initial-major-mode)
+ (trust-set-buffer-trust nil t))
scratch)))
(defun scratch-buffer ()
--- /dev/null
+;;; trust.el --- Trust API -*- lexical-binding:t -*-
+
+;; Copyright (C) 2025 Eshel Yaron
+
+;; Author: Eshel Yaron <me@eshelyaron.com>
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(defgroup trust ()
+ "Trust related settings."
+ :group 'files)
+
+(defcustom trust--trusted-content nil
+ "List of files and directories whose content we trust.
+Be extra careful here since trusting means that Emacs might execute the
+code contained within those files and directories without an explicit
+request by the user.
+One important case when this might happen is when `flymake-mode' is
+enabled (for example, when it is added to a mode hook).
+Each element of the list should be a string:
+- If it ends in \"/\", it is considered as a directory name and means that
+ Emacs should trust all the files whose name has this directory as a prefix.
+- Otherwise, it is considered a file name.
+Use abbreviated file names. For example, an entry \"~/mycode/\" means
+that Emacs will trust all the files in your directory \"mycode\".
+This variable can also be set to `:all', in which case Emacs will trust
+all files, which opens a gaping security hole. Emacs Lisp authors
+should note that this value must never be set by a major or minor mode."
+ :type '(choice (repeat :tag "List" file)
+ (const :tag "Trust everything (DANGEROUS!)" :all))
+ :version "30.1")
+(put 'trust--trusted-content 'risky-local-variable t)
+
+;;;###autoload
+(defun trust-trusted-content-p ()
+ "Return non-nil if we trust the contents of the current buffer.
+Here, \"trust\" means that we are willing to run code found inside of it.
+See also `trust--trusted-content'."
+ (or
+ (eq trust--trusted-content :all)
+ (and
+ (or vc-followed-link buffer-file-name)
+ (with-demoted-errors "trusted-content-p: %S"
+ (let* ((file (expand-file-name (or vc-followed-link buffer-file-name)))
+ (exists (file-exists-p file)))
+ (catch 'ball
+ (dolist (tf trust--trusted-content)
+ (let ((ef (expand-file-name tf)))
+ (and
+ (or (if exists (file-equal-p ef file) (equal ef file))
+ ;; We don't use `file-in-directory-p' here,
+ ;; because we want to err on the conservative
+ ;; side: "guilty until proven innocent".
+ (and (string-suffix-p "/" ef)
+ (string-prefix-p ef file)))
+ (throw 'ball t))))))))))
+
+(defvar trust-buffer-hook nil
+ "Abnormal hook run when buffer is affected by change in trust settings.
+Functions on this hook are called with one argument, which is non-nil if
+the current buffer is now trusted, and nil if the it is now untrusted.")
+
+;;;###autoload
+(defun trust-set-buffer-trust (&optional buf trust)
+ (interactive
+ (let ((trust (unless current-prefix-arg :all)))
+ (list (read-buffer
+ (format-prompt (if trust "Trust buffer" "Untrust buffer"))
+ (buffer-name (current-buffer)) t
+ (lambda (b)
+ (not (eq trust
+ (when (buffer-local-value
+ 'trust--trusted-content (get-buffer b))
+ :all)))))
+ trust)))
+ (with-current-buffer (or buf (current-buffer))
+ (setq-local trust--trusted-content (when trust :all))
+ (run-hook-with-args 'trust-buffer-hook trust)))
+
+;;;###autoload
+(defun trust-set-file-trust (file &optional trust)
+ (interactive
+ (let ((trust (not current-prefix-arg))
+ (def (when default-directory (abbreviate-file-name default-directory))))
+ (list (read-file-name
+ (format-prompt (if trust "Trust file" "Untrust file") def)
+ nil def)
+ trust)))
+ (let* ((file (expand-file-name (if (file-directory-p file)
+ (file-name-as-directory file)
+ file)))
+ (exists (file-exists-p file))
+ (cur (delete file (default-value 'trust--trusted-content))))
+ (setq-default trust--trusted-content (if trust (cons file cur) cur))
+ (dolist (buf (buffer-list))
+ (with-current-buffer buf
+ (when-let ((fbuf (or vc-followed-link buffer-file-name))
+ (fbuf (expand-file-name fbuf)))
+ (when (or (and (string-suffix-p "/" file) (string-prefix-p file fbuf))
+ (if exists (file-equal-p fbuf file) (equal fbuf file)))
+ (run-hook-with-args 'trust-buffer-hook trust)))))))
+
+(provide 'trust)
+;;; trust.el ends here
(declare-function compilation--message->loc nil "compile")
(ert-deftest find-func-tests--locate-macro-generated-symbols () ;bug#45443
- (let ((trusted-content
- (list (abbreviate-file-name (find-library-name "compile"))
- (abbreviate-file-name (find-library-name "cc-mode")))))
+ (let ((trust--trusted-content
+ (list (find-library-name "compile") (find-library-name "cc-mode"))))
(should (cdr (find-function-search-for-symbol
#'compilation--message->loc nil "compile")))
(should (cdr (find-function-search-for-symbol