File ImageMagick-CVE-2026-25797.patch of Package ImageMagick.42999

From 7284564901441ddb04dadaad306e9f0fb527d71f Mon Sep 17 00:00:00 2001
From: Dirk Lemstra <dirk@lemstra.org>
Date: Fri, 20 Feb 2026 14:08:23 +0100
Subject: [PATCH] Properly escape the strings that are written as raw html
 (https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-rw6c-xp26-225v)

---
 coders/html.c | 64 +++++++++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 25 deletions(-)

Index: ImageMagick-6.8.8-1/coders/html.c
===================================================================
--- ImageMagick-6.8.8-1.orig/coders/html.c
+++ ImageMagick-6.8.8-1/coders/html.c
@@ -208,6 +208,21 @@ ModuleExport void UnregisterHTMLImage(vo
 %
 %
 */
+static void WriteHtmlEncodedString(Image *image,const char* value)
+{
+  char
+    *encoded_value;
+
+  encoded_value=AcquireString(value);
+  (void) SubstituteString(&encoded_value,"<","&lt;");
+  (void) SubstituteString(&encoded_value,">","&gt;");
+  (void) SubstituteString(&encoded_value,"&","&amp;");
+  (void) SubstituteString(&encoded_value,"\"","&quot;");
+  (void) SubstituteString(&encoded_value,"'","&apos;");
+  WriteBlobString(image,encoded_value);
+  encoded_value=DestroyString(encoded_value);
+}
+
 static MagickBooleanType WriteHTMLImage(const ImageInfo *image_info,
   Image *image)
 {
@@ -301,29 +316,29 @@ static MagickBooleanType WriteHTMLImage(
         "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
       (void) WriteBlobString(image,"<html>\n");
       (void) WriteBlobString(image,"<head>\n");
+      (void) WriteBlobString(image,"<title>");
       value=GetImageProperty(image,"label");
       if (value != (const char *) NULL)
-        (void) FormatLocaleString(buffer,MaxTextExtent,"<title>%s</title>\n",
-          value);
+        WriteHtmlEncodedString(image,value);
       else
         {
           GetPathComponent(filename,BasePath,basename);
-          (void) FormatLocaleString(buffer,MaxTextExtent,
-            "<title>%s</title>\n",basename);
+          WriteHtmlEncodedString(image,basename);
         }
-      (void) WriteBlobString(image,buffer);
+      (void) WriteBlobString(image,"</title>\n");
       (void) WriteBlobString(image,"</head>\n");
       (void) WriteBlobString(image,"<body style=\"text-align: center;\">\n");
-      (void) FormatLocaleString(buffer,MaxTextExtent,"<h1>%s</h1>\n",
-        image->filename);
-      (void) WriteBlobString(image,buffer);
+      (void) WriteBlobString(image,"<h1>");
+      WriteHtmlEncodedString(image,image->filename);
+      (void) WriteBlobString(image,"</h1>");
       (void) WriteBlobString(image,"<div>\n");
       (void) CopyMagickString(filename,image->filename,MaxTextExtent);
       AppendImageFormat("png",filename);
-      (void) FormatLocaleString(buffer,MaxTextExtent,"<img usemap=\"#%s\" "
-        "src=\"%s\" style=\"border: 0;\" alt=\"Image map\" />\n",mapname,
-        filename);
-      (void) WriteBlobString(image,buffer);
+      (void) WriteBlobString(image,"<img usemap=\"#");
+      WriteHtmlEncodedString(image,mapname);
+      (void) WriteBlobString(image,"\" src=\"");
+      WriteHtmlEncodedString(image,filename);
+      (void) WriteBlobString(image,"\" style=\"border: 0;\" alt=\"Image map\" />\n");
       /*
         Determine the size and location of each image tile.
       */
@@ -333,17 +348,18 @@ static MagickBooleanType WriteHTMLImage(
       /*
         Write an image map.
       */
-      (void) FormatLocaleString(buffer,MaxTextExtent,
-        "<map id=\"%s\" name=\"%s\">\n",mapname,mapname);
-      (void) WriteBlobString(image,buffer);
-      (void) FormatLocaleString(buffer,MaxTextExtent,"  <area href=\"%s",url);
-      (void) WriteBlobString(image,buffer);
+      (void) WriteBlobString(image,"<map id=\"");
+      WriteHtmlEncodedString(image,mapname);
+      (void) WriteBlobString(image,"\" name=\"");
+      WriteHtmlEncodedString(image,mapname);
+      (void) WriteBlobString(image,"\">\n<area href=\"");
+      WriteHtmlEncodedString(image,url);
       if (image->directory == (char *) NULL)
         {
+          WriteHtmlEncodedString(image,image->filename);
           (void) FormatLocaleString(buffer,MaxTextExtent,
-            "%s\" shape=\"rect\" coords=\"0,0,%.20g,%.20g\" alt=\"\" />\n",
-            image->filename,(double) geometry.width-1,(double) geometry.height-
-            1);
+            "\" shape=\"rect\" coords=\"0,0,%.20g,%.20g\" alt=\"\" />\n",
+            (double) geometry.width-1,(double) geometry.height-1);
           (void) WriteBlobString(image,buffer);
         }
       else
@@ -359,9 +375,9 @@ static MagickBooleanType WriteHTMLImage(
               (void) WriteBlobString(image,buffer);
               if (*(p+1) != '\0')
                 {
-                  (void) FormatLocaleString(buffer,MaxTextExtent,
-                    "  <area href=%s\"",url);
-                  (void) WriteBlobString(image,buffer);
+                  (void) WriteBlobString(image,"  <area href=\"");
+                  WriteHtmlEncodedString(image,url);
+                  (void) WriteBlobString(image,"\"");
                 }
               geometry.x+=(ssize_t) geometry.width;
               if ((geometry.x+4) >= (ssize_t) image->columns)
@@ -371,7 +387,6 @@ static MagickBooleanType WriteHTMLImage(
                 }
             }
       (void) WriteBlobString(image,"</map>\n");
-      (void) CopyMagickString(filename,image->filename,MaxTextExtent);
       (void) WriteBlobString(image,"</div>\n");
       (void) WriteBlobString(image,"</body>\n");
       (void) WriteBlobString(image,"</html>\n");
@@ -379,7 +394,6 @@ static MagickBooleanType WriteHTMLImage(
       /*
         Write the image as PNG.
       */
-      (void) CopyMagickString(image->filename,filename,MaxTextExtent);
       AppendImageFormat("png",image->filename);
       next=GetNextImageInList(image);
       image->next=NewImageList();
Index: ImageMagick-6.8.8-1/coders/ps.c
===================================================================
--- ImageMagick-6.8.8-1.orig/coders/ps.c
+++ ImageMagick-6.8.8-1/coders/ps.c
@@ -1045,6 +1045,82 @@ static inline size_t MagickMin(const siz
   return(y);
 }
 
+static void inline FilenameToTitle(const char *filename,char *title,
+  const size_t extent)
+{
+  int
+    depth = 0;
+
+  ssize_t
+    i,
+    offset = 0;
+
+  if (extent == 0)
+    return;
+  for (i=0; (filename[i] != '\0') && ((offset+1) < extent); i++)
+  {
+    unsigned char
+      c = filename[i];
+
+    /*
+      Only allow printable ASCII.
+    */
+    if ((c < 32) || (c > 126))
+      {
+        title[offset++]='_';
+        continue;
+      }
+    /*
+      Percent signs break DSC parsing.
+    */
+    if (c == '%')
+      {
+        title[offset++]='_';
+        continue;
+      }
+    /*
+      Parentheses must remain balanced.
+    */
+    if (c == '(')
+      {
+        depth++;
+        title[offset++] = '(';
+        continue;
+      }
+    if (c == ')')
+      {
+        if (depth <= 0)
+          title[offset++]='_';
+        else
+          {
+            depth--;
+            title[offset++]=')';
+          }
+         continue;
+     }
+    /*
+      Everything else is allowed.
+    */
+    title[offset++]=c;
+  }
+  /*
+    If parentheses remain unbalanced, close them.
+  */
+  while ((depth > 0) && ((offset+1) < extent)) {
+    title[offset++]=')';
+    depth--;
+  }
+  title[offset]='\0';
+  /*
+    Ensure non-empty result.
+  */
+  if (offset == 0)
+    {
+      (void) strncpy(title,"Untitled",extent-1);
+      title[extent-1]='\0';
+    }
+}
+
 static inline unsigned char *PopHexPixel(const char **hex_digits,
   const size_t pixel,unsigned char *pixels)
 {
@@ -1529,6 +1605,9 @@ static MagickBooleanType WritePSImage(co
       text_size=(size_t) (MultilineCensus(value)*pointsize+12);
     if (page == 1)
       {
+        char
+          title[MaxTextExtent];
+
         /*
           Output Postscript header.
         */
@@ -1539,8 +1618,9 @@ static MagickBooleanType WritePSImage(co
             MaxTextExtent);
         (void) WriteBlobString(image,buffer);
         (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
+        FilenameToTitle(image->filename,title,MaxTextExtent);
         (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
-          image->filename);
+          title);
         (void) WriteBlobString(image,buffer);
         timer=time((time_t *) NULL);
         (void) FormatMagickTime(timer,MaxTextExtent,date);
Index: ImageMagick-6.8.8-1/coders/ps2.c
===================================================================
--- ImageMagick-6.8.8-1.orig/coders/ps2.c
+++ ImageMagick-6.8.8-1/coders/ps2.c
@@ -185,6 +185,82 @@ ModuleExport void UnregisterPS2Image(voi
 %
 */
 
+static void inline FilenameToTitle(const char *filename,char *title,
+  const size_t extent)
+{
+  int
+    depth = 0;
+
+  ssize_t
+    i,
+    offset = 0;
+
+  if (extent == 0)
+    return;
+  for (i=0; (filename[i] != '\0') && ((offset+1) < extent); i++)
+  {
+    unsigned char
+      c = filename[i];
+
+    /*
+      Only allow printable ASCII.
+    */
+    if ((c < 32) || (c > 126))
+      {
+        title[offset++]='_';
+        continue;
+      }
+    /*
+      Percent signs break DSC parsing.
+    */
+    if (c == '%')
+      {
+        title[offset++]='_';
+        continue;
+      }
+    /*
+      Parentheses must remain balanced.
+    */
+    if (c == '(')
+      {
+        depth++;
+        title[offset++] = '(';
+        continue;
+      }
+    if (c == ')')
+      {
+        if (depth <= 0)
+          title[offset++]='_';
+        else
+          {
+            depth--;
+            title[offset++]=')';
+          }
+         continue;
+     }
+    /*
+      Everything else is allowed.
+    */
+    title[offset++]=c;
+  }
+  /*
+    If parentheses remain unbalanced, close them.
+  */
+  while ((depth > 0) && ((offset+1) < extent)) {
+    title[offset++]=')';
+    depth--;
+  }
+  title[offset]='\0';
+  /*
+    Ensure non-empty result.
+  */
+  if (offset == 0)
+    {
+      (void) strncpy(title,"Untitled",extent-1);
+      title[extent-1]='\0';
+    }
+}
+
 static MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
   Image *image,Image *inject_image)
 {
@@ -556,6 +632,9 @@ static MagickBooleanType WritePS2Image(c
       text_size=(size_t) (MultilineCensus(value)*pointsize+12);
     if (page == 1)
       {
+        char
+          title[MaxTextExtent];
+
         /*
           Output Postscript header.
         */
@@ -566,8 +645,9 @@ static MagickBooleanType WritePS2Image(c
             MaxTextExtent);
         (void) WriteBlobString(image,buffer);
         (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
+        FilenameToTitle(image->filename,title,MaxTextExtent);
         (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
-          image->filename);
+          title);
         (void) WriteBlobString(image,buffer);
         timer=time((time_t *) NULL);
         (void) FormatMagickTime(timer,MaxTextExtent,date);
Index: ImageMagick-6.8.8-1/coders/ps3.c
===================================================================
--- ImageMagick-6.8.8-1.orig/coders/ps3.c
+++ ImageMagick-6.8.8-1/coders/ps3.c
@@ -199,6 +199,82 @@ ModuleExport void UnregisterPS3Image(voi
 %
 */
 
+static void inline FilenameToTitle(const char *filename,char *title,
+  const size_t extent)
+{
+  int
+    depth = 0;
+
+  ssize_t
+    i,
+    offset = 0;
+
+  if (extent == 0)
+    return;
+  for (i=0; (filename[i] != '\0') && ((offset+1) < extent); i++)
+  {
+    unsigned char
+      c = filename[i];
+
+    /*
+      Only allow printable ASCII.
+    */
+    if ((c < 32) || (c > 126))
+      {
+        title[offset++]='_';
+        continue;
+      }
+    /*
+      Percent signs break DSC parsing.
+    */
+    if (c == '%')
+      {
+        title[offset++]='_';
+        continue;
+      }
+    /*
+      Parentheses must remain balanced.
+    */
+    if (c == '(')
+      {
+        depth++;
+        title[offset++] = '(';
+        continue;
+      }
+    if (c == ')')
+      {
+        if (depth <= 0)
+          title[offset++]='_';
+        else
+          {
+            depth--;
+            title[offset++]=')';
+          }
+         continue;
+     }
+    /*
+      Everything else is allowed.
+    */
+    title[offset++]=c;
+  }
+  /*
+    If parentheses remain unbalanced, close them.
+  */
+  while ((depth > 0) && ((offset+1) < extent)) {
+    title[offset++]=')';
+    depth--;
+  }
+  title[offset]='\0';
+  /*
+    Ensure non-empty result.
+  */
+  if (offset == 0)
+    {
+      (void) strncpy(title,"Untitled",extent-1);
+      title[extent-1]='\0';
+    }
+}
+
 static MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
   Image *image,Image *inject_image)
 {
@@ -993,6 +1069,9 @@ static MagickBooleanType WritePS3Image(c
     page++;
     if (page == 1)
       {
+        char
+          title[MaxTextExtent];
+
         /*
           Postscript header on the first page.
         */
@@ -1005,8 +1084,8 @@ static MagickBooleanType WritePS3Image(c
         (void) FormatLocaleString(buffer,MaxTextExtent,
           "%%%%Creator: ImageMagick %s\n",MagickLibVersionText);
         (void) WriteBlobString(image,buffer);
-        (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: %s\n",
-          image->filename);
+        FilenameToTitle(image->filename,title,MaxTextExtent);
+        (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: %s\n",title);
         (void) WriteBlobString(image,buffer);
         timer=time((time_t *) NULL);
         (void) FormatMagickTime(timer,MaxTextExtent,date);
openSUSE Build Service is sponsored by