From 42b988ad888f285df6aa19e6f93abeb7d640abca Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Mon, 24 Apr 2017 15:36:25 -0700 Subject: [PATCH] ./ paths relative on page. --- .../Internal/ViewEnginePath.cs | 9 ++- .../Resources.resx | 2 +- .../RedirectToPageResultTest.cs | 57 +++++++++++++++++++ .../Routing/UrlHelperTest.cs | 2 +- .../RazorPagesTest.cs | 41 ++++++++++--- .../RazorViewEngineTest.cs | 1 + .../Pages/Redirects/RedirectToSibling.cshtml | 2 + .../Pages/TagHelper/PathTraversalLinks.cshtml | 3 +- 8 files changed, 106 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ViewEnginePath.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ViewEnginePath.cs index 514bb57719..18f69cbec6 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ViewEnginePath.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ViewEnginePath.cs @@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal { public static class ViewEnginePath { + private const string CurrentDirectoryToken = "."; private const string ParentDirectoryToken = ".."; private static readonly char[] _pathSeparators = new[] { '/', '\\' }; @@ -70,6 +71,11 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal } pathSegments.RemoveAt(pathSegments.Count - 1); } + else if (segment.Equals(CurrentDirectoryToken, StringComparison.Ordinal)) + { + // We already have the current directory + continue; + } else { pathSegments.Add(segment); @@ -89,7 +95,8 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal private static bool RequiresPathResolution(string path) { - return path.IndexOf(ParentDirectoryToken, StringComparison.Ordinal) != -1; + return path.IndexOf(ParentDirectoryToken, StringComparison.Ordinal) != -1 || + path.IndexOf(CurrentDirectoryToken, StringComparison.Ordinal) != -1; } } } diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Resources.resx b/src/Microsoft.AspNetCore.Mvc.Core/Resources.resx index 8fd48ee9ce..82da0b9f83 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Resources.resx +++ b/src/Microsoft.AspNetCore.Mvc.Core/Resources.resx @@ -404,6 +404,6 @@ No page named '{0}' matches the supplied values. - The relative page path '{0}' can only can only be used while executing a Razor Page. Specify a root relative path with a leading '/' to generate a URL outside of a Razor Page. + The relative page path '{0}' can only be used while executing a Razor Page. Specify a root relative path with a leading '/' to generate a URL outside of a Razor Page. \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/RedirectToPageResultTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/RedirectToPageResultTest.cs index 27c7dba121..6b98f0086f 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/RedirectToPageResultTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/RedirectToPageResultTest.cs @@ -77,6 +77,63 @@ namespace Microsoft.AspNetCore.Mvc httpResponse.Verify(r => r.Redirect(expectedUrl, permanentRedirect), Times.Exactly(1)); } + [Fact] + public async Task ExecuteResultAsync_LocalRelativePaths() + { + // Arrange + var httpContext = new DefaultHttpContext + { + RequestServices = CreateServices(), + }; + + var pageContext = new PageContext + { + HttpContext = httpContext, + RouteData = new RouteData(), + ActionDescriptor = new CompiledPageActionDescriptor(), + }; + + pageContext.RouteData.Values.Add("page", "/A/Redirecting/Page"); + + UrlRouteContext context = null; + var urlHelper = new Mock(); + urlHelper.SetupGet(h => h.ActionContext).Returns(pageContext); + urlHelper.Setup(h => h.RouteUrl(It.IsAny())) + .Callback((UrlRouteContext c) => context = c) + .Returns("some-value"); + var values = new { test = "test-value" }; + var result = new RedirectToPageResult("./", "page-handler", values, true, "test-fragment") + { + UrlHelper = urlHelper.Object, + Protocol = "ftp", + }; + + // Act + await result.ExecuteResultAsync(pageContext); + + // Assert + Assert.NotNull(context); + Assert.Null(context.RouteName); + Assert.Collection(Assert.IsType(context.Values), + value => + { + Assert.Equal("test", value.Key); + Assert.Equal("test-value", value.Value); + }, + value => + { + Assert.Equal("page", value.Key); + Assert.Equal("/A/Redirecting", value.Value); + }, + value => + { + Assert.Equal("handler", value.Key); + Assert.Equal("page-handler", value.Value); + }); + Assert.Equal("ftp", context.Protocol); + Assert.Equal("test-fragment", context.Fragment); + } + [Fact] public async Task ExecuteResultAsync_WithAllParameters() { diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs index a1e337e607..563cfd2410 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs @@ -1518,7 +1518,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing // Act & Assert var ex = Assert.Throws(() => urlHelper.Object.Page(expected)); - Assert.Equal($"The relative page path '{expected}' can only can only be used while executing a Razor Page. " + + Assert.Equal($"The relative page path '{expected}' can only be used while executing a Razor Page. " + "Specify a root relative path with a leading '/' to generate a URL outside of a Razor Page.", ex.Message); } diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs index 5805b21e1b..8a6610eb7a 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs @@ -924,6 +924,20 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore._InjectedP Assert.Equal(expected, response.Headers.Location.ToString()); } + [Fact] + public async Task RedirectToSibling_RedirectsToDotSlash() + { + // Arrange + var expected = "/Pages/Redirects/SubDir/SubDirPage"; + + // Act + var response = await Client.GetAsync("/Pages/Redirects/RedirectToSibling/RedirectToDotSlash"); + + // Assert + Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); + Assert.Equal(expected, response.Headers.Location.ToString()); + } + [Fact] public async Task RedirectToSibling_RedirectsToParentDirectory() { @@ -940,8 +954,8 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore._InjectedP public async Task TagHelpers_SupportSiblingRoutes() { // Arrange -var expected = -@"
+ var expected = + @"
"; @@ -956,8 +970,8 @@ var expected = public async Task TagHelpers_SupportSubDirectoryRoutes() { // Arrange -var expected = -@"
+ var expected = + @"
"; @@ -972,8 +986,8 @@ var expected = public async Task TagHelpers_SupportsPathNavigation() { // Arrange -var expected = -@"
+ var expected = + @"
"; @@ -981,7 +995,20 @@ var expected = var response = await Client.GetStringAsync("/Pages/TagHelper/PathTraversalLinks"); // Assert - Assert.Equal(expected, response.Trim()); + Assert.EndsWith(expected, response.Trim()); + } + + [Fact] + public async Task TagHelpers_SupportsRelativeNavigation() + { + // Arrange + var expected = @"
"; + + // Act + var response = await Client.GetStringAsync("/Pages/TagHelper/PathTraversalLinks"); + + // Assert + Assert.StartsWith(expected, response.Trim()); } private async Task AddAntiforgeryHeaders(HttpRequestMessage request) diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs index 48e489507d..f664954c94 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs @@ -1472,6 +1472,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test [InlineData("/Views/Home/Index.cshtml", "..\\Shared\\_Partial.cshtml")] [InlineData("/Areas/MyArea/Views/Home/Index.cshtml", "../../../../Views/Shared/_Partial.cshtml")] [InlineData("/Views/Accounts/Users.cshtml", "../Test/../Shared/_Partial.cshtml")] + [InlineData("Views/Accounts/Users.cshtml", "./../Shared/./_Partial.cshtml")] public void GetAbsolutePath_ResolvesPathTraversals(string executingFilePath, string pagePath) { // Arrange diff --git a/test/WebSites/RazorPagesWebSite/Pages/Redirects/RedirectToSibling.cshtml b/test/WebSites/RazorPagesWebSite/Pages/Redirects/RedirectToSibling.cshtml index acb01f4acb..f0935c53db 100644 --- a/test/WebSites/RazorPagesWebSite/Pages/Redirects/RedirectToSibling.cshtml +++ b/test/WebSites/RazorPagesWebSite/Pages/Redirects/RedirectToSibling.cshtml @@ -8,4 +8,6 @@ public IActionResult OnGetRedirectToSubDir() => RedirectToPage("SubDir/SubDirPage"); public IActionResult OnGetRedirectToParent() => RedirectToPage("../Conventions/AuthFolder/Index"); + + public IActionResult OnGetRedirectToDotSlash() => RedirectToPage("./SubDir/SubDirPage"); } diff --git a/test/WebSites/RazorPagesWebSite/Pages/TagHelper/PathTraversalLinks.cshtml b/test/WebSites/RazorPagesWebSite/Pages/TagHelper/PathTraversalLinks.cshtml index bfa5615ed2..456deacfaf 100644 --- a/test/WebSites/RazorPagesWebSite/Pages/TagHelper/PathTraversalLinks.cshtml +++ b/test/WebSites/RazorPagesWebSite/Pages/TagHelper/PathTraversalLinks.cshtml @@ -1,5 +1,6 @@ @page +
- \ No newline at end of file +