summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-23 03:31:04 -0500
committerCraig Jennings <c@cjennings.net>2026-05-23 03:31:04 -0500
commit8fc6432d44e41787fb7f69ad792f50cc906393d5 (patch)
tree1a0554a9c7421e234a2d6ee5117d06be4debdab1
parentbeb8fed5a4143710e0a9b2a526f12aae303d2cf8 (diff)
downloaddotemacs-8fc6432d44e41787fb7f69ad792f50cc906393d5.tar.gz
dotemacs-8fc6432d44e41787fb7f69ad792f50cc906393d5.zip
fix(dirvish): guard nil file and reject path-traversal playlist names
cj/set-wallpaper passed `(dired-file-name-at-point)` straight to `expand-file-name`, so running it with no file at point raised a bare `wrong-type-argument` instead of a clear error. cj/dired-create-playlist-from-marked expanded the raw playlist name under `music-dir` without checking it, so a name like "../foo" or "/etc/foo" would write outside the music directory. I added a nil-file guard to set-wallpaper and a `cj/--playlist-name-safe-p` check that rejects any name carrying a directory separator before the path is built. Both paths now fail cleanly with a user-error. Regression tests went into the existing wrapper and playlist test files.
-rw-r--r--modules/dirvish-config.el37
-rw-r--r--tests/test-dirvish-config-playlist.el15
-rw-r--r--tests/test-dirvish-config-wrappers.el6
3 files changed, 46 insertions, 12 deletions
diff --git a/modules/dirvish-config.el b/modules/dirvish-config.el
index 774c9ab7..8fe5c7b8 100644
--- a/modules/dirvish-config.el
+++ b/modules/dirvish-config.el
@@ -102,6 +102,14 @@ mixed-case names match."
Pure helper. An embedded `.m3u' that isn't at the end stays put."
(replace-regexp-in-string "\\.m3u\\'" "" name))
+(defun cj/--playlist-name-safe-p (name)
+ "Return non-nil when NAME is a safe bare playlist filename.
+A safe name is non-empty and carries no directory separator, so it can't
+steer `cj/dired-create-playlist-from-marked' to write outside `music-dir'
+through a `../' or absolute path. Pure helper."
+ (and (not (string-empty-p name))
+ (not (string-match-p "/" name))))
+
(defun cj/dired-create-playlist-from-marked ()
"Create an .m3u playlist file from marked files in Dired (or Dirvish).
Filters for audio files, prompts for the playlist name, and saves the resulting
@@ -120,19 +128,21 @@ Filters for audio files, prompts for the playlist name, and saves the resulting
(while (not done)
(setq base-name (cj/--playlist-sanitize-name
(read-string "Playlist name (without .m3u): ")))
- (setq playlist-path (expand-file-name (concat base-name ".m3u") music-dir))
(cond
- ((not (file-exists-p playlist-path))
- (setq done t))
+ ((not (cj/--playlist-name-safe-p base-name))
+ (message "Playlist name must be a bare filename, without '/'."))
(t
- (let ((choice (read-char-choice
- (format "Playlist '%s' exists. [o]verwrite, [c]ancel, [r]ename? "
- (file-name-nondirectory playlist-path))
- '(?o ?c ?r))))
- (cl-case choice
- (?o (setq done t))
- (?c (user-error "Cancelled playlist creation"))
- (?r (setq done nil)))))))
+ (setq playlist-path (expand-file-name (concat base-name ".m3u") music-dir))
+ (if (not (file-exists-p playlist-path))
+ (setq done t)
+ (let ((choice (read-char-choice
+ (format "Playlist '%s' exists. [o]verwrite, [c]ancel, [r]ename? "
+ (file-name-nondirectory playlist-path))
+ '(?o ?c ?r))))
+ (cl-case choice
+ (?o (setq done t))
+ (?c (user-error "Cancelled playlist creation"))
+ (?r (setq done nil))))))))
(with-temp-file playlist-path
(dolist (af audio-files)
(insert af "\n")))
@@ -343,11 +353,14 @@ Pure helper used by `cj/set-wallpaper'."
"Set the image at point as the desktop wallpaper.
Uses feh on X11, swww on Wayland."
(interactive)
- (let* ((file (expand-file-name (dired-file-name-at-point)))
+ (let* ((raw (dired-file-name-at-point))
+ (file (and raw (expand-file-name raw)))
(env (cond ((env-x11-p) 'x11)
((env-wayland-p) 'wayland)
(t nil)))
(cmd (cj/--wallpaper-program-for env)))
+ (unless file
+ (user-error "No file at point"))
(if (null cmd)
(message "Unknown display server (not X11 or Wayland)")
(when-let ((path (cj/executable-find-or-warn
diff --git a/tests/test-dirvish-config-playlist.el b/tests/test-dirvish-config-playlist.el
index 3876a177..d059a899 100644
--- a/tests/test-dirvish-config-playlist.el
+++ b/tests/test-dirvish-config-playlist.el
@@ -78,5 +78,20 @@ lowercase extension list."
"Boundary: a name that's just `.m3u' becomes empty after stripping."
(should (equal (cj/--playlist-sanitize-name ".m3u") "")))
+;;; cj/--playlist-name-safe-p
+
+(ert-deftest test-cj--playlist-name-safe-p-bare-name ()
+ "Normal: a bare filename is safe."
+ (should (cj/--playlist-name-safe-p "roadtrip")))
+
+(ert-deftest test-cj--playlist-name-safe-p-empty ()
+ "Boundary: an empty name is not safe."
+ (should-not (cj/--playlist-name-safe-p "")))
+
+(ert-deftest test-cj--playlist-name-safe-p-rejects-separators ()
+ "Error: any directory separator (relative, absolute, or nested) is rejected."
+ (dolist (bad '("../evil" "../../etc/cron" "/etc/passwd" "sub/dir/name"))
+ (should-not (cj/--playlist-name-safe-p bad))))
+
(provide 'test-dirvish-config-playlist)
;;; test-dirvish-config-playlist.el ends here
diff --git a/tests/test-dirvish-config-wrappers.el b/tests/test-dirvish-config-wrappers.el
index 7072fcf7..bead4583 100644
--- a/tests/test-dirvish-config-wrappers.el
+++ b/tests/test-dirvish-config-wrappers.el
@@ -140,5 +140,11 @@ calls the wallpaper-setter binary."
(should (member "/some/picture.jpg" call-process-args))
(should (string-match-p "Wallpaper set" msg))))
+(ert-deftest test-dirvish-set-wallpaper-no-file-errors ()
+ "Error: with no file at point, set-wallpaper signals user-error rather
+than passing nil to expand-file-name."
+ (cl-letf (((symbol-function 'dired-file-name-at-point) (lambda () nil)))
+ (should-error (cj/set-wallpaper) :type 'user-error)))
+
(provide 'test-dirvish-config-wrappers)
;;; test-dirvish-config-wrappers.el ends here