Commit 3a9639c0b for imagemagick.org

commit 3a9639c0bd21f61f22fcce4c5f6838f1f0d8f738
Author: Dirk Lemstra <dirk@lemstra.org>
Date:   Sun Oct 25 09:30:23 2026 +0100

    Added new method to sanitize filenames and use that for GHSA-2pc3-3339-3qc9.

diff --git a/MagickCore/methods.h b/MagickCore/methods.h
index f04fc47f9..08d771b74 100644
--- a/MagickCore/methods.h
+++ b/MagickCore/methods.h
@@ -1077,6 +1077,7 @@ extern "C" {
 #define RotationalBlurImage  PrependMagickMethod(RotationalBlurImage)
 #define SampleImage  PrependMagickMethod(SampleImage)
 #define SanitizeString  PrependMagickMethod(SanitizeString)
+#define SanitizeStringForIdentifier  PrependMagickMethod(SanitizeStringForIdentifier)
 #define ScaleGeometryKernelInfo  PrependMagickMethod(ScaleGeometryKernelInfo)
 #define ScaleImage  PrependMagickMethod(ScaleImage)
 #define ScaleKernelInfo  PrependMagickMethod(ScaleKernelInfo)
diff --git a/MagickCore/string.c b/MagickCore/string.c
index cac00ea52..f0406a830 100644
--- a/MagickCore/string.c
+++ b/MagickCore/string.c
@@ -1583,6 +1583,53 @@ MagickExport void ResetStringInfo(StringInfo *string_info)
 %                                                                             %
 %                                                                             %
 %                                                                             %
+%   S a n i t i z e S t r i n g F o r I d e n t i f i e r                     %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  SanitizeStringForIdentifier() returns a new string suitable for use as a C
+%  identifier.  Invalid characters are replaced with underscores.  An empty
+%  source string becomes a single underscore.
+%
+%  Free the sanitized string with DestroyString().
+%
+%  The format of the SanitizeStringForIdentifier method is:
+%
+%      char *SanitizeStringForIdentifier(const char *source)
+%
+%  A description of each parameter follows:
+%
+%    o source: A character string.
+%
+*/
+MagickExport char *SanitizeStringForIdentifier(const char *source)
+{
+  char
+    *p,
+    *sanitize_source;
+
+  static const char
+    letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_",
+    digits[] = "0123456789";
+
+  assert(source != (const char *) NULL);
+  sanitize_source=AcquireString(*source == '\0' ? "_" : source);
+  if (strchr(letters,(int) ((unsigned char) *sanitize_source)) == (char *) NULL)
+    *sanitize_source='_';
+  for (p=sanitize_source+1; *p != '\0'; p++)
+    if ((strchr(letters,(int) ((unsigned char) *p)) == (char *) NULL) &&
+        (strchr(digits,(int) ((unsigned char) *p)) == (char *) NULL))
+      *p='_';
+  return(sanitize_source);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
 %   S a n t i z e S t r i n g                                                 %
 %                                                                             %
 %                                                                             %
diff --git a/MagickCore/string_.h b/MagickCore/string_.h
index e562a8a1d..4b1f5cd0d 100644
--- a/MagickCore/string_.h
+++ b/MagickCore/string_.h
@@ -50,6 +50,7 @@ extern MagickExport char
   *FileToString(const char *,const size_t,ExceptionInfo *),
   *GetEnvironmentValue(const char *),
   *SanitizeString(const char *),
+  *SanitizeStringForIdentifier(const char *),
   *StringInfoToDigest(const StringInfo *),
   *StringInfoToHexString(const StringInfo *),
   *StringInfoToString(const StringInfo *),
diff --git a/coders/magick.c b/coders/magick.c
index 55f5c73f9..712263605 100644
--- a/coders/magick.c
+++ b/coders/magick.c
@@ -13288,8 +13288,7 @@ static MagickBooleanType WriteMAGICKImage(const ImageInfo *image_info,
       return(status);
     }
   (void) WriteBlobString(image,"/*\n");
-  sanitized_filename=AcquireString(image->filename);
-  (void) SubstituteString(&sanitized_filename,"*/","??");
+  sanitized_filename=SanitizeStringForIdentifier(image->filename);
   (void) FormatLocaleString(buffer,MagickPathExtent,"  %s (%s).\n",
     sanitized_filename,write_info->magick);
   sanitized_filename=DestroyString(sanitized_filename);
diff --git a/coders/xbm.c b/coders/xbm.c
index 108f7ad6f..3f86ac8ac 100644
--- a/coders/xbm.c
+++ b/coders/xbm.c
@@ -516,7 +516,8 @@ static MagickBooleanType WriteXBMImage(const ImageInfo *image_info,Image *image,
 {
   char
     basename[MagickPathExtent],
-    buffer[MagickPathExtent];
+    buffer[MagickPathExtent],
+    *sanitized_filename;

   MagickBooleanType
     status;
@@ -555,14 +556,16 @@ static MagickBooleanType WriteXBMImage(const ImageInfo *image_info,Image *image,
     Write X bitmap header.
   */
   GetPathComponent(image->filename,BasePath,basename);
+  sanitized_filename=SanitizeStringForIdentifier(basename);
   (void) FormatLocaleString(buffer,MagickPathExtent,"#define %s_width %.17g\n",
-    basename,(double) image->columns);
+    sanitized_filename,(double) image->columns);
   (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer);
   (void) FormatLocaleString(buffer,MagickPathExtent,"#define %s_height %.17g\n",
-    basename,(double) image->rows);
+    sanitized_filename,(double) image->rows);
   (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer);
   (void) FormatLocaleString(buffer,MagickPathExtent,
-    "static char %s_bits[] = {\n",basename);
+    "static char %s_bits[] = {\n",sanitized_filename);
+  sanitized_filename=DestroyString(sanitized_filename);
   (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer);
   (void) CopyMagickString(buffer," ",MagickPathExtent);
   (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer);
diff --git a/coders/xpm.c b/coders/xpm.c
index 4437083fd..b6002a998 100644
--- a/coders/xpm.c
+++ b/coders/xpm.c
@@ -678,7 +678,8 @@ static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
     buffer[MagickPathExtent],
     basename[MagickPathExtent],
     name[MagickPathExtent],
-    symbol[MagickPathExtent];
+    symbol[MagickPathExtent],
+    *sanitized_filename;

   Image
     *affinity_image,
@@ -836,8 +837,10 @@ static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
   */
   (void) WriteBlobString(image,"/* XPM */\n");
   GetPathComponent(picon->filename,BasePath,basename);
+  sanitized_filename=SanitizeStringForIdentifier(basename);
   (void) FormatLocaleString(buffer,MagickPathExtent,
-    "static const char *%.1024s[] = {\n",basename);
+    "static const char *%.1024s[] = {\n",sanitized_filename);
+  sanitized_filename=DestroyString(sanitized_filename);
   (void) WriteBlobString(image,buffer);
   (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
   (void) FormatLocaleString(buffer,MagickPathExtent,
@@ -950,7 +953,8 @@ static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
     buffer[MagickPathExtent],
     basename[MagickPathExtent],
     name[MagickPathExtent],
-    symbol[MagickPathExtent];
+    symbol[MagickPathExtent],
+    *sanitized_filename;

   const Quantum
     *p;
@@ -1062,18 +1066,10 @@ static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
   */
   (void) WriteBlobString(image,"/* XPM */\n");
   GetPathComponent(image->filename,BasePath,basename);
-  if (isalnum((int) ((unsigned char) *basename)) == 0)
-    {
-      (void) FormatLocaleString(buffer,MagickPathExtent,"xpm_%.1024s",basename);
-      (void) CopyMagickString(basename,buffer,MagickPathExtent);
-    }
-  if (isalpha((int) ((unsigned char) basename[0])) == 0)
-    basename[0]='_';
-  for (i=1; basename[i] != '\0'; i++)
-    if (isalnum((int) ((unsigned char) basename[i])) == 0)
-      basename[i]='_';
+  sanitized_filename=SanitizeStringForIdentifier(basename);
   (void) FormatLocaleString(buffer,MagickPathExtent,
-    "static char *%.1024s[] = {\n",basename);
+    "static char *%.1024s[] = {\n",sanitized_filename);
+  sanitized_filename=DestroyString(sanitized_filename);
   (void) WriteBlobString(image,buffer);
   (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
   (void) FormatLocaleString(buffer,MagickPathExtent,