7947ad20b75e839bd0bb944db858bc73b56e568f
max
  Mon Jun 16 05:36:01 2025 -0700
XSS prevention in error message output for errAbort(), refs #34157

diff --git src/lib/errAbort.c src/lib/errAbort.c
index cc57a3e1df0..185248aaeb6 100644
--- src/lib/errAbort.c
+++ src/lib/errAbort.c
@@ -42,39 +42,76 @@
     boolean errAbortInProgress;     /* Flag to indicate that an error abort is in progress.
                                       * Needed so that a warn handler can tell if it's really
                                       * being called because of a warning or an error. */
     WarnHandler warnArray[maxWarnHandlers];
     int warnIx;
     AbortHandler abortArray[maxAbortHandlers];
     int abortIx;
     };
 
 static struct perThreadAbortVars *getThreadVars();  // forward declaration
 
 static void defaultVaWarn(char *format, va_list args)
 /* Default error message handler. */
 {
 if (format != NULL) {
-    // vfprintf() cannot be called twice in a row without a va_end/va_start
-    // so this must be an if/else situation
     if (doContentType)
         {
         puts("Content-type: text/html\n");
         puts("Error: ");
-        vfprintf(stdout, format, args);
+
+        // Need to destroy < and > in format AND args, to make XSS impossible.
+        // and vfprintf() cannot be called twice in a row without a va_copy
+        va_list args_copy;
+        
+        // first output message to stderr, as before
+        va_copy(args_copy, args);
+        vfprintf(stderr, format, args);
+        va_end(args_copy);
+
+        // get size of buffer needed
+        va_copy(args_copy, args);
+        int needed = vsnprintf(NULL, 0, format, args_copy);
+        va_end(args_copy);
+        if (needed < 0)
+            {
+            puts("defaultVaWarn - string format error in errAbort");  // Formatting error
+            return;
+            }
+
+        // allocate buffer
+        char *buffer = malloc(needed + 1);
+        if (!buffer)
+            {
+            puts("defaultVaWarn - cannot allocate memory for errAbort message");  // Formatting error
+            return;
+            }
+
+        // write message to buffer
+        vsprintf(buffer, format, args);
+        
+        // sanitize buffer
+        for (char *p = buffer; *p; ++p) {
+            if (*p == '<') *p = '[';
+            if (*p == '>') *p = ']';
+        }
+
+        // output
+        fputs(buffer, stdout);  // or log it
         fprintf(stdout, "\n");
         fflush(stdout);
+        free(buffer);
         }
     else
         {
         fflush(stdout);
         vfprintf(stderr, format, args);
         fprintf(stderr, "\n");
         fflush(stderr);
         }
     }
 }
 
 static void silentVaWarn(char *format, va_list args)
 /* Warning handler that just hides it.  Useful sometimes when high level code
  * expects low level code may fail (as in finding a file on the net) but doesn't
  * want user to be bothered about it. */