diff --git a/.azure/pipelines/jobs/default-build.yml b/.azure/pipelines/jobs/default-build.yml index f3eb0197a617..2908d5bb869a 100644 --- a/.azure/pipelines/jobs/default-build.yml +++ b/.azure/pipelines/jobs/default-build.yml @@ -94,7 +94,7 @@ jobs: # See https://github.com/dotnet/arcade/blob/master/Documentation/ChoosingAMachinePool.md pool: ${{ if eq(parameters.agentOs, 'macOS') }}: - vmImage: macOS-10.15 + vmImage: macOS-11 ${{ if eq(parameters.agentOs, 'Linux') }}: ${{ if and(eq(parameters.useHostedUbuntu, true), or(ne(variables['System.TeamProject'], 'internal'), in(variables['Build.Reason'], 'Manual', 'PullRequest', 'Schedule'))) }}: vmImage: ubuntu-18.04 @@ -151,8 +151,8 @@ jobs: - script: df -h displayName: Disk size - ${{ if eq(parameters.agentOs, 'macOS') }}: - - script: sudo xcode-select -s /Applications/Xcode_12.2.app/Contents/Developer - displayName: Use XCode 12.2 + - script: sudo xcode-select -s /Applications/Xcode_12.5.1.app/Contents/Developer + displayName: Use XCode 12.5.1 - checkout: self clean: true - ${{ if and(eq(parameters.agentOs, 'Windows'), eq(parameters.isTestingJob, true)) }}: diff --git a/NuGet.config b/NuGet.config index 0057e5ff395f..63330d10bbf3 100644 --- a/NuGet.config +++ b/NuGet.config @@ -4,10 +4,10 @@ - + - + @@ -27,10 +27,10 @@ - + - + diff --git a/eng/Baseline.Designer.props b/eng/Baseline.Designer.props index dc5385edc181..28042e9d0222 100644 --- a/eng/Baseline.Designer.props +++ b/eng/Baseline.Designer.props @@ -2,28 +2,28 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - - - + + + @@ -34,120 +34,120 @@ - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - - + + - 6.0.7 + 6.0.8 - - + + - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - + - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 @@ -155,114 +155,114 @@ - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - + - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - + - 6.0.7 + 6.0.8 - - + + - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - - + + - 6.0.7 + 6.0.8 - + - 6.0.7 + 6.0.8 - - - + + + - 6.0.7 + 6.0.8 - - + + - 6.0.7 + 6.0.8 - - + + - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - - + + @@ -270,7 +270,7 @@ - 6.0.7 + 6.0.8 @@ -278,132 +278,132 @@ - 6.0.7 + 6.0.8 - + - + - + - + - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - - + + - + - - + + - + - - + + - + - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - - + + - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - + - 6.0.7 + 6.0.8 @@ -411,71 +411,71 @@ - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - + - + - + - + - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - - + + - 6.0.7 + 6.0.8 - - + + - 6.0.7 + 6.0.8 @@ -491,195 +491,195 @@ - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - + - 6.0.7 + 6.0.8 - - + + - 6.0.7 + 6.0.8 - - + + - 6.0.7 + 6.0.8 - + - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - + - 6.0.7 + 6.0.8 - - + + - - + + - - + + - 6.0.7 + 6.0.8 - - + + - - + + - - + + - - + + - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - - - - + + + + - 6.0.7 + 6.0.8 @@ -688,69 +688,69 @@ - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - + - 6.0.7 + 6.0.8 - + - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 @@ -769,7 +769,7 @@ - 6.0.7 + 6.0.8 @@ -788,7 +788,7 @@ - 6.0.7 + 6.0.8 @@ -804,46 +804,46 @@ - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - - - + + + - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 @@ -853,7 +853,7 @@ - 6.0.7 + 6.0.8 @@ -862,73 +862,73 @@ - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - + - + - + - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 @@ -957,11 +957,11 @@ - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 @@ -979,13 +979,13 @@ - 6.0.7 + 6.0.8 - 6.0.7 + 6.0.8 - + \ No newline at end of file diff --git a/eng/Baseline.xml b/eng/Baseline.xml index 79b54e2ee34d..549d55431ba9 100644 --- a/eng/Baseline.xml +++ b/eng/Baseline.xml @@ -4,111 +4,111 @@ This file contains a list of all the packages and their versions which were rele Update this list when preparing for a new patch. --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2036ca3cb2d8..bf05f45e33d6 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -9,37 +9,37 @@ --> - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 62e1b2f494af376a4588bf3d365f7be5443d4a5d + 33e3c950af2eb996c0b3c48e30eb4471138da675 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 62e1b2f494af376a4588bf3d365f7be5443d4a5d + 33e3c950af2eb996c0b3c48e30eb4471138da675 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 62e1b2f494af376a4588bf3d365f7be5443d4a5d + 33e3c950af2eb996c0b3c48e30eb4471138da675 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 62e1b2f494af376a4588bf3d365f7be5443d4a5d + 33e3c950af2eb996c0b3c48e30eb4471138da675 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 62e1b2f494af376a4588bf3d365f7be5443d4a5d + 33e3c950af2eb996c0b3c48e30eb4471138da675 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 62e1b2f494af376a4588bf3d365f7be5443d4a5d + 33e3c950af2eb996c0b3c48e30eb4471138da675 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 62e1b2f494af376a4588bf3d365f7be5443d4a5d + 33e3c950af2eb996c0b3c48e30eb4471138da675 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 62e1b2f494af376a4588bf3d365f7be5443d4a5d + 33e3c950af2eb996c0b3c48e30eb4471138da675 https://github.com/dotnet/runtime @@ -129,9 +129,9 @@ https://github.com/dotnet/runtime 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - c24d9a9c91c5d04b7b4de71f1a9f33ac35e09663 + 163a63591cf9e9b682063cf3995948c2b885a042 https://github.com/dotnet/runtime @@ -177,9 +177,9 @@ https://github.com/dotnet/runtime 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 55fb7ef977e7d120dc12f0960edcff0739d7ee0e + 163a63591cf9e9b682063cf3995948c2b885a042 https://github.com/dotnet/runtime @@ -233,9 +233,9 @@ https://github.com/dotnet/runtime 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 7cca709db2944a09b4db6ca7b20c457ff260fb5a + 163a63591cf9e9b682063cf3995948c2b885a042 https://github.com/dotnet/runtime @@ -245,33 +245,33 @@ https://github.com/dotnet/runtime 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 55fb7ef977e7d120dc12f0960edcff0739d7ee0e + 163a63591cf9e9b682063cf3995948c2b885a042 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 55fb7ef977e7d120dc12f0960edcff0739d7ee0e + 163a63591cf9e9b682063cf3995948c2b885a042 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 55fb7ef977e7d120dc12f0960edcff0739d7ee0e + 163a63591cf9e9b682063cf3995948c2b885a042 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 55fb7ef977e7d120dc12f0960edcff0739d7ee0e + 163a63591cf9e9b682063cf3995948c2b885a042 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 55fb7ef977e7d120dc12f0960edcff0739d7ee0e + 163a63591cf9e9b682063cf3995948c2b885a042 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 55fb7ef977e7d120dc12f0960edcff0739d7ee0e + 163a63591cf9e9b682063cf3995948c2b885a042 diff --git a/eng/Versions.props b/eng/Versions.props index c2703b08cf0b..bc3e57278226 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -8,7 +8,7 @@ 6 0 - 8 + 9 true 6.0.0 - 6.0.8 - 6.0.8 - 6.0.8 - 6.0.8 - 6.0.8 - 6.0.8-servicing.22363.6 + 6.0.9 + 6.0.9 + 6.0.9 + 6.0.9 + 6.0.9 + 6.0.9-servicing.22419.5 6.0.0 6.0.1 6.0.0 @@ -91,7 +91,7 @@ 6.0.0 6.0.1 6.0.0 - 6.0.1 + 6.0.2 6.0.0 6.0.0 6.0.0 @@ -103,7 +103,7 @@ 6.0.0 6.0.0 6.0.0 - 6.0.8-servicing.22363.6 + 6.0.9-servicing.22419.5 6.0.0 6.0.0 6.0.1 @@ -117,19 +117,19 @@ 6.0.1 6.0.0 6.0.0 - 6.0.5 + 6.0.6 6.0.0 6.0.5 - 6.0.8 - 6.0.8 - 6.0.8 - 6.0.8 - 6.0.8 - 6.0.8 - 6.0.8 - 6.0.8 + 6.0.9 + 6.0.9 + 6.0.9 + 6.0.9 + 6.0.9 + 6.0.9 + 6.0.9 + 6.0.9 6.0.0-beta.22314.7 6.0.0-beta.22314.7 @@ -214,7 +214,7 @@ 2.1.1 2.2.0 - 3.1.27-servicing-22316-11 + 3.1.28-servicing-22364-2 $(MicrosoftAspNetCoreAzureAppServicesSiteExtension31Version) $(MicrosoftAspNetCoreAzureAppServicesSiteExtension31Version) 5.0.17-servicing-22215-7 diff --git a/eng/test-configuration.json b/eng/test-configuration.json index 6f8c5e36aaf1..02a918bc9f4d 100644 --- a/eng/test-configuration.json +++ b/eng/test-configuration.json @@ -1,6 +1,6 @@ { "version" : 1, - "defaultOnFailure": "fail", + "defaultOnFailure": "retry", "localRerunCount" : 3, "retryOnRules": [ {"testName": {"contains": "AppOfflineDroppedWhileSiteStarting_SiteShutsDown_InProcess"}}, @@ -15,4 +15,4 @@ ], "quarantineRules": [ ] -} \ No newline at end of file +} diff --git a/global.json b/global.json index bbd837de7707..fe757ec1255f 100644 --- a/global.json +++ b/global.json @@ -1,9 +1,9 @@ { "sdk": { - "version": "6.0.107" + "version": "6.0.108" }, "tools": { - "dotnet": "6.0.107", + "dotnet": "6.0.108", "runtimes": { "dotnet/x64": [ "2.1.30", @@ -13,7 +13,7 @@ "$(MicrosoftNETCoreBrowserDebugHostTransportVersion)" ], "aspnetcore/x64": [ - "3.1.27" + "3.1.28" ] }, "Git": "2.22.0", diff --git a/src/DefaultBuilder/src/WebApplication.cs b/src/DefaultBuilder/src/WebApplication.cs index 9d0a837c8300..2c1fb9d17925 100644 --- a/src/DefaultBuilder/src/WebApplication.cs +++ b/src/DefaultBuilder/src/WebApplication.cs @@ -27,7 +27,7 @@ public sealed class WebApplication : IHost, IApplicationBuilder, IEndpointRouteB internal WebApplication(IHost host) { _host = host; - ApplicationBuilder = new ApplicationBuilder(host.Services); + ApplicationBuilder = new ApplicationBuilder(host.Services, ServerFeatures); Logger = host.Services.GetRequiredService().CreateLogger(Environment.ApplicationName); Properties[GlobalEndpointRouteBuilderKey] = this; diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs index d487d2594939..7be67edb239f 100644 --- a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs +++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs @@ -31,6 +31,16 @@ namespace Microsoft.AspNetCore.Tests { public class WebApplicationTests { + [Fact] + public async Task WebApplicationBuilder_New() + { + var builder = WebApplication.CreateBuilder(new string[] { "--urls", "http://localhost:5001" }); + + await using var app = builder.Build(); + var newApp = (app as IApplicationBuilder).New(); + Assert.NotNull(newApp.ServerFeatures); + } + [Fact] public async Task WebApplicationBuilderConfiguration_IncludesCommandLineArguments() { @@ -1696,13 +1706,13 @@ public void EmptyAppConfiguration() createdDirectory = true; Directory.CreateDirectory(wwwroot); } - + try { var builder = WebApplication.CreateBuilder(); - + builder.WebHost.ConfigureAppConfiguration((ctx, config) => { }); - + using var app = builder.Build(); var hostEnv = app.Services.GetRequiredService(); Assert.Equal(wwwroot, hostEnv.WebRootPath); diff --git a/src/HttpClientFactory/Polly/src/PolicyHttpMessageHandler.cs b/src/HttpClientFactory/Polly/src/PolicyHttpMessageHandler.cs index c32c52fc2f1d..71bfe54ce0f7 100644 --- a/src/HttpClientFactory/Polly/src/PolicyHttpMessageHandler.cs +++ b/src/HttpClientFactory/Polly/src/PolicyHttpMessageHandler.cs @@ -167,7 +167,7 @@ protected virtual async Task SendCoreAsync(HttpRequestMessa disposable.Dispose(); } - var result = await base.SendAsync(request, cancellationToken); + var result = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); request.Properties[PriorResponseKey] = result; diff --git a/src/Installers/Windows/SharedFrameworkBundle/Bundle.wxs b/src/Installers/Windows/SharedFrameworkBundle/Bundle.wxs index 453548c27511..ca33112a07ce 100644 --- a/src/Installers/Windows/SharedFrameworkBundle/Bundle.wxs +++ b/src/Installers/Windows/SharedFrameworkBundle/Bundle.wxs @@ -4,6 +4,7 @@ diff --git a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelStateDictionary.cs b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelStateDictionary.cs index e72d6443c4e1..1ff9b7dd9b3c 100644 --- a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelStateDictionary.cs +++ b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelStateDictionary.cs @@ -25,6 +25,9 @@ public class ModelStateDictionary : IReadOnlyDictionary public static readonly int DefaultMaxAllowedErrors = 200; + // internal for testing + internal const int DefaultMaxRecursionDepth = 32; + private const char DelimiterDot = '.'; private const char DelimiterOpen = '['; @@ -43,8 +46,18 @@ public ModelStateDictionary() /// Initializes a new instance of the class. /// public ModelStateDictionary(int maxAllowedErrors) + : this(maxAllowedErrors, maxValidationDepth: DefaultMaxRecursionDepth, maxStateDepth: DefaultMaxRecursionDepth) + { + } + + /// + /// Initializes a new instance of the class. + /// + private ModelStateDictionary(int maxAllowedErrors, int maxValidationDepth, int maxStateDepth) { MaxAllowedErrors = maxAllowedErrors; + MaxValidationDepth = maxValidationDepth; + MaxStateDepth = maxStateDepth; var emptySegment = new StringSegment(buffer: string.Empty); _root = new ModelStateNode(subKey: emptySegment) { @@ -58,7 +71,9 @@ public ModelStateDictionary(int maxAllowedErrors) /// /// The to copy values from. public ModelStateDictionary(ModelStateDictionary dictionary) - : this(dictionary?.MaxAllowedErrors ?? DefaultMaxAllowedErrors) + : this(dictionary?.MaxAllowedErrors ?? DefaultMaxAllowedErrors, + dictionary?.MaxValidationDepth ?? DefaultMaxRecursionDepth, + dictionary?.MaxStateDepth ?? DefaultMaxRecursionDepth) { if (dictionary == null) { @@ -154,7 +169,7 @@ public bool IsValid } /// - public ModelValidationState ValidationState => GetValidity(_root) ?? ModelValidationState.Valid; + public ModelValidationState ValidationState => GetValidity(_root, currentDepth: 0) ?? ModelValidationState.Valid; /// public ModelStateEntry? this[string key] @@ -174,6 +189,10 @@ public ModelStateEntry? this[string key] // Flag that indicates if TooManyModelErrorException has already been added to this dictionary. private bool HasRecordedMaxModelError { get; set; } + internal int? MaxValidationDepth { get; set; } + + internal int? MaxStateDepth { get; set; } + /// /// Adds the specified to the instance /// that is associated with the specified . If the maximum number of allowed @@ -217,7 +236,6 @@ public bool TryAddModelException(string key, Exception exception) return false; } - ErrorCount++; AddModelErrorCore(key, exception); return true; } @@ -327,7 +345,6 @@ public bool TryAddModelError(string key, Exception exception, ModelMetadata meta return TryAddModelError(key, exception.Message); } - ErrorCount++; AddModelErrorCore(key, exception); return true; } @@ -385,13 +402,13 @@ public bool TryAddModelError(string key, string errorMessage) return false; } - ErrorCount++; var modelState = GetOrAddNode(key); Count += !modelState.IsContainerNode ? 0 : 1; modelState.ValidationState = ModelValidationState.Invalid; modelState.MarkNonContainerNode(); modelState.Errors.Add(errorMessage); + ErrorCount++; return true; } @@ -411,7 +428,7 @@ public ModelValidationState GetFieldValidationState(string key) } var item = GetNode(key); - return GetValidity(item) ?? ModelValidationState.Unvalidated; + return GetValidity(item, currentDepth: 0) ?? ModelValidationState.Unvalidated; } /// @@ -611,11 +628,18 @@ private ModelStateNode GetOrAddNode(string key) var current = _root; if (key.Length > 0) { + var currentDepth = 0; var match = default(MatchResult); do { + if (MaxStateDepth != null && currentDepth >= MaxStateDepth) + { + throw new InvalidOperationException(Resources.FormatModelStateDictionary_MaxModelStateDepth(MaxStateDepth)); + } + var subKey = FindNext(key, ref match); current = current.GetOrAddNode(subKey); + currentDepth++; } while (match.Type != Delimiter.None); @@ -661,9 +685,10 @@ private static StringSegment FindNext(string key, ref MatchResult currentMatch) return new StringSegment(key, keyStart, index - keyStart); } - private static ModelValidationState? GetValidity(ModelStateNode? node) + private ModelValidationState? GetValidity(ModelStateNode? node, int currentDepth) { - if (node == null) + if (node == null || + (MaxValidationDepth != null && currentDepth >= MaxValidationDepth)) { return null; } @@ -686,9 +711,11 @@ private static StringSegment FindNext(string key, ref MatchResult currentMatch) if (node.ChildNodes != null) { + currentDepth++; + for (var i = 0; i < node.ChildNodes.Count; i++) { - var entryState = GetValidity(node.ChildNodes[i]); + var entryState = GetValidity(node.ChildNodes[i], currentDepth); if (entryState == ModelValidationState.Unvalidated) { @@ -712,7 +739,6 @@ private void EnsureMaxErrorsReachedRecorded() var exception = new TooManyModelErrorsException(Resources.ModelStateDictionary_MaxModelStateErrors); AddModelErrorCore(string.Empty, exception); HasRecordedMaxModelError = true; - ErrorCount++; } } @@ -723,6 +749,8 @@ private void AddModelErrorCore(string key, Exception exception) modelState.ValidationState = ModelValidationState.Invalid; modelState.MarkNonContainerNode(); modelState.Errors.Add(exception); + + ErrorCount++; } /// diff --git a/src/Mvc/Mvc.Abstractions/src/Resources.resx b/src/Mvc/Mvc.Abstractions/src/Resources.resx index c4159218e799..5d38b2bdae14 100644 --- a/src/Mvc/Mvc.Abstractions/src/Resources.resx +++ b/src/Mvc/Mvc.Abstractions/src/Resources.resx @@ -180,4 +180,7 @@ Record type '{0}' has validation metadata defined on property '{1}' that will be ignored. '{1}' is a parameter in the record primary constructor and validation metadata must be associated with the constructor parameter. - + + The specified key exceeded the maximum ModelState depth: {0} + + \ No newline at end of file diff --git a/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelStateDictionaryTest.cs b/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelStateDictionaryTest.cs index 229457d71006..750ada389f97 100644 --- a/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelStateDictionaryTest.cs +++ b/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelStateDictionaryTest.cs @@ -1601,6 +1601,163 @@ public void GetModelStateForProperty_ReturnsModelStateForIndexedChildren() Assert.Equal("value1", property.RawValue); } + + [Fact] + public void GetFieldValidationState_ReturnsUnvalidated_IfTreeHeightIsGreaterThanLimit() + { + // Arrange + var stackLimit = 5; + var dictionary = new ModelStateDictionary(); + var key = string.Join(".", Enumerable.Repeat("foo", stackLimit + 1)); + dictionary.MaxValidationDepth = stackLimit; + dictionary.MaxStateDepth = null; + dictionary.MarkFieldValid(key); + + // Act + var validationState = dictionary.GetFieldValidationState("foo"); + + // Assert + Assert.Equal(ModelValidationState.Unvalidated, validationState); + } + + [Fact] + public void IsValidProperty_ReturnsTrue_IfTreeHeightIsGreaterThanLimit() + { + // Arrange + var stackLimit = 5; + var dictionary = new ModelStateDictionary(); + var key = string.Join(".", Enumerable.Repeat("foo", stackLimit + 1)); + dictionary.MaxValidationDepth = stackLimit; + dictionary.MaxStateDepth = null; + dictionary.AddModelError(key, "some error"); + + // Act + var isValid = dictionary.IsValid; + var validationState = dictionary.ValidationState; + + // Assert + Assert.True(isValid); + Assert.Equal(ModelValidationState.Valid, validationState); + } + + [Fact] + public void TryAddModelException_Throws_IfKeyHasTooManySegments() + { + // Arrange + var exception = new TestException(); + + var stateDepth = 5; + var dictionary = new ModelStateDictionary(); + var key = string.Join(".", Enumerable.Repeat("foo", stateDepth + 1)); + dictionary.MaxStateDepth = stateDepth; + + // Act + var invalidException = Assert.Throws(() => dictionary.TryAddModelException(key, exception)); + + // Assert + Assert.Equal( + $"The specified key exceeded the maximum ModelState depth: {dictionary.MaxStateDepth}", + invalidException.Message); + } + + [Fact] + public void TryAddModelError_Throws_IfKeyHasTooManySegments() + { + // Arrange + var stateDepth = 5; + var dictionary = new ModelStateDictionary(); + var key = string.Join(".", Enumerable.Repeat("foo", stateDepth + 1)); + dictionary.MaxStateDepth = stateDepth; + + // Act + var invalidException = Assert.Throws(() => dictionary.TryAddModelError(key, "errorMessage")); + + // Assert + Assert.Equal( + $"The specified key exceeded the maximum ModelState depth: {dictionary.MaxStateDepth}", + invalidException.Message); + } + + [Fact] + public void SetModelValue_Throws_IfKeyHasTooManySegments() + { + var stateDepth = 5; + var dictionary = new ModelStateDictionary(); + var key = string.Join(".", Enumerable.Repeat("foo", stateDepth + 1)); + dictionary.MaxStateDepth = stateDepth; + + // Act + var invalidException = Assert.Throws(() => dictionary.SetModelValue(key, string.Empty, string.Empty)); + + // Assert + Assert.Equal( + $"The specified key exceeded the maximum ModelState depth: {dictionary.MaxStateDepth}", + invalidException.Message); + } + + [Fact] + public void MarkFieldValid_Throws_IfKeyHasTooManySegments() + { + // Arrange + var stateDepth = 5; + var source = new ModelStateDictionary(); + var key = string.Join(".", Enumerable.Repeat("foo", stateDepth + 1)); + source.MaxStateDepth = stateDepth; + + // Act + var exception = Assert.Throws(() => source.MarkFieldValid(key)); + + // Assert + Assert.Equal( + $"The specified key exceeded the maximum ModelState depth: {source.MaxStateDepth}", + exception.Message); + } + + [Fact] + public void MarkFieldSkipped_Throws_IfKeyHasTooManySegments() + { + // Arrange + var stateDepth = 5; + var source = new ModelStateDictionary(); + var key = string.Join(".", Enumerable.Repeat("foo", stateDepth + 1)); + source.MaxStateDepth = stateDepth; + + // Act + var exception = Assert.Throws(() => source.MarkFieldSkipped(key)); + + // Assert + Assert.Equal( + $"The specified key exceeded the maximum ModelState depth: {source.MaxStateDepth}", + exception.Message); + } + + [Fact] + public void Constructor_SetsDefaultRecursionDepth() + { + // Arrange && Act + var dictionary = new ModelStateDictionary(); + + // Assert + Assert.Equal(ModelStateDictionary.DefaultMaxRecursionDepth, dictionary.MaxValidationDepth); + Assert.Equal(ModelStateDictionary.DefaultMaxRecursionDepth, dictionary.MaxStateDepth); + } + + [Fact] + public void CopyConstructor_PreservesRecursionDepth() + { + // Arrange + var dictionary = new ModelStateDictionary(); + dictionary.MaxValidationDepth = 5; + dictionary.MaxStateDepth = 4; + + // Act + var newDictionary = new ModelStateDictionary(dictionary); + + // Assert + Assert.Equal(dictionary.MaxValidationDepth, newDictionary.MaxValidationDepth); + Assert.Equal(dictionary.MaxStateDepth, newDictionary.MaxStateDepth); + } + private class OptionsAccessor : IOptions { public MvcOptions Value { get; } = new MvcOptions(); diff --git a/src/Mvc/Mvc.Core/src/Infrastructure/ControllerActionInvokerProvider.cs b/src/Mvc/Mvc.Core/src/Infrastructure/ControllerActionInvokerProvider.cs index dd576bf2c8ad..c1a8f07877f8 100644 --- a/src/Mvc/Mvc.Core/src/Infrastructure/ControllerActionInvokerProvider.cs +++ b/src/Mvc/Mvc.Core/src/Infrastructure/ControllerActionInvokerProvider.cs @@ -20,6 +20,8 @@ internal class ControllerActionInvokerProvider : IActionInvokerProvider private readonly ControllerActionInvokerCache _controllerActionInvokerCache; private readonly IReadOnlyList _valueProviderFactories; private readonly int _maxModelValidationErrors; + private readonly int? _maxValidationDepth; + private readonly int _maxModelBindingRecursionDepth; private readonly ILogger _logger; private readonly DiagnosticListener _diagnosticListener; private readonly IActionResultTypeMapper _mapper; @@ -46,6 +48,8 @@ public ControllerActionInvokerProvider( _controllerActionInvokerCache = controllerActionInvokerCache; _valueProviderFactories = optionsAccessor.Value.ValueProviderFactories.ToArray(); _maxModelValidationErrors = optionsAccessor.Value.MaxModelValidationErrors; + _maxValidationDepth = optionsAccessor.Value.MaxValidationDepth; + _maxModelBindingRecursionDepth = optionsAccessor.Value.MaxModelBindingRecursionDepth; _logger = loggerFactory.CreateLogger(); _diagnosticListener = diagnosticListener; _mapper = mapper; @@ -70,6 +74,8 @@ public void OnProvidersExecuting(ActionInvokerProviderContext context) ValueProviderFactories = new CopyOnWriteList(_valueProviderFactories) }; controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors; + controllerContext.ModelState.MaxValidationDepth = _maxValidationDepth; + controllerContext.ModelState.MaxStateDepth = _maxModelBindingRecursionDepth; var (cacheEntry, filters) = _controllerActionInvokerCache.GetCachedResult(controllerContext); diff --git a/src/Mvc/Mvc.Core/src/Routing/ControllerRequestDelegateFactory.cs b/src/Mvc/Mvc.Core/src/Routing/ControllerRequestDelegateFactory.cs index 74d84f8541e4..d62237dbfb92 100644 --- a/src/Mvc/Mvc.Core/src/Routing/ControllerRequestDelegateFactory.cs +++ b/src/Mvc/Mvc.Core/src/Routing/ControllerRequestDelegateFactory.cs @@ -21,6 +21,8 @@ internal class ControllerRequestDelegateFactory : IRequestDelegateFactory private readonly ControllerActionInvokerCache _controllerActionInvokerCache; private readonly IReadOnlyList _valueProviderFactories; private readonly int _maxModelValidationErrors; + private readonly int? _maxValidationDepth; + private readonly int _maxModelBindingRecursionDepth; private readonly ILogger _logger; private readonly DiagnosticListener _diagnosticListener; private readonly IActionResultTypeMapper _mapper; @@ -48,6 +50,8 @@ public ControllerRequestDelegateFactory( _controllerActionInvokerCache = controllerActionInvokerCache; _valueProviderFactories = optionsAccessor.Value.ValueProviderFactories.ToArray(); _maxModelValidationErrors = optionsAccessor.Value.MaxModelValidationErrors; + _maxValidationDepth = optionsAccessor.Value.MaxValidationDepth; + _maxModelBindingRecursionDepth = optionsAccessor.Value.MaxModelBindingRecursionDepth; _enableActionInvokers = optionsAccessor.Value.EnableActionInvokers; _logger = loggerFactory.CreateLogger(); _diagnosticListener = diagnosticListener; @@ -86,6 +90,8 @@ public ControllerRequestDelegateFactory( }; controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors; + controllerContext.ModelState.MaxValidationDepth = _maxValidationDepth; + controllerContext.ModelState.MaxStateDepth = _maxModelBindingRecursionDepth; var (cacheEntry, filters) = _controllerActionInvokerCache.GetCachedResult(controllerContext); diff --git a/src/Mvc/Mvc.Core/test/Infrastructure/ControllerActionInvokerProviderTest.cs b/src/Mvc/Mvc.Core/test/Infrastructure/ControllerActionInvokerProviderTest.cs new file mode 100644 index 000000000000..c278f8ba7f24 --- /dev/null +++ b/src/Mvc/Mvc.Core/test/Infrastructure/ControllerActionInvokerProviderTest.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Reflection; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Moq; + +namespace Microsoft.AspNetCore.Mvc.Infrastructure +{ + public class ControllerActionInvokerProviderTest + { + [Fact] + public void OnExecuting_ConfiguresModelState_WithMvcOptions() + { + // Arrange + var provider = CreateInvokerProvider(new MvcOptions() { MaxValidationDepth = 1, MaxModelBindingRecursionDepth = 2, MaxModelValidationErrors = 3 }); + + var context = new ActionInvokerProviderContext(new ActionContext() + { + ActionDescriptor = GetControllerActionDescriptor(), + HttpContext = new DefaultHttpContext(), + RouteData = new RouteData(), + }); + + // Act + provider.OnProvidersExecuting(context); + + // Assert + var invoker = Assert.IsType(context.Result); + Assert.Equal(1, invoker.ControllerContext.ModelState.MaxValidationDepth); + Assert.Equal(2, invoker.ControllerContext.ModelState.MaxStateDepth); + Assert.Equal(3, invoker.ControllerContext.ModelState.MaxAllowedErrors); + + } + + private static ControllerActionDescriptor GetControllerActionDescriptor() + { + var method = typeof(TestActions).GetMethod(nameof(TestActions.GetAction)); + var actionDescriptor = new ControllerActionDescriptor + { + MethodInfo = method, + FilterDescriptors = new List(), + ControllerTypeInfo = typeof(TestActions).GetTypeInfo(), + }; + + foreach (var filterAttribute in method.GetCustomAttributes().OfType()) + { + actionDescriptor.FilterDescriptors.Add(new FilterDescriptor(filterAttribute, FilterScope.Action)); + } + + return actionDescriptor; + } + + private static ControllerActionInvokerProvider CreateInvokerProvider(MvcOptions mvcOptions = null) + { + var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var modelBinderFactory = TestModelBinderFactory.CreateDefault(); + mvcOptions ??= new MvcOptions(); + + var parameterBinder = new ParameterBinder( + modelMetadataProvider, + TestModelBinderFactory.CreateDefault(), + Mock.Of(), + Options.Create(mvcOptions), + NullLoggerFactory.Instance); + + var cache = new ControllerActionInvokerCache( + parameterBinder, + modelBinderFactory, + modelMetadataProvider, + new[] { new DefaultFilterProvider() }, + Mock.Of(), + Options.Create(mvcOptions)); + + return new( + cache, + Options.Create(mvcOptions), + NullLoggerFactory.Instance, + new DiagnosticListener("Microsoft.AspNetCore"), + new ActionResultTypeMapper()); + } + + private class TestActions : Controller + { + public IActionResult GetAction() => new OkResult(); + } + } +} diff --git a/src/Shared/CertificateGeneration/MacOSCertificateManager.cs b/src/Shared/CertificateGeneration/MacOSCertificateManager.cs index c58eff3e0382..a63aeb50ed82 100644 --- a/src/Shared/CertificateGeneration/MacOSCertificateManager.cs +++ b/src/Shared/CertificateGeneration/MacOSCertificateManager.cs @@ -19,17 +19,17 @@ internal class MacOSCertificateManager : CertificateManager private static readonly string MacOSUserKeyChain = Environment.GetEnvironmentVariable("HOME") + "/Library/Keychains/login.keychain-db"; private const string MacOSSystemKeyChain = "/Library/Keychains/System.keychain"; private const string MacOSFindCertificateCommandLine = "security"; - private const string MacOSFindCertificateCommandLineArgumentsFormat = "find-certificate -c {0} -a -Z -p " + MacOSSystemKeyChain; + private const string MacOSFindCertificateCommandLineArgumentsFormat = "find-certificate -c {0} -a -Z -p \"" + MacOSSystemKeyChain + "\""; private const string MacOSFindCertificateOutputRegex = "SHA-1 hash: ([0-9A-Z]+)"; private const string MacOSRemoveCertificateTrustCommandLine = "sudo"; - private const string MacOSRemoveCertificateTrustCommandLineArgumentsFormat = "security remove-trusted-cert -d {0}"; + private const string MacOSRemoveCertificateTrustCommandLineArgumentsFormat = "security remove-trusted-cert -d \"{0}\""; private const string MacOSDeleteCertificateCommandLine = "sudo"; - private const string MacOSDeleteCertificateCommandLineArgumentsFormat = "security delete-certificate -Z {0} {1}"; + private const string MacOSDeleteCertificateCommandLineArgumentsFormat = "security delete-certificate -Z {0} \"{1}\""; private const string MacOSTrustCertificateCommandLine = "sudo"; - private const string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k " + MacOSSystemKeyChain + " "; + private const string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k \"" + MacOSSystemKeyChain + "\" "; private const string MacOSAddCertificateToKeyChainCommandLine = "security"; - private static readonly string MacOSAddCertificateToKeyChainCommandLineArgumentsFormat = "import {0} -k " + MacOSUserKeyChain + " -t cert -f pkcs12 -P {1} -A"; + private static readonly string MacOSAddCertificateToKeyChainCommandLineArgumentsFormat = "import \"{0}\" -k \"" + MacOSUserKeyChain + "\" -t cert -f pkcs12 -P {1} -A"; public const string InvalidCertificateState = "The ASP.NET Core developer certificate is in an invalid state. " + "To fix this issue, run the following commands 'dotnet dev-certs https --clean' and 'dotnet dev-certs https' to remove all existing ASP.NET Core development certificates " + diff --git a/src/submodules/googletest b/src/submodules/googletest index 96f51426e4c7..dd7a9d29a33d 160000 --- a/src/submodules/googletest +++ b/src/submodules/googletest @@ -1 +1 @@ -Subproject commit 96f51426e4c776a71d0a446c1e4f4c7a5ea921df +Subproject commit dd7a9d29a33de34836c345c3b753d4eba15c5f44 diff --git a/src/submodules/spa-templates b/src/submodules/spa-templates index 74e05a19dc83..0c5a96babb59 160000 --- a/src/submodules/spa-templates +++ b/src/submodules/spa-templates @@ -1 +1 @@ -Subproject commit 74e05a19dc83b736877cb2c5120456948d7799f1 +Subproject commit 0c5a96babb595838516b43680db6f100c8867a7c