Commit 4f3be6803 for imagemagick.org

commit 4f3be68037c3a378cba8f75e10d4511325e636fa
Author: Dirk Lemstra <dirk@lemstra.org>
Date:   Fri Oct 30 10:36:02 2026 +0100

    Match both slash- and backslash-separated paths and patterns during lexical and canonical path-policy evaluation on Windows (GHSA-hpjv-7vmp-f58w)

diff --git a/MagickCore/policy.c b/MagickCore/policy.c
index 0699ee2d5..fb3120a88 100644
--- a/MagickCore/policy.c
+++ b/MagickCore/policy.c
@@ -792,6 +792,34 @@ static MagickBooleanType IsPolicyCacheInstantiated(ExceptionInfo *exception)
 %
 */

+static MagickBooleanType IsPathPolicyMatch(const char *path,const char *pattern)
+{
+#if defined(MAGICKCORE_WINDOWS_SUPPORT) && !defined(__CYGWIN__)
+  char
+    *normalized_path,
+    *normalized_pattern,
+    *p;
+
+  MagickBooleanType
+    match;
+
+  normalized_path=AcquireString(path);
+  normalized_pattern=AcquireString(pattern);
+  for (p=normalized_path; *p != '\0'; p++)
+    if (*p == '\\')
+      *p='/';
+  for (p=normalized_pattern; *p != '\0'; p++)
+    if (*p == '\\')
+      *p='/';
+  match=GlobExpression(normalized_path,normalized_pattern,MagickFalse);
+  normalized_path=DestroyString(normalized_path);
+  normalized_pattern=DestroyString(normalized_pattern);
+  return(match);
+#else
+  return(GlobExpression(path,pattern,MagickFalse));
+#endif
+}
+
 MagickExport MagickBooleanType IsRightsAuthorizedByName(
   const PolicyDomain domain,const char *name,const PolicyRights rights,
   const char *pattern)
@@ -861,9 +889,9 @@ MagickExport MagickBooleanType IsRightsAuthorizedByName(
       continue;
     if ((name != (char *) NULL) && (LocaleCompare(name,policy->name) != 0))
       continue;
-    match=GlobExpression(pattern,policy->pattern,MagickFalse);
     if (policy->domain == PathPolicyDomain)
       {
+        match=IsPathPolicyMatch(pattern,policy->pattern);
         if (paths_provisioned == MagickFalse)
           {
             /*
@@ -891,19 +919,21 @@ MagickExport MagickBooleanType IsRightsAuthorizedByName(
           Match the given name against the directory and basename forms.
         */
         if ((canonical_directory != (char *) NULL) && (match == MagickFalse))
-          match=GlobExpression(canonical_directory,policy->pattern,MagickFalse);
+          match=IsPathPolicyMatch(canonical_directory,policy->pattern);
         if ((canonical_candidate != (char *) NULL) && (match == MagickFalse))
-          match=GlobExpression(canonical_candidate,policy->pattern,MagickFalse);
+          match=IsPathPolicyMatch(canonical_candidate,policy->pattern);
         /*
           Match the fully resolved path on its own.
         */
         if ((canonical_path != (char *) NULL) &&
-            (GlobExpression(canonical_path,policy->pattern,MagickFalse) != MagickFalse))
+            (IsPathPolicyMatch(canonical_path,policy->pattern) != MagickFalse))
           {
             resolved_matched_any=MagickTrue;
             resolved_rights=policy->rights;
           }
       }
+    else
+      match=GlobExpression(pattern,policy->pattern,MagickFalse);
     if (match == MagickFalse)
       continue;
     matched_any=MagickTrue;