Skip to content

Commit

Permalink
compat-30: untrusted-content, trusted-files, trusted-content-p
Browse files Browse the repository at this point in the history
  • Loading branch information
minad committed Dec 15, 2024
1 parent f06c24c commit 5be942f
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 1 deletion.
6 changes: 6 additions & 0 deletions NEWS.org
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
#+link: compat-gh https://github.com/emacs-compat/compat/issues/
#+options: toc:nil num:nil author:nil

* Development

- compat-30: New variable =untrusted-content=.
- compat-30: New variable =trusted-files=.
- compat-30: New function =trusted-content-p=.

* Release of "Compat" Version 30.0.0.0

- compat-28: Mark =subr-native-elisp-p= as obsolete (renamed in Emacs 30).
Expand Down
55 changes: 55 additions & 0 deletions compat-30.el
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@
(eval-when-compile (load "compat-macs.el" nil t t))
(compat-require compat-29 "29.1")

(compat-version "29.3")
(compat-defvar untrusted-content nil ;; <compat-tests:untrusted-content>
"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."
:local permanent)

;; TODO Update to 30.1 as soon as the Emacs emacs-30 branch version bumped
(compat-version "30.0.50")

Expand All @@ -50,6 +59,52 @@ See also `find-buffer-visiting'."

;;;; Defined in files.el

(compat-defvar trusted-files nil ;; <compat-tests:trusted-files>
"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.
- else it is considered as 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."
:risky t)

(compat-defun trusted-content-p () ;; <compat-tests: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-files'."
;; We compare with `buffer-file-truename' i.s.o `buffer-file-name'
;; to try and avoid marking as trusted a file that's merely accessed
;; via a symlink that happens to be inside a trusted dir.
(and (not untrusted-content)
buffer-file-truename
(with-demoted-errors "trusted-content-p: %S"
(let ((exists (file-exists-p buffer-file-truename)))
(or
(eq trusted-files :all)
;; We can't avoid trusting the user's init file.
(if (and exists user-init-file)
(file-equal-p buffer-file-truename user-init-file)
(equal buffer-file-truename user-init-file))
(let ((file (abbreviate-file-name buffer-file-truename))
(trusted nil))
(dolist (tf trusted-files)
(when (or (if exists (file-equal-p tf file) (equal tf 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 "/" tf)
(string-prefix-p tf file)))
(setq trusted t)))
trusted))))))

(compat-defun require-with-check (feature &optional filename noerror) ;; <compat-tests:require-with-check>
"If FEATURE is not already loaded, load it from FILENAME.
This is like `require' except if FEATURE is already a member of the list
Expand Down
7 changes: 6 additions & 1 deletion compat-macs.el
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ definition is generated.
- :constant :: Mark the variable as constant if t.
- :risky :: Mark the variable as risky if t.
- :local :: Make the variable buffer-local if t. If the value is
`permanent' make the variable additionally permanently local.
Expand All @@ -232,11 +234,13 @@ definition is generated.
(doc-string 3) (indent 2))
(compat-macs--guard
attrs (list :constant #'booleanp
:risky #'booleanp
:local (lambda (x) (memq x '(nil t permanent)))
:obsolete (lambda (x) (or (booleanp x) (stringp x))))
(lambda (constant local obsolete)
(lambda (constant risky local obsolete)
(compat-macs--strict (not (boundp name)) "%s already defined" name)
(compat-macs--assert (not (and constant local)) "Both :constant and :local")
(compat-macs--assert (not (and local risky)) "Both :risky and :local")
;; The boundp check is performed at runtime to make sure that we never
;; redefine an existing definition if Compat is loaded on a newer Emacs
;; version.
Expand All @@ -250,6 +254,7 @@ definition is generated.
',name ,(if (stringp obsolete) obsolete "No substitute")
,compat-macs--version))))
,@(and local `((make-variable-buffer-local ',name)))
,@(and risky `((put ',name 'risky-local-variable t)))
,@(and (eq local 'permanent) `((put ',name 'permanent-local t)))))))

(defmacro compat-version (version)
Expand Down
26 changes: 26 additions & 0 deletions compat-tests.el
Original file line number Diff line number Diff line change
Expand Up @@ -3232,5 +3232,31 @@
(let ((completion-category-overrides '((compat-test (a . 10)))))
(should-equal 10 (compat-call completion-metadata-get md 'a))))))

(ert-deftest compat-untrusted-content ()
(should (local-variable-if-set-p 'untrusted-content)))

(ert-deftest compat-trusted-files ()
(static-if (< emacs-major-version 30) ;; TODO reenable on Emacs 30
(progn
(should (boundp 'trusted-files))
(should (risky-local-variable-p 'trusted-files)))))

(ert-deftest compat-trusted-content-p ()
(static-if (< emacs-major-version 30) ;; TODO reenable on Emacs 30
(progn
(should-not (trusted-content-p))
(let ((untrusted-content t)
(buffer-file-truename user-init-file))
(should-not (trusted-content-p)))
(let ((buffer-file-truename (expand-file-name "compat-tests.el")))
(should-not (trusted-content-p)))
(let ((buffer-file-truename (expand-file-name "compat-tests.el"))
(trusted-files '("compat-tests.el")))
(should (trusted-content-p)))
(let ((untrusted-content t)
(buffer-file-truename (expand-file-name "compat-tests.el"))
(trusted-files '("compat-tests.el")))
(should-not (trusted-content-p))))))

(provide 'compat-tests)
;;; compat-tests.el ends here
29 changes: 29 additions & 0 deletions compat.texi
Original file line number Diff line number Diff line change
Expand Up @@ -2264,6 +2264,13 @@ care.
The @code{defcustom} type @code{key} introduced in Emacs 29.1 is
made available by Compat.

@defvar untrusted-content
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.
@end defvar

@c copied from lispref/loading.texi
@defvar lisp-directory
This variable holds a string naming the directory which holds Emacs's
Expand Down Expand Up @@ -3326,6 +3333,28 @@ older than 30.1. Note that due to upstream changes, it might happen
that there will be the need for changes, so use these functions with
care.

@defvar trusted-files
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
@code{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.
- else it is considered as 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 @code{:all}, in which case Emacs will trust all
files, which opens a gaping security hole.
@end defvar

@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 @code{trusted-files}.
@end defun

@c copied from lispref/nonascii.texi
@defun char-to-name char
This function returns the Unicode name of @var{char}. It returns
Expand Down

0 comments on commit 5be942f

Please sign in to comment.