Fix heredoc injection in gsh-subprocess stdin piping
ober
0515767843359985f04b0cf7ce548119ad35602c
--- a/src/jerboa-emacs/gsh-subprocess.ss +++ b/src/jerboa-emacs/gsh-subprocess.ss @@ -12,6 +12,7 @@ (import :std/sugar :std/format + :std/misc/string :jsh/lib :jsh/environment :jerboa-emacs/core) @@ -28,6 +29,21 @@ (set! *gsh-active-process* #f)) ;;;============================================================================ +;;; Heredoc delimiter safety +;;;============================================================================ + +(def (make-safe-heredoc-delimiter text) + "Generate a heredoc delimiter that does not appear in TEXT. + Avoids injection when TEXT contains the delimiter string." + (let loop ((suffix 0)) + (let ((delim (if (= suffix 0) + "__GSH_STDIN__" + (string-append "__GSH_STDIN_" (number->string suffix) "__")))) + (if (string-contains text delim) + (loop (+ suffix 1)) + delim)))) + +;;;============================================================================ ;;; Shared gsh environment for one-shot commands ;;;============================================================================ @@ -68,10 +84,12 @@ ;; Handle stdin-text by prepending heredoc (let ((effective-cmd (if stdin-text - ;; Pipe stdin-text into the command via heredoc - (string-append "cat <<'__GSH_STDIN__'\n" - stdin-text - "\n__GSH_STDIN__\n | " cmd) + ;; Pipe stdin-text into the command via heredoc. + ;; Use a safe delimiter that doesn't appear in stdin-text. + (let ((delim (make-safe-heredoc-delimiter stdin-text))) + (string-append "cat <<'" delim "'\n" + stdin-text + "\n" delim "\n | " cmd)) cmd))) (let-values (((output status) (gsh-capture effective-cmd env))) (values (or output "") (or status 0))))) @@ -104,9 +122,10 @@ (lambda () (let ((effective-cmd (if stdin-text - (string-append "cat <<'__GSH_STDIN__'\n" - stdin-text - "\n__GSH_STDIN__\n | " cmd) + (let ((delim (make-safe-heredoc-delimiter stdin-text))) + (string-append "cat <<'" delim "'\n" + stdin-text + "\n" delim "\n | " cmd)) cmd))) (let-values (((output status) (gsh-capture effective-cmd env))) (values (or output "") (or status 0)))))