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

Commit d0a8b5f

Browse files
authored
Port fix for URL helper redirect (#6917)
Fixes #6910
1 parent 9fbb2f1 commit d0a8b5f

File tree

3 files changed

+105
-7
lines changed

3 files changed

+105
-7
lines changed

src/Microsoft.AspNetCore.Mvc.Core/Routing/UrlHelper.cs

+40-6
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,48 @@ public virtual string Action(UrlActionContext actionContext)
102102
/// <inheritdoc />
103103
public virtual bool IsLocalUrl(string url)
104104
{
105-
return
106-
!string.IsNullOrEmpty(url) &&
105+
if (string.IsNullOrEmpty(url))
106+
{
107+
return false;
108+
}
109+
110+
// Allows "/" or "/foo" but not "//" or "/\".
111+
if (url[0] == '/')
112+
{
113+
// url is exactly "/"
114+
if (url.Length == 1)
115+
{
116+
return true;
117+
}
118+
119+
// url doesn't start with "//" or "/\"
120+
if (url[1] != '/' && url[1] != '\\')
121+
{
122+
return true;
123+
}
124+
125+
return false;
126+
}
127+
128+
// Allows "~/" or "~/foo" but not "~//" or "~/\".
129+
if (url[0] == '~' && url.Length > 1 && url[1] == '/')
130+
{
131+
// url is exactly "~/"
132+
if (url.Length == 2)
133+
{
134+
return true;
135+
}
107136

108-
// Allows "/" or "/foo" but not "//" or "/\".
109-
((url[0] == '/' && (url.Length == 1 || (url[1] != '/' && url[1] != '\\'))) ||
137+
// url doesn't start with "~//" or "~/\"
138+
if (url[2] != '/' && url[2] != '\\')
139+
{
140+
return true;
141+
}
110142

111-
// Allows "~/" or "~/foo".
112-
(url.Length > 1 && url[0] == '~' && url[1] == '/'));
143+
return false;
144+
}
145+
146+
return false;
113147
}
114148

115149
/// <inheritdoc />

test/Microsoft.AspNetCore.Mvc.Core.Test/LocalRedirectResultTest.cs

+31-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using Microsoft.AspNetCore.Http.Internal;
88
using Microsoft.AspNetCore.Mvc.Abstractions;
99
using Microsoft.AspNetCore.Mvc.Infrastructure;
10-
using Microsoft.AspNetCore.Mvc.Internal;
1110
using Microsoft.AspNetCore.Mvc.Routing;
1211
using Microsoft.AspNetCore.Routing;
1312
using Microsoft.Extensions.DependencyInjection;
@@ -87,6 +86,10 @@ public async Task Execute_ReturnsExpectedValues()
8786
}
8887

8988
[Theory]
89+
[InlineData("", "//", "/test")]
90+
[InlineData("", "/\\", "/test")]
91+
[InlineData("", "//foo", "/test")]
92+
[InlineData("", "/\\foo", "/test")]
9093
[InlineData("", "Home/About", "/Home/About")]
9194
[InlineData("/myapproot", "http://www.example.com", "/test")]
9295
public async Task Execute_Throws_ForNonLocalUrl(
@@ -111,6 +114,33 @@ public async Task Execute_Throws_ForNonLocalUrl(
111114
exception.Message);
112115
}
113116

117+
[Theory]
118+
[InlineData("", "~//", "//")]
119+
[InlineData("", "~/\\", "/\\")]
120+
[InlineData("", "~//foo", "//foo")]
121+
[InlineData("", "~/\\foo", "/\\foo")]
122+
public async Task Execute_Throws_ForNonLocalUrlTilde(
123+
string appRoot,
124+
string contentPath,
125+
string expectedPath)
126+
{
127+
// Arrange
128+
var httpResponse = new Mock<HttpResponse>();
129+
httpResponse.Setup(o => o.Redirect(expectedPath, false))
130+
.Verifiable();
131+
132+
var httpContext = GetHttpContext(appRoot, contentPath, expectedPath, httpResponse.Object);
133+
var actionContext = GetActionContext(httpContext);
134+
var result = new LocalRedirectResult(contentPath);
135+
136+
// Act & Assert
137+
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => result.ExecuteResultAsync(actionContext));
138+
Assert.Equal(
139+
"The supplied URL is not local. A URL with an absolute path is considered local if it does not " +
140+
"have a host/authority part. URLs using virtual paths ('~/') are also local.",
141+
exception.Message);
142+
}
143+
114144
private static ActionContext GetActionContext(HttpContext httpContext)
115145
{
116146
var routeData = new RouteData();

test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs

+34
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,40 @@ public void IsLocalUrl_RejectsInvalidUrls(string url)
261261
Assert.False(result);
262262
}
263263

264+
[Theory]
265+
[InlineData("~//www.example.com")]
266+
[InlineData("~//www.example.com?")]
267+
[InlineData("~//www.example.com:80")]
268+
[InlineData("~//www.example.com/foobar.html")]
269+
[InlineData("~///www.example.com")]
270+
[InlineData("~//////www.example.com")]
271+
public void IsLocalUrl_RejectsTokenUrlsWithMissingSchemeName(string url)
272+
{
273+
// Arrange
274+
var helper = CreateUrlHelper("www.mysite.com");
275+
276+
// Act
277+
var result = helper.IsLocalUrl(url);
278+
279+
// Assert
280+
Assert.False(result);
281+
}
282+
283+
[Theory]
284+
[InlineData("~/\\")]
285+
[InlineData("~/\\foo")]
286+
public void IsLocalUrl_RejectsInvalidTokenUrls(string url)
287+
{
288+
// Arrange
289+
var helper = CreateUrlHelper("www.mysite.com");
290+
291+
// Act
292+
var result = helper.IsLocalUrl(url);
293+
294+
// Assert
295+
Assert.False(result);
296+
}
297+
264298
[Fact]
265299
public void RouteUrlWithDictionary()
266300
{

0 commit comments

Comments
 (0)