From dd8e1576cdfa282efbbc610737b039721841c60c Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Tue, 30 Jun 2026 17:34:32 -0400 Subject: fix(prog-shell): only auto-chmod scripts in prog-mode buffers cj/make-script-executable runs from a global after-save-hook and set +x on any saved file whose first line was a shebang, in every buffer. A downloaded script you were reading, a template, or a shebang in a text or org file silently became executable. I gated it on derived-mode-p prog-mode, so it only acts on actual script buffers. Real scripts (sh-mode, python-mode) still get the fast path. --- modules/prog-shell.el | 6 +++++- tests/test-prog-shell--make-script-executable.el | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/modules/prog-shell.el b/modules/prog-shell.el index d7f97932b..3ed51da11 100644 --- a/modules/prog-shell.el +++ b/modules/prog-shell.el @@ -166,8 +166,12 @@ Overrides default prog-mode keybindings with shell-specific commands." ;; Automatically set execute permission on shell scripts with shebangs (defun cj/make-script-executable () - "Make the current file executable if it has a shebang." + "Make the current file executable if it is a script buffer with a shebang. +Runs from a global `after-save-hook', so it gates on `prog-mode': a shebang in a +text, org, or fundamental-mode buffer (a script being read, quoted, or reviewed) +is left alone rather than silently made executable." (when (and buffer-file-name + (derived-mode-p 'prog-mode) (not (file-executable-p buffer-file-name)) (save-excursion (goto-char (point-min)) diff --git a/tests/test-prog-shell--make-script-executable.el b/tests/test-prog-shell--make-script-executable.el index e2bb0e6de..0f220b19b 100644 --- a/tests/test-prog-shell--make-script-executable.el +++ b/tests/test-prog-shell--make-script-executable.el @@ -106,6 +106,20 @@ (kill-buffer)) (delete-file temp-file)))) +(ert-deftest test-make-script-executable-non-prog-mode-skipped () + "Boundary: a shebang file visited in a non-prog-mode buffer (a script being +read, quoted, or reviewed) is NOT silently made executable. The auto-exec hook +runs on every save globally, so it must only act on actual script buffers." + (let ((temp-file (test--create-temp-script "#!/bin/bash\necho hello"))) + (unwind-protect + (with-current-buffer (find-file-noselect temp-file) + (text-mode) + (should-not (test--file-executable-p temp-file)) + (cj/make-script-executable) + (should-not (test--file-executable-p temp-file)) + (kill-buffer)) + (delete-file temp-file)))) + ;;; Edge Cases (ert-deftest test-make-script-executable-edge-no-buffer-file () -- cgit v1.2.3