Skip to content

Commit 12c0165

Browse files
authored
Reject control characters in IsLocalUrl check (#25378)
Fixes #18109
1 parent 8991f17 commit 12c0165

File tree

2 files changed

+56
-4
lines changed

2 files changed

+56
-4
lines changed

src/Mvc/Mvc.Core/src/Routing/UrlHelperBase.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
@@ -60,7 +60,7 @@ public virtual bool IsLocalUrl(string url)
6060
// url doesn't start with "//" or "/\"
6161
if (url[1] != '/' && url[1] != '\\')
6262
{
63-
return true;
63+
return !HasControlCharacter(url.AsSpan(1));
6464
}
6565

6666
return false;
@@ -78,15 +78,30 @@ public virtual bool IsLocalUrl(string url)
7878
// url doesn't start with "~//" or "~/\"
7979
if (url[2] != '/' && url[2] != '\\')
8080
{
81-
return true;
81+
return !HasControlCharacter(url.AsSpan(2));
8282
}
8383

8484
return false;
8585
}
8686

8787
return false;
88+
89+
static bool HasControlCharacter(ReadOnlySpan<char> readOnlySpan)
90+
{
91+
// URLs may not contain ASCII control characters.
92+
for (var i = 0; i < readOnlySpan.Length; i++)
93+
{
94+
if (char.IsControl(readOnlySpan[i]))
95+
{
96+
return true;
97+
}
98+
}
99+
100+
return false;
101+
}
88102
}
89103

104+
90105
/// <inheritdoc />
91106
public virtual string Content(string contentPath)
92107
{

src/Mvc/Mvc.Core/test/Routing/UrlHelperTestBase.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
@@ -288,6 +288,43 @@ public void IsLocalUrl_RejectsInvalidTokenUrls(string url)
288288
Assert.False(result);
289289
}
290290

291+
[Theory]
292+
[InlineData("\n")]
293+
[InlineData("\\n")]
294+
[InlineData("/\n")]
295+
[InlineData("~\n")]
296+
[InlineData("~/\n")]
297+
public void IsLocalUrl_RejectsUrlWithNewLineAtStart(string url)
298+
{
299+
// Arrange
300+
var helper = CreateUrlHelper(appRoot: string.Empty, host: "www.mysite.com", protocol: null);
301+
302+
// Act
303+
var result = helper.IsLocalUrl(url);
304+
305+
// Assert
306+
Assert.False(result);
307+
}
308+
309+
[Theory]
310+
[InlineData("/\r\nsomepath")]
311+
[InlineData("~/\r\nsomepath")]
312+
[InlineData("/some\npath")]
313+
[InlineData("~/some\npath")]
314+
[InlineData("\\path\b")]
315+
[InlineData("~\\path\b")]
316+
public void IsLocalUrl_RejectsUrlWithControlCharacters(string url)
317+
{
318+
// Arrange
319+
var helper = CreateUrlHelper(appRoot: string.Empty, host: "www.mysite.com", protocol: null);
320+
321+
// Act
322+
var result = helper.IsLocalUrl(url);
323+
324+
// Assert
325+
Assert.False(result);
326+
}
327+
291328
[Fact]
292329
public void RouteUrlWithDictionary()
293330
{

0 commit comments

Comments
 (0)