./ paths relative on page.

This commit is contained in:
Ryan Brandenburg 2017-04-24 15:36:25 -07:00
parent 2d2ed571df
commit 42b988ad88
8 changed files with 106 additions and 11 deletions

View File

@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
{ {
public static class ViewEnginePath public static class ViewEnginePath
{ {
private const string CurrentDirectoryToken = ".";
private const string ParentDirectoryToken = ".."; private const string ParentDirectoryToken = "..";
private static readonly char[] _pathSeparators = new[] { '/', '\\' }; private static readonly char[] _pathSeparators = new[] { '/', '\\' };
@ -70,6 +71,11 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
} }
pathSegments.RemoveAt(pathSegments.Count - 1); pathSegments.RemoveAt(pathSegments.Count - 1);
} }
else if (segment.Equals(CurrentDirectoryToken, StringComparison.Ordinal))
{
// We already have the current directory
continue;
}
else else
{ {
pathSegments.Add(segment); pathSegments.Add(segment);
@ -89,7 +95,8 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
private static bool RequiresPathResolution(string path) 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;
} }
} }
} }

View File

@ -404,6 +404,6 @@
<value>No page named '{0}' matches the supplied values.</value> <value>No page named '{0}' matches the supplied values.</value>
</data> </data>
<data name="UrlHelper_RelativePagePathIsNotSupported" xml:space="preserve"> <data name="UrlHelper_RelativePagePathIsNotSupported" xml:space="preserve">
<value>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.</value> <value>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.</value>
</data> </data>
</root> </root>

View File

@ -77,6 +77,63 @@ namespace Microsoft.AspNetCore.Mvc
httpResponse.Verify(r => r.Redirect(expectedUrl, permanentRedirect), Times.Exactly(1)); 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<IUrlHelper>();
urlHelper.SetupGet(h => h.ActionContext).Returns(pageContext);
urlHelper.Setup(h => h.RouteUrl(It.IsAny<UrlRouteContext>()))
.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<RouteValueDictionary>(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] [Fact]
public async Task ExecuteResultAsync_WithAllParameters() public async Task ExecuteResultAsync_WithAllParameters()
{ {

View File

@ -1518,7 +1518,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
// Act & Assert // Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() => urlHelper.Object.Page(expected)); var ex = Assert.Throws<InvalidOperationException>(() => 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); "Specify a root relative path with a leading '/' to generate a URL outside of a Razor Page.", ex.Message);
} }

View File

@ -924,6 +924,20 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore._InjectedP
Assert.Equal(expected, response.Headers.Location.ToString()); 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] [Fact]
public async Task RedirectToSibling_RedirectsToParentDirectory() public async Task RedirectToSibling_RedirectsToParentDirectory()
{ {
@ -940,8 +954,8 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore._InjectedP
public async Task TagHelpers_SupportSiblingRoutes() public async Task TagHelpers_SupportSiblingRoutes()
{ {
// Arrange // Arrange
var expected = var expected =
@"<form method=""post"" action=""/Pages/TagHelper/CrossPost""></form> @"<form method=""post"" action=""/Pages/TagHelper/CrossPost""></form>
<a href=""/Pages/TagHelper/SelfPost/12"" /> <a href=""/Pages/TagHelper/SelfPost/12"" />
<input type=""image"" formaction=""/Pages/TagHelper/CrossPost#my-fragment"" />"; <input type=""image"" formaction=""/Pages/TagHelper/CrossPost#my-fragment"" />";
@ -956,8 +970,8 @@ var expected =
public async Task TagHelpers_SupportSubDirectoryRoutes() public async Task TagHelpers_SupportSubDirectoryRoutes()
{ {
// Arrange // Arrange
var expected = var expected =
@"<form method=""post"" action=""/Pages/TagHelper/SubDir/SubDirPage""></form> @"<form method=""post"" action=""/Pages/TagHelper/SubDir/SubDirPage""></form>
<a href=""/Pages/TagHelper/SubDir/SubDirPage/12"" /> <a href=""/Pages/TagHelper/SubDir/SubDirPage/12"" />
<input type=""image"" formaction=""/Pages/TagHelper/SubDir/SubDirPage#my-fragment"" />"; <input type=""image"" formaction=""/Pages/TagHelper/SubDir/SubDirPage#my-fragment"" />";
@ -972,8 +986,8 @@ var expected =
public async Task TagHelpers_SupportsPathNavigation() public async Task TagHelpers_SupportsPathNavigation()
{ {
// Arrange // Arrange
var expected = var expected =
@"<form method=""post"" action=""/HelloWorld""></form> @"<form method=""post"" action=""/HelloWorld""></form>
<a href=""/Pages/Redirects/RedirectToIndex"" /> <a href=""/Pages/Redirects/RedirectToIndex"" />
<input type=""image"" formaction=""/Pages/Admin#my-fragment"" />"; <input type=""image"" formaction=""/Pages/Admin#my-fragment"" />";
@ -981,7 +995,20 @@ var expected =
var response = await Client.GetStringAsync("/Pages/TagHelper/PathTraversalLinks"); var response = await Client.GetStringAsync("/Pages/TagHelper/PathTraversalLinks");
// Assert // Assert
Assert.Equal(expected, response.Trim()); Assert.EndsWith(expected, response.Trim());
}
[Fact]
public async Task TagHelpers_SupportsRelativeNavigation()
{
// Arrange
var expected = @"<form method=""post"" action=""/Pages/TagHelper/SubDirectoryLinks""></form>";
// Act
var response = await Client.GetStringAsync("/Pages/TagHelper/PathTraversalLinks");
// Assert
Assert.StartsWith(expected, response.Trim());
} }
private async Task AddAntiforgeryHeaders(HttpRequestMessage request) private async Task AddAntiforgeryHeaders(HttpRequestMessage request)

View File

@ -1472,6 +1472,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
[InlineData("/Views/Home/Index.cshtml", "..\\Shared\\_Partial.cshtml")] [InlineData("/Views/Home/Index.cshtml", "..\\Shared\\_Partial.cshtml")]
[InlineData("/Areas/MyArea/Views/Home/Index.cshtml", "../../../../Views/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", "../Test/../Shared/_Partial.cshtml")]
[InlineData("Views/Accounts/Users.cshtml", "./../Shared/./_Partial.cshtml")]
public void GetAbsolutePath_ResolvesPathTraversals(string executingFilePath, string pagePath) public void GetAbsolutePath_ResolvesPathTraversals(string executingFilePath, string pagePath)
{ {
// Arrange // Arrange

View File

@ -8,4 +8,6 @@
public IActionResult OnGetRedirectToSubDir() => RedirectToPage("SubDir/SubDirPage"); public IActionResult OnGetRedirectToSubDir() => RedirectToPage("SubDir/SubDirPage");
public IActionResult OnGetRedirectToParent() => RedirectToPage("../Conventions/AuthFolder/Index"); public IActionResult OnGetRedirectToParent() => RedirectToPage("../Conventions/AuthFolder/Index");
public IActionResult OnGetRedirectToDotSlash() => RedirectToPage("./SubDir/SubDirPage");
} }

View File

@ -1,5 +1,6 @@
@page @page
<form method="post" asp-page="./SubDirectoryLinks" asp-antiforgery="false"></form>
<form method="post" asp-page="../../HelloWorld" asp-antiforgery="false"></form> <form method="post" asp-page="../../HelloWorld" asp-antiforgery="false"></form>
<a asp-page="../Redirects/Index" asp-route-handler="RedirectToIndex" /> <a asp-page="../Redirects/Index" asp-route-handler="RedirectToIndex" />
<input type="image" asp-page="../Admin/Index" asp-fragment="my-fragment" /> <input type="image" asp-page="../Admin/Index" asp-fragment="my-fragment" />