Fix heredoc injection in gsh-subprocess stdin piping

ober

0515767843359985f04b0cf7ce548119ad35602c

diff --git a/src/jerboa-emacs/gsh-subprocess.ss b/src/jerboa-emacs/gsh-subprocess.ss
index 6210fb7..56ebd74 100644
--- 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)))))