aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS.org6
-rw-r--r--compat-30.el55
-rw-r--r--compat-macs.el7
-rw-r--r--compat-tests.el26
-rw-r--r--compat.texi29
5 files changed, 122 insertions, 1 deletions
diff --git a/NEWS.org b/NEWS.org
index acc06d0..2f38878 100644
--- a/NEWS.org
+++ b/NEWS.org
@@ -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).
diff --git a/compat-30.el b/compat-30.el
index e5a3a9e..02ef426 100644
--- a/compat-30.el
+++ b/compat-30.el
@@ -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")
@@ -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
diff --git a/compat-macs.el b/compat-macs.el
index ffd5223..d200fbb 100644
--- a/compat-macs.el
+++ b/compat-macs.el
@@ -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.
@@ -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.
@@ -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)
diff --git a/compat-tests.el b/compat-tests.el
index b736015..b8ae0c1 100644
--- a/compat-tests.el
+++ b/compat-tests.el
@@ -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
diff --git a/compat.texi b/compat.texi
index 70afd76..b8b1335 100644
--- a/compat.texi
+++ b/compat.texi
@@ -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
@@ -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