*** 'comint-run' can now accept a list of switches to pass to the program.
'C-u M-x comint-run' will prompt for the switches interactively.
+*** Abnormal hook `comint-password-function' has been added.
+This hook permits a derived mode to supply a password for the
+underlying command interpreter without prompting the user. For
+example, in sql-mode, the password for connecting to the database may
+be stored in the connection wallet and may be passed on the command
+line to start the SQL interpreter. This is a potential security flaw
+that could expose user's database passwords on the command line
+through the use of a process list (Bug#8427). With this hook, it is
+possible to not pass the password on the command line and wait for the
+program to prompt for the password. When it does so, the password cam
+be supplied to the SQL interpreter without involving the user just as
+if it had been supplied on the command line.
+
** SQL
*** SQL Indent Minor Mode
;; saved -- typically passwords to ftp, telnet, or somesuch.
;; Just enter m-x comint-send-invisible and type in your line.
+(defvar comint-password-function nil
+ "Abnormal hook run when prompted for a password.
+This function gets one argument, a string containing the prompt.
+It may return a string containing the password, or nil if normal
+password prompting should occur.")
+(make-variable-buffer-local 'comint-password-function)
+
(defun comint-send-invisible (&optional prompt)
"Read a string without echoing.
Then send it to the process running in the current buffer.
(format "(In buffer %s) "
(current-buffer)))))
(if proc
- (let ((str (read-passwd (concat prefix
- (or prompt "Non-echoed text: ")))))
+ (let ((prefix-prompt (concat prefix
+ (or prompt "Non-echoed text: ")))
+ str)
+ (when comint-password-function
+ (setq str (funcall comint-password-function prefix-prompt)))
+ (unless str
+ (setq str (read-passwd prefix-prompt)))
(if (stringp str)
(progn
(comint-snapshot-last-prompt)
(dolist (str comint-testsuite-password-strings)
(should (string-match comint-password-prompt-regexp str))))
+(ert-deftest comint-test-no-password-function ()
+ "Test that `comint-password-function' not being set does not
+alter normal password flow."
+ (cl-letf
+ (((symbol-function 'read-passwd)
+ (lambda (_prompt &optional _confirm _default)
+ "PaSsWoRd123")))
+ (let ((cat (executable-find "cat")))
+ (when cat
+ (with-temp-buffer
+ (make-comint-in-buffer "test-comint-password" (current-buffer) cat)
+ (let ((proc (get-buffer-process (current-buffer))))
+ (comint-send-string proc "Password: ")
+ (accept-process-output proc 0 1 t)
+ (comint-send-eof)
+ (accept-process-output proc 0 1 t)
+ (should (string-equal (buffer-substring-no-properties (point-min) (point-max))
+ "Password: PaSsWoRd123\n"))
+ (when (process-live-p proc)
+ (kill-process proc))
+ (accept-process-output proc 0 1 t)))))))
+
+(ert-deftest comint-test-password-function-with-value ()
+ "Test that `comint-password-function' alters normal password
+flow. Hook function returns alternative password."
+ (cl-letf
+ (((symbol-function 'read-passwd)
+ (lambda (_prompt &optional _confirm _default)
+ "PaSsWoRd123")))
+ (let ((cat (executable-find "cat"))
+ (comint-password-function (lambda (_prompt) "MaGiC-PaSsWoRd789")))
+ (when cat
+ (with-temp-buffer
+ (make-comint-in-buffer "test-comint-password" (current-buffer) cat)
+ (let ((proc (get-buffer-process (current-buffer))))
+ (comint-send-string proc "Password: ")
+ (accept-process-output proc 0 1 t)
+ (comint-send-eof)
+ (accept-process-output proc 0 1 t)
+ (should (string-equal (buffer-substring-no-properties (point-min) (point-max))
+ "Password: MaGiC-PaSsWoRd789\n"))
+ (when (process-live-p proc)
+ (kill-process proc))
+ (accept-process-output proc 0 1 t)))))))
+
+(ert-deftest comint-test-password-function-with-nil ()
+ "Test that `comint-password-function' does not alter the normal
+password flow if it returns a nil value."
+ (cl-letf
+ (((symbol-function 'read-passwd)
+ (lambda (_prompt &optional _confirm _default)
+ "PaSsWoRd456")))
+ (let ((cat (executable-find "cat"))
+ (comint-password-function (lambda (_prompt) nil)))
+ (when cat
+ (with-temp-buffer
+ (make-comint-in-buffer "test-comint-password" (current-buffer) cat)
+ (let ((proc (get-buffer-process (current-buffer))))
+ (comint-send-string proc "Password: ")
+ (accept-process-output proc 0 1 t)
+ (comint-send-eof)
+ (accept-process-output proc 0 1 t)
+ (should (string-equal (buffer-substring-no-properties (point-min) (point-max))
+ "Password: PaSsWoRd456\n"))
+ (when (process-live-p proc)
+ (kill-process proc))
+ (accept-process-output proc 0 1 t)))))))
+
;; Local Variables:
;; no-byte-compile: t
;; End: