diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/AttributeRouteModel.cs b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/AttributeRouteModel.cs index 4623d67090..333033d00d 100644 --- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/AttributeRouteModel.cs +++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/AttributeRouteModel.cs @@ -231,6 +231,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels var state = TemplateParserState.Plaintext; int? tokenStart = null; + var scope = 0; // We'll run the loop one extra time with 'null' to detect the end of the string. for (var i = 0; i <= template.Length; i++) @@ -241,6 +242,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels case TemplateParserState.Plaintext: if (c == '[') { + scope++; state = TemplateParserState.SeenLeft; break; } @@ -321,6 +323,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels } else if (c == ']') { + --scope; state = TemplateParserState.InsideToken | TemplateParserState.SeenRight; break; } @@ -353,7 +356,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels throw new InvalidOperationException(message); } case TemplateParserState.InsideToken | TemplateParserState.SeenRight: - if (c == ']') + if (c == ']' && scope == 0) { // This is an escaped right-bracket state = TemplateParserState.InsideToken; @@ -402,6 +405,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels state = TemplateParserState.Plaintext; } + scope = 0; tokenStart = null; break; } diff --git a/src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModels/AttributeRouteModelTests.cs b/src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModels/AttributeRouteModelTests.cs index dc312a50bd..c6646c110a 100644 --- a/src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModels/AttributeRouteModelTests.cs +++ b/src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModels/AttributeRouteModelTests.cs @@ -590,6 +590,83 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels }, "Home/Index/{id}" }; + + yield return new object[] + { + "[controller]/[[[action]]]/{id}", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "controller", "Home" }, + { "action", "Index" } + }, + "Home/[Index]/{id}" + }; + + yield return new object[] + { + "[controller]/[[[[[action]]]]]/{id}", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "controller", "Home" }, + { "action", "Index" } + }, + "Home/[[Index]]/{id}" + }; + + yield return new object[] + { + "[controller]/[[[[[[[action]]]]]]]/{id}", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "controller", "Home" }, + { "action", "Index" } + }, + "Home/[[[Index]]]/{id}" + }; + + yield return new object[] + { + "[controller]/[[[[[action]]]]]]]/{id}", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "controller", "Home" }, + { "action", "Index" } + }, + "Home/[[Index]]]/{id}" + }; + + yield return new object[] + { + "[controller]/[[[[[[[action]]]]]/{id}", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "controller", "Home" }, + { "action", "Index" } + }, + "Home/[[[Index]]/{id}" + }; + + yield return new object[] + { + "[controller]/[[[action]]]]]/{id}", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "controller", "Home" }, + { "action", "Index" } + }, + "Home/[Index]]/{id}" + }; + + yield return new object[] + { + "[controller]/[[[[[[[action]]]/{id}", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "controller", "Home" }, + { "action", "Index" } + }, + "Home/[[[Index]/{id}" + }; } } @@ -655,6 +732,61 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels new Dictionary(StringComparer.OrdinalIgnoreCase), "An empty replacement token ('[]') is not allowed.", }; + + yield return new object[] + { + "[controller]/[[[action]]/{id}", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "controller", "Home" }, + { "action", "Index" } + }, + "Token delimiters ('[', ']') are imbalanced.", + }; + + yield return new object[] + { + "[controller]/[[[action]]]]/{id}", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "controller", "Home" }, + { "action", "Index" } + }, + "Token delimiters ('[', ']') are imbalanced.", + }; + + yield return new object[] + { + "[controller]/[[action]]]/{id}", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "controller", "Home" }, + { "action", "Index" } + }, + "Token delimiters ('[', ']') are imbalanced.", + }; + + yield return new object[] + { + "[controller]/[[[[[[[action]]]]]]/{id}", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "controller", "Home" }, + { "action", "Index" } + }, + "Token delimiters ('[', ']') are imbalanced.", + }; + + yield return new object[] + { + "[controller]/[[[[[[action]]]]]]]/{id}", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "controller", "Home" }, + { "action", "Index" } + }, + "Token delimiters ('[', ']') are imbalanced.", + }; } }