Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Port fix for URL helper redirect #6917

Merged
merged 1 commit into from
Oct 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 40 additions & 6 deletions src/Microsoft.AspNetCore.Mvc.Core/Routing/UrlHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,48 @@ public virtual string Action(UrlActionContext actionContext)
/// <inheritdoc />
public virtual bool IsLocalUrl(string url)
{
return
!string.IsNullOrEmpty(url) &&
if (string.IsNullOrEmpty(url))
{
return false;
}

// Allows "/" or "/foo" but not "//" or "/\".
if (url[0] == '/')
{
// url is exactly "/"
if (url.Length == 1)
{
return true;
}

// url doesn't start with "//" or "/\"
if (url[1] != '/' && url[1] != '\\')
{
return true;
}

return false;
}

// Allows "~/" or "~/foo" but not "~//" or "~/\".
if (url[0] == '~' && url.Length > 1 && url[1] == '/')
{
// url is exactly "~/"
if (url.Length == 2)
{
return true;
}

// Allows "/" or "/foo" but not "//" or "/\".
((url[0] == '/' && (url.Length == 1 || (url[1] != '/' && url[1] != '\\'))) ||
// url doesn't start with "~//" or "~/\"
if (url[2] != '/' && url[2] != '\\')
{
return true;
}

// Allows "~/" or "~/foo".
(url.Length > 1 && url[0] == '~' && url[1] == '/'));
return false;
}

return false;
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -87,6 +86,10 @@ public async Task Execute_ReturnsExpectedValues()
}

[Theory]
[InlineData("", "//", "/test")]
[InlineData("", "/\\", "/test")]
[InlineData("", "//foo", "/test")]
[InlineData("", "/\\foo", "/test")]
[InlineData("", "Home/About", "/Home/About")]
[InlineData("/myapproot", "http://www.example.com", "/test")]
public async Task Execute_Throws_ForNonLocalUrl(
Expand All @@ -111,6 +114,33 @@ public async Task Execute_Throws_ForNonLocalUrl(
exception.Message);
}

[Theory]
[InlineData("", "~//", "//")]
[InlineData("", "~/\\", "/\\")]
[InlineData("", "~//foo", "//foo")]
[InlineData("", "~/\\foo", "/\\foo")]
public async Task Execute_Throws_ForNonLocalUrlTilde(
string appRoot,
string contentPath,
string expectedPath)
{
// Arrange
var httpResponse = new Mock<HttpResponse>();
httpResponse.Setup(o => o.Redirect(expectedPath, false))
.Verifiable();

var httpContext = GetHttpContext(appRoot, contentPath, expectedPath, httpResponse.Object);
var actionContext = GetActionContext(httpContext);
var result = new LocalRedirectResult(contentPath);

// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => result.ExecuteResultAsync(actionContext));
Assert.Equal(
"The supplied URL is not local. A URL with an absolute path is considered local if it does not " +
"have a host/authority part. URLs using virtual paths ('~/') are also local.",
exception.Message);
}

private static ActionContext GetActionContext(HttpContext httpContext)
{
var routeData = new RouteData();
Expand Down
34 changes: 34 additions & 0 deletions test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,40 @@ public void IsLocalUrl_RejectsInvalidUrls(string url)
Assert.False(result);
}

[Theory]
[InlineData("~//www.example.com")]
[InlineData("~//www.example.com?")]
[InlineData("~//www.example.com:80")]
[InlineData("~//www.example.com/foobar.html")]
[InlineData("~///www.example.com")]
[InlineData("~//////www.example.com")]
public void IsLocalUrl_RejectsTokenUrlsWithMissingSchemeName(string url)
{
// Arrange
var helper = CreateUrlHelper("www.mysite.com");

// Act
var result = helper.IsLocalUrl(url);

// Assert
Assert.False(result);
}

[Theory]
[InlineData("~/\\")]
[InlineData("~/\\foo")]
public void IsLocalUrl_RejectsInvalidTokenUrls(string url)
{
// Arrange
var helper = CreateUrlHelper("www.mysite.com");

// Act
var result = helper.IsLocalUrl(url);

// Assert
Assert.False(result);
}

[Fact]
public void RouteUrlWithDictionary()
{
Expand Down