diff --git a/src/Mvc/Mvc.Core/src/Routing/UrlHelperBase.cs b/src/Mvc/Mvc.Core/src/Routing/UrlHelperBase.cs index 2cea4a9fc7f2..54eedb0e45c4 100644 --- a/src/Mvc/Mvc.Core/src/Routing/UrlHelperBase.cs +++ b/src/Mvc/Mvc.Core/src/Routing/UrlHelperBase.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -60,7 +60,7 @@ public virtual bool IsLocalUrl(string url) // url doesn't start with "//" or "/\" if (url[1] != '/' && url[1] != '\\') { - return true; + return !HasControlCharacter(url.AsSpan(1)); } return false; @@ -78,15 +78,30 @@ public virtual bool IsLocalUrl(string url) // url doesn't start with "~//" or "~/\" if (url[2] != '/' && url[2] != '\\') { - return true; + return !HasControlCharacter(url.AsSpan(2)); } return false; } return false; + + static bool HasControlCharacter(ReadOnlySpan readOnlySpan) + { + // URLs may not contain ASCII control characters. + for (var i = 0; i < readOnlySpan.Length; i++) + { + if (char.IsControl(readOnlySpan[i])) + { + return true; + } + } + + return false; + } } + /// public virtual string Content(string contentPath) { diff --git a/src/Mvc/Mvc.Core/test/Routing/UrlHelperTestBase.cs b/src/Mvc/Mvc.Core/test/Routing/UrlHelperTestBase.cs index a1b3da308869..a0693946bc57 100644 --- a/src/Mvc/Mvc.Core/test/Routing/UrlHelperTestBase.cs +++ b/src/Mvc/Mvc.Core/test/Routing/UrlHelperTestBase.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -288,6 +288,43 @@ public void IsLocalUrl_RejectsInvalidTokenUrls(string url) Assert.False(result); } + [Theory] + [InlineData("\n")] + [InlineData("\\n")] + [InlineData("/\n")] + [InlineData("~\n")] + [InlineData("~/\n")] + public void IsLocalUrl_RejectsUrlWithNewLineAtStart(string url) + { + // Arrange + var helper = CreateUrlHelper(appRoot: string.Empty, host: "www.mysite.com", protocol: null); + + // Act + var result = helper.IsLocalUrl(url); + + // Assert + Assert.False(result); + } + + [Theory] + [InlineData("/\r\nsomepath")] + [InlineData("~/\r\nsomepath")] + [InlineData("/some\npath")] + [InlineData("~/some\npath")] + [InlineData("\\path\b")] + [InlineData("~\\path\b")] + public void IsLocalUrl_RejectsUrlWithControlCharacters(string url) + { + // Arrange + var helper = CreateUrlHelper(appRoot: string.Empty, host: "www.mysite.com", protocol: null); + + // Act + var result = helper.IsLocalUrl(url); + + // Assert + Assert.False(result); + } + [Fact] public void RouteUrlWithDictionary() {