Validate the maximum number of route segments (#911)
This commit is contained in:
parent
bd481034fe
commit
b6a1de5676
|
|
@ -20,6 +20,8 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
// /api/template/{id:int} == 1.12
|
// /api/template/{id:int} == 1.12
|
||||||
public static decimal ComputeInbound(RouteTemplate template)
|
public static decimal ComputeInbound(RouteTemplate template)
|
||||||
{
|
{
|
||||||
|
ValidateSegementLength(template.Segments.Count);
|
||||||
|
|
||||||
// Each precedence digit corresponds to one decimal place. For example, 3 segments with precedences 2, 1,
|
// Each precedence digit corresponds to one decimal place. For example, 3 segments with precedences 2, 1,
|
||||||
// and 4 results in a combined precedence of 2.14 (decimal).
|
// and 4 results in a combined precedence of 2.14 (decimal).
|
||||||
var precedence = 0m;
|
var precedence = 0m;
|
||||||
|
|
@ -40,6 +42,8 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
// See description on ComputeInbound(RouteTemplate)
|
// See description on ComputeInbound(RouteTemplate)
|
||||||
internal static decimal ComputeInbound(RoutePattern routePattern)
|
internal static decimal ComputeInbound(RoutePattern routePattern)
|
||||||
{
|
{
|
||||||
|
ValidateSegementLength(routePattern.PathSegments.Count);
|
||||||
|
|
||||||
var precedence = 0m;
|
var precedence = 0m;
|
||||||
|
|
||||||
for (var i = 0; i < routePattern.PathSegments.Count; i++)
|
for (var i = 0; i < routePattern.PathSegments.Count; i++)
|
||||||
|
|
@ -62,6 +66,8 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
// /api/template/{id:int} == 5.54
|
// /api/template/{id:int} == 5.54
|
||||||
public static decimal ComputeOutbound(RouteTemplate template)
|
public static decimal ComputeOutbound(RouteTemplate template)
|
||||||
{
|
{
|
||||||
|
ValidateSegementLength(template.Segments.Count);
|
||||||
|
|
||||||
// Each precedence digit corresponds to one decimal place. For example, 3 segments with precedences 2, 1,
|
// Each precedence digit corresponds to one decimal place. For example, 3 segments with precedences 2, 1,
|
||||||
// and 4 results in a combined precedence of 2.14 (decimal).
|
// and 4 results in a combined precedence of 2.14 (decimal).
|
||||||
var precedence = 0m;
|
var precedence = 0m;
|
||||||
|
|
@ -82,6 +88,8 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
// see description on ComputeOutbound(RouteTemplate)
|
// see description on ComputeOutbound(RouteTemplate)
|
||||||
internal static decimal ComputeOutbound(RoutePattern routePattern)
|
internal static decimal ComputeOutbound(RoutePattern routePattern)
|
||||||
{
|
{
|
||||||
|
ValidateSegementLength(routePattern.PathSegments.Count);
|
||||||
|
|
||||||
// Each precedence digit corresponds to one decimal place. For example, 3 segments with precedences 2, 1,
|
// Each precedence digit corresponds to one decimal place. For example, 3 segments with precedences 2, 1,
|
||||||
// and 4 results in a combined precedence of 2.14 (decimal).
|
// and 4 results in a combined precedence of 2.14 (decimal).
|
||||||
var precedence = 0m;
|
var precedence = 0m;
|
||||||
|
|
@ -99,6 +107,15 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
return precedence;
|
return precedence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ValidateSegementLength(int length)
|
||||||
|
{
|
||||||
|
if (length > 28)
|
||||||
|
{
|
||||||
|
// An OverflowException will be thrown by Math.Pow when greater than 28
|
||||||
|
throw new InvalidOperationException("Route exceeds the maximum number of allowed segments of 28 and is unable to be processed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Segments have the following order:
|
// Segments have the following order:
|
||||||
// 5 - Literal segments
|
// 5 - Literal segments
|
||||||
// 4 - Multi-part segments && Constrained parameter segments
|
// 4 - Multi-part segments && Constrained parameter segments
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Routing.Template
|
||||||
|
{
|
||||||
|
public class RoutePatternPrecedenceTests : RoutePrecedenceTestsBase
|
||||||
|
{
|
||||||
|
protected override decimal ComputeMatched(string template)
|
||||||
|
{
|
||||||
|
return ComputeRoutePattern(template, RoutePrecedence.ComputeInbound);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override decimal ComputeGenerated(string template)
|
||||||
|
{
|
||||||
|
return ComputeRoutePattern(template, RoutePrecedence.ComputeOutbound);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static decimal ComputeRoutePattern(string template, Func<RoutePattern, decimal> func)
|
||||||
|
{
|
||||||
|
var parsed = RoutePatternFactory.Parse(template);
|
||||||
|
return func(parsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,13 +2,11 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Moq;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Routing.Template
|
namespace Microsoft.AspNetCore.Routing.Template
|
||||||
{
|
{
|
||||||
public class RoutePrecedenceTests
|
public abstract class RoutePrecedenceTestsBase
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("Employees/{id}", "Employees/{employeeId}")]
|
[InlineData("Employees/{id}", "Employees/{employeeId}")]
|
||||||
|
|
@ -100,22 +98,34 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
Assert.True(xPrecedence > yPrecedence);
|
Assert.True(xPrecedence > yPrecedence);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static decimal ComputeMatched(string template)
|
[Fact]
|
||||||
|
public void ComputeGenerated_TooManySegments_ThrowHumaneError()
|
||||||
{
|
{
|
||||||
return Compute(template, RoutePrecedence.ComputeInbound);
|
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||||
}
|
{
|
||||||
private static decimal ComputeGenerated(string template)
|
// Arrange & Act
|
||||||
{
|
ComputeGenerated("{a}/{b}/{c}/{d}/{e}/{f}/{g}/{h}/{i}/{j}/{k}/{l}/{m}/{n}/{o}/{p}/{q}/{r}/{s}/{t}/{u}/{v}/{w}/{x}/{y}/{z}/{a2}/{b2}/{b3}");
|
||||||
return Compute(template, RoutePrecedence.ComputeOutbound);
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Route exceeds the maximum number of allowed segments of 28 and is unable to be processed.", ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static decimal Compute(string template, Func<RouteTemplate, decimal> func)
|
[Fact]
|
||||||
|
public void ComputeMatched_TooManySegments_ThrowHumaneError()
|
||||||
{
|
{
|
||||||
var options = new Mock<IOptions<RouteOptions>>();
|
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||||
options.SetupGet(o => o.Value).Returns(new RouteOptions());
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
ComputeMatched("{a}/{b}/{c}/{d}/{e}/{f}/{g}/{h}/{i}/{j}/{k}/{l}/{m}/{n}/{o}/{p}/{q}/{r}/{s}/{t}/{u}/{v}/{w}/{x}/{y}/{z}/{a2}/{b2}/{b3}");
|
||||||
|
});
|
||||||
|
|
||||||
var parsed = TemplateParser.Parse(template);
|
// Assert
|
||||||
return func(parsed);
|
Assert.Equal("Route exceeds the maximum number of allowed segments of 28 and is unable to be processed.", ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract decimal ComputeMatched(string template);
|
||||||
|
|
||||||
|
protected abstract decimal ComputeGenerated(string template);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Routing.Template
|
||||||
|
{
|
||||||
|
public class RouteTemplatePrecedenceTests : RoutePrecedenceTestsBase
|
||||||
|
{
|
||||||
|
protected override decimal ComputeMatched(string template)
|
||||||
|
{
|
||||||
|
return ComputeRouteTemplate(template, RoutePrecedence.ComputeInbound);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override decimal ComputeGenerated(string template)
|
||||||
|
{
|
||||||
|
return ComputeRouteTemplate(template, RoutePrecedence.ComputeOutbound);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static decimal ComputeRouteTemplate(string template, Func<RouteTemplate, decimal> func)
|
||||||
|
{
|
||||||
|
var parsed = TemplateParser.Parse(template);
|
||||||
|
return func(parsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue