CVE-2024-34071 (published 2024-05-21T14:15Z)

Umbraco is an ASP.NET CMS used by more than 730.000 websites. Umbraco has an endpoint that is vulnerable to open redirects. The endpoint is protected so it requires the user to be signed into backoffice before the vulnerable is exposed. This vulnerability has been patched in version(s) 8.18.14, 10.8.6, 12.3.10 and 13.3.1.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/umbraco/Umbraco-CMS
commit_details: repo_url: https://github.com/umbraco/Umbraco-CMS
author: Bjarke Berg
author_date: 2024-05-17 08:37:51+02:00
msg: Merge pull request from GHSA-j74q-mv2c-rxmp
repository: repo_url: https://github.com/umbraco/Umbraco-CMS
repo_name: umbraco/Umbraco-CMS
description: Umbraco is a free and open source .NET content management system helping you deliver delightful digital experiences.
repo_language: C#
owner: umbraco
file_changes: filename: WebPath.cs
change_type: ModificationType.MODIFY
diff: @@ -50,4 +50,28 @@ public static string Combine(params string[]? paths)
 
         return sb.ToString();
     }
+
+
+    /// <summary>
+    /// Determines whether the provided web path is well-formed according to the specified UriKind.
+    /// </summary>
+    /// <param name="webPath">The web path to check. This can be null.</param>
+    /// <param name="uriKind">The kind of Uri (Absolute, Relative, or RelativeOrAbsolute).</param>
+    /// <returns>
+    /// true if <paramref name="webPath"/> is well-formed; otherwise, false.
+    /// </returns>
+    public static bool IsWellFormedWebPath(string? webPath, UriKind uriKind)
+    {
+        if (string.IsNullOrWhiteSpace(webPath))
+        {
+            return false;
+        }
+
+        if (webPath.StartsWith("//"))
+        {
+            return uriKind is not UriKind.Relative;
+        }
+
+        return Uri.IsWellFormedUriString(webPath, uriKind);
+    }
 }

programming_language: C#
filename: ImagesController.cs
change_type: ModificationType.MODIFY
diff: @@ -8,6 +8,7 @@
 using Umbraco.Cms.Core.IO;
 using Umbraco.Cms.Core.Media;
 using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Routing;
 using Umbraco.Cms.Web.Common.Attributes;
 using Umbraco.Cms.Web.Common.DependencyInjection;
 using Umbraco.Extensions;
@@ -122,7 +123,7 @@ public IActionResult GetResized(string imagePath, int width)
 
     private bool IsAllowed(string encodedImagePath)
     {
-        if(Uri.IsWellFormedUriString(encodedImagePath, UriKind.Relative))
+        if(WebPath.IsWellFormedWebPath(encodedImagePath, UriKind.Relative))
         {
             return true;
         }

programming_language: C#
filename: PreviewController.cs
change_type: ModificationType.MODIFY
diff: @@ -11,6 +11,7 @@
 using Umbraco.Cms.Core.Models.Membership;
 using Umbraco.Cms.Core.Models.PublishedContent;
 using Umbraco.Cms.Core.PublishedCache;
+using Umbraco.Cms.Core.Routing;
 using Umbraco.Cms.Core.Security;
 using Umbraco.Cms.Core.Services;
 using Umbraco.Cms.Core.Web;
@@ -152,8 +153,7 @@ public ActionResult End(string? redir = null)
         // Expire Client-side cookie that determines whether the user has accepted to be in Preview Mode when visiting the website.
         _cookieManager.ExpireCookie(Constants.Web.AcceptPreviewCookieName);
 
-        if (Uri.IsWellFormedUriString(redir, UriKind.Relative)
-            && redir.StartsWith("//") == false
+        if (WebPath.IsWellFormedWebPath(redir, UriKind.Relative)
             && Uri.TryCreate(redir, UriKind.Relative, out Uri? url))
         {
             return Redirect(url.ToString());

programming_language: C#
filename: WebPathTests.cs
change_type: ModificationType.MODIFY
diff: @@ -30,4 +30,87 @@ public class WebPathTests
 
     [Test]
     public void Combine_must_handle_null() => Assert.Throws<ArgumentNullException>(() => WebPath.Combine(null));
+
+
+    [Test]
+    [TestCase("ftp://hello.com/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("file:///hello.com/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("ws://hello.com/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("wss://hello.com/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com:8080/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com:8080/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path?query=param", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path?query=param", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com:8080/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com:8080/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080/path", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("//hello.com", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("/test/test.jpg", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("/test", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("test", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase(null, UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("this is not welformed", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("ftp://hello.com/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("file:///hello.com/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("ws://hello.com/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("wss://hello.com/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com:8080/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com:8080/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com/path", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com/path", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com/path?query=param", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com/path?query=param", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com/path#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com/path#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com:8080/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com:8080/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("//hello.com:8080/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("//hello.com:8080/path", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("//hello.com:8080", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("//hello.com", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("/test/test.jpg", UriKind.Relative, ExpectedResult = true)]
+    [TestCase("/test", UriKind.Relative, ExpectedResult = true)]
+    [TestCase("test", UriKind.Relative, ExpectedResult = true)]
+    [TestCase("", UriKind.Relative, ExpectedResult = false)]
+    [TestCase(null, UriKind.Relative, ExpectedResult = false)]
+    [TestCase("this is not welformed", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("ftp://hello.com/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("file:///hello.com/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("ws://hello.com/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("wss://hello.com/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com:8080/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com:8080/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path?query=param", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path?query=param", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com:8080/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com:8080/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080/path", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("//hello.com", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("/test/test.jpg", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("/test", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("test", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("", UriKind.RelativeOrAbsolute, ExpectedResult = false)]
+    [TestCase(null, UriKind.RelativeOrAbsolute, ExpectedResult = false)]
+    [TestCase("this is not welformed", UriKind.RelativeOrAbsolute, ExpectedResult = false)]
+    public bool IsWellFormedWebPath(string? webPath, UriKind uriKind) => WebPath.IsWellFormedWebPath(webPath, uriKind);
+
 }

programming_language: C#
repo_url: https://github.com/umbraco/Umbraco-CMS
commit_details: repo_url: https://github.com/umbraco/Umbraco-CMS
author: Bjarke Berg
author_date: 2024-05-17 08:37:51+02:00
msg: Merge pull request from GHSA-j74q-mv2c-rxmp
repository: repo_url: https://github.com/umbraco/Umbraco-CMS
repo_name: umbraco/Umbraco-CMS
description: Umbraco is a free and open source .NET content management system helping you deliver delightful digital experiences.
repo_language: C#
owner: umbraco
file_changes: filename: WebPath.cs
change_type: ModificationType.MODIFY
diff: @@ -50,4 +50,28 @@ public static string Combine(params string[]? paths)
 
         return sb.ToString();
     }
+
+
+    /// <summary>
+    /// Determines whether the provided web path is well-formed according to the specified UriKind.
+    /// </summary>
+    /// <param name="webPath">The web path to check. This can be null.</param>
+    /// <param name="uriKind">The kind of Uri (Absolute, Relative, or RelativeOrAbsolute).</param>
+    /// <returns>
+    /// true if <paramref name="webPath"/> is well-formed; otherwise, false.
+    /// </returns>
+    public static bool IsWellFormedWebPath(string? webPath, UriKind uriKind)
+    {
+        if (string.IsNullOrWhiteSpace(webPath))
+        {
+            return false;
+        }
+
+        if (webPath.StartsWith("//"))
+        {
+            return uriKind is not UriKind.Relative;
+        }
+
+        return Uri.IsWellFormedUriString(webPath, uriKind);
+    }
 }

programming_language: C#
filename: ImagesController.cs
change_type: ModificationType.MODIFY
diff: @@ -7,6 +7,7 @@
 using Umbraco.Cms.Core.IO;
 using Umbraco.Cms.Core.Media;
 using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Routing;
 using Umbraco.Cms.Web.Common.Attributes;
 using Umbraco.Cms.Web.Common.DependencyInjection;
 using Umbraco.Extensions;
@@ -123,7 +124,7 @@ public IActionResult GetResized(string imagePath, int width)
 
     private bool IsAllowed(string encodedImagePath)
     {
-        if(Uri.IsWellFormedUriString(encodedImagePath, UriKind.Relative))
+        if(WebPath.IsWellFormedWebPath(encodedImagePath, UriKind.Relative))
         {
             return true;
         }

programming_language: C#
filename: PreviewController.cs
change_type: ModificationType.MODIFY
diff: @@ -11,6 +11,7 @@
 using Umbraco.Cms.Core.Models.Membership;
 using Umbraco.Cms.Core.Models.PublishedContent;
 using Umbraco.Cms.Core.PublishedCache;
+using Umbraco.Cms.Core.Routing;
 using Umbraco.Cms.Core.Security;
 using Umbraco.Cms.Core.Services;
 using Umbraco.Cms.Core.Web;
@@ -152,8 +153,7 @@ public ActionResult End(string? redir = null)
         // Expire Client-side cookie that determines whether the user has accepted to be in Preview Mode when visiting the website.
         _cookieManager.ExpireCookie(Constants.Web.AcceptPreviewCookieName);
 
-        if (Uri.IsWellFormedUriString(redir, UriKind.Relative)
-            && redir.StartsWith("//") == false
+        if (WebPath.IsWellFormedWebPath(redir, UriKind.Relative)
             && Uri.TryCreate(redir, UriKind.Relative, out Uri? url))
         {
             return Redirect(url.ToString());

programming_language: C#
filename: WebPathTests.cs
change_type: ModificationType.MODIFY
diff: @@ -31,4 +31,87 @@ public class WebPathTests
 
     [Test]
     public void Combine_must_handle_null() => Assert.Throws<ArgumentNullException>(() => WebPath.Combine(null));
+
+
+    [Test]
+    [TestCase("ftp://hello.com/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("file:///hello.com/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("ws://hello.com/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("wss://hello.com/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com:8080/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com:8080/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path?query=param", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path?query=param", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com:8080/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com:8080/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080/path", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("//hello.com", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("/test/test.jpg", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("/test", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("test", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase(null, UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("this is not welformed", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("ftp://hello.com/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("file:///hello.com/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("ws://hello.com/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("wss://hello.com/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com:8080/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com:8080/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com/path", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com/path", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com/path?query=param", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com/path?query=param", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com/path#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com/path#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com:8080/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com:8080/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("//hello.com:8080/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("//hello.com:8080/path", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("//hello.com:8080", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("//hello.com", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("/test/test.jpg", UriKind.Relative, ExpectedResult = true)]
+    [TestCase("/test", UriKind.Relative, ExpectedResult = true)]
+    [TestCase("test", UriKind.Relative, ExpectedResult = true)]
+    [TestCase("", UriKind.Relative, ExpectedResult = false)]
+    [TestCase(null, UriKind.Relative, ExpectedResult = false)]
+    [TestCase("this is not welformed", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("ftp://hello.com/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("file:///hello.com/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("ws://hello.com/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("wss://hello.com/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com:8080/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com:8080/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path?query=param", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path?query=param", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com:8080/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com:8080/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080/path", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("//hello.com", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("/test/test.jpg", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("/test", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("test", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("", UriKind.RelativeOrAbsolute, ExpectedResult = false)]
+    [TestCase(null, UriKind.RelativeOrAbsolute, ExpectedResult = false)]
+    [TestCase("this is not welformed", UriKind.RelativeOrAbsolute, ExpectedResult = false)]
+    public bool IsWellFormedWebPath(string? webPath, UriKind uriKind) => WebPath.IsWellFormedWebPath(webPath, uriKind);
+
 }

programming_language: C#
repo_url: https://github.com/umbraco/Umbraco-CMS
commit_details: repo_url: https://github.com/umbraco/Umbraco-CMS
author: Bjarke Berg
author_date: 2024-05-17 08:37:51+02:00
msg: Merge pull request from GHSA-j74q-mv2c-rxmp
repository: repo_url: https://github.com/umbraco/Umbraco-CMS
repo_name: umbraco/Umbraco-CMS
description: Umbraco is a free and open source .NET content management system helping you deliver delightful digital experiences.
repo_language: C#
owner: umbraco
file_changes: filename: WebPath.cs
change_type: ModificationType.MODIFY
diff: @@ -50,4 +50,28 @@ public static string Combine(params string[]? paths)
 
         return sb.ToString();
     }
+
+
+    /// <summary>
+    /// Determines whether the provided web path is well-formed according to the specified UriKind.
+    /// </summary>
+    /// <param name="webPath">The web path to check. This can be null.</param>
+    /// <param name="uriKind">The kind of Uri (Absolute, Relative, or RelativeOrAbsolute).</param>
+    /// <returns>
+    /// true if <paramref name="webPath"/> is well-formed; otherwise, false.
+    /// </returns>
+    public static bool IsWellFormedWebPath(string? webPath, UriKind uriKind)
+    {
+        if (string.IsNullOrWhiteSpace(webPath))
+        {
+            return false;
+        }
+
+        if (webPath.StartsWith("//"))
+        {
+            return uriKind is not UriKind.Relative;
+        }
+
+        return Uri.IsWellFormedUriString(webPath, uriKind);
+    }
 }

programming_language: C#
filename: ImagesController.cs
change_type: ModificationType.MODIFY
diff: @@ -8,6 +8,7 @@
 using Umbraco.Cms.Core.IO;
 using Umbraco.Cms.Core.Media;
 using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Routing;
 using Umbraco.Cms.Web.Common.Attributes;
 using Umbraco.Cms.Web.Common.DependencyInjection;
 using Umbraco.Extensions;
@@ -122,7 +123,7 @@ public IActionResult GetResized(string imagePath, int width)
 
     private bool IsAllowed(string encodedImagePath)
     {
-        if(Uri.IsWellFormedUriString(encodedImagePath, UriKind.Relative))
+        if(WebPath.IsWellFormedWebPath(encodedImagePath, UriKind.Relative))
         {
             return true;
         }

programming_language: C#
filename: PreviewController.cs
change_type: ModificationType.MODIFY
diff: @@ -11,6 +11,7 @@
 using Umbraco.Cms.Core.Models.Membership;
 using Umbraco.Cms.Core.Models.PublishedContent;
 using Umbraco.Cms.Core.PublishedCache;
+using Umbraco.Cms.Core.Routing;
 using Umbraco.Cms.Core.Security;
 using Umbraco.Cms.Core.Services;
 using Umbraco.Cms.Core.Web;
@@ -152,8 +153,7 @@ public ActionResult End(string? redir = null)
         // Expire Client-side cookie that determines whether the user has accepted to be in Preview Mode when visiting the website.
         _cookieManager.ExpireCookie(Constants.Web.AcceptPreviewCookieName);
 
-        if (Uri.IsWellFormedUriString(redir, UriKind.Relative)
-            && redir.StartsWith("//") == false
+        if (WebPath.IsWellFormedWebPath(redir, UriKind.Relative)
             && Uri.TryCreate(redir, UriKind.Relative, out Uri? url))
         {
             return Redirect(url.ToString());

programming_language: C#
filename: WebPathTests.cs
change_type: ModificationType.MODIFY
diff: @@ -30,4 +30,87 @@ public class WebPathTests
 
     [Test]
     public void Combine_must_handle_null() => Assert.Throws<ArgumentNullException>(() => WebPath.Combine(null));
+
+
+    [Test]
+    [TestCase("ftp://hello.com/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("file:///hello.com/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("ws://hello.com/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("wss://hello.com/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com:8080/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com:8080/", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path?query=param", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path?query=param", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("https://hello.com:8080/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("http://hello.com:8080/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080/path?query=param#fragment", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080/path", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("//hello.com", UriKind.Absolute, ExpectedResult = true)]
+    [TestCase("/test/test.jpg", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("/test", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("test", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase(null, UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("this is not welformed", UriKind.Absolute, ExpectedResult = false)]
+    [TestCase("ftp://hello.com/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("file:///hello.com/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("ws://hello.com/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("wss://hello.com/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com:8080/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com:8080/", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com/path", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com/path", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com/path?query=param", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com/path?query=param", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com/path#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com/path#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("https://hello.com:8080/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("http://hello.com:8080/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("//hello.com:8080/path?query=param#fragment", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("//hello.com:8080/path", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("//hello.com:8080", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("//hello.com", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("/test/test.jpg", UriKind.Relative, ExpectedResult = true)]
+    [TestCase("/test", UriKind.Relative, ExpectedResult = true)]
+    [TestCase("test", UriKind.Relative, ExpectedResult = true)]
+    [TestCase("", UriKind.Relative, ExpectedResult = false)]
+    [TestCase(null, UriKind.Relative, ExpectedResult = false)]
+    [TestCase("this is not welformed", UriKind.Relative, ExpectedResult = false)]
+    [TestCase("ftp://hello.com/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("file:///hello.com/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("ws://hello.com/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("wss://hello.com/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com:8080/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com:8080/", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path?query=param", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path?query=param", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("https://hello.com:8080/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("http://hello.com:8080/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080/path?query=param#fragment", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080/path", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("//hello.com:8080", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("//hello.com", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("/test/test.jpg", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("/test", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("test", UriKind.RelativeOrAbsolute, ExpectedResult = true)]
+    [TestCase("", UriKind.RelativeOrAbsolute, ExpectedResult = false)]
+    [TestCase(null, UriKind.RelativeOrAbsolute, ExpectedResult = false)]
+    [TestCase("this is not welformed", UriKind.RelativeOrAbsolute, ExpectedResult = false)]
+    public bool IsWellFormedWebPath(string? webPath, UriKind uriKind) => WebPath.IsWellFormedWebPath(webPath, uriKind);
+
 }

programming_language: C#