Validate the maximum number of route segments (#911)

This commit is contained in:
James Newton-King 2018-11-14 09:25:14 +13:00 committed by GitHub
parent bd481034fe
commit b6a1de5676
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 14 deletions

View File

@ -20,6 +20,8 @@ namespace Microsoft.AspNetCore.Routing.Template
// /api/template/{id:int} == 1.12
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,
// and 4 results in a combined precedence of 2.14 (decimal).
var precedence = 0m;
@ -40,6 +42,8 @@ namespace Microsoft.AspNetCore.Routing.Template
// See description on ComputeInbound(RouteTemplate)
internal static decimal ComputeInbound(RoutePattern routePattern)
{
ValidateSegementLength(routePattern.PathSegments.Count);
var precedence = 0m;
for (var i = 0; i < routePattern.PathSegments.Count; i++)
@ -62,6 +66,8 @@ namespace Microsoft.AspNetCore.Routing.Template
// /api/template/{id:int} == 5.54
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,
// and 4 results in a combined precedence of 2.14 (decimal).
var precedence = 0m;
@ -82,6 +88,8 @@ namespace Microsoft.AspNetCore.Routing.Template
// see description on ComputeOutbound(RouteTemplate)
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,
// and 4 results in a combined precedence of 2.14 (decimal).
var precedence = 0m;
@ -99,6 +107,15 @@ namespace Microsoft.AspNetCore.Routing.Template
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:
// 5 - Literal segments
// 4 - Multi-part segments && Constrained parameter segments

View File

@ -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);
}
}
}

View File

@ -2,13 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Routing.Template
{
public class RoutePrecedenceTests
public abstract class RoutePrecedenceTestsBase
{
[Theory]
[InlineData("Employees/{id}", "Employees/{employeeId}")]
@ -100,22 +98,34 @@ namespace Microsoft.AspNetCore.Routing.Template
Assert.True(xPrecedence > yPrecedence);
}
private static decimal ComputeMatched(string template)
[Fact]
public void ComputeGenerated_TooManySegments_ThrowHumaneError()
{
return Compute(template, RoutePrecedence.ComputeInbound);
}
private static decimal ComputeGenerated(string template)
{
return Compute(template, RoutePrecedence.ComputeOutbound);
var ex = Assert.Throws<InvalidOperationException>(() =>
{
// 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}");
});
// 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>>();
options.SetupGet(o => o.Value).Returns(new RouteOptions());
var ex = Assert.Throws<InvalidOperationException>(() =>
{
// 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);
return func(parsed);
// Assert
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);
}
}

View File

@ -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);
}
}
}