Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Limit the maximum number of Model errors to a reasonable value.
Browse files Browse the repository at this point in the history
Fixes #1042
  • Loading branch information
pranavkm committed Aug 28, 2014
1 parent ddea73b commit 6945a18
Show file tree
Hide file tree
Showing 17 changed files with 410 additions and 36 deletions.
19 changes: 19 additions & 0 deletions src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class MvcOptions
{
private AntiForgeryOptions _antiForgeryOptions = new AntiForgeryOptions();
private RazorViewEngineOptions _viewEngineOptions = new RazorViewEngineOptions();
private int _maxModelStateErrors = 200;

public MvcOptions()
{
Expand Down Expand Up @@ -85,6 +86,24 @@ public RazorViewEngineOptions ViewEngineOptions
}
}

/// <summary>
/// Gets or sets the maximum number of validation errors that are allowed by this application before further
/// errors are ignored.
/// </summary>
public int MaxModelValidationErrors
{
get { return _maxModelStateErrors; }
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("MaxModelValidationErrors");
}

_maxModelStateErrors = value;
}
}

/// <summary>
/// Get a list of the <see cref="ModelBinderDescriptor" /> used by the
/// <see cref="ModelBinding.CompositeModelBinder" />.
Expand Down
18 changes: 11 additions & 7 deletions src/Microsoft.AspNet.Mvc.Core/MvcRouteHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel;

namespace Microsoft.AspNet.Mvc
{
Expand All @@ -32,7 +33,7 @@ public string GetVirtualPath([NotNull] VirtualPathContext context)
public async Task RouteAsync([NotNull] RouteContext context)
{
var services = context.HttpContext.RequestServices;

// Verify if AddMvc was done before calling UseMvc
// We use the MvcMarkerService to make sure if all the services were added.
MvcServicesHelper.ThrowIfMvcNotRegistered(services);
Expand Down Expand Up @@ -64,19 +65,22 @@ public async Task RouteAsync([NotNull] RouteContext context)
return;
}

if (actionDescriptor.RouteValueDefaults != null)
{
foreach (var kvp in actionDescriptor.RouteValueDefaults)
if (actionDescriptor.RouteValueDefaults != null)
{
if (!context.RouteData.Values.ContainsKey(kvp.Key))
foreach (var kvp in actionDescriptor.RouteValueDefaults)
{
context.RouteData.Values.Add(kvp.Key, kvp.Value);
if (!context.RouteData.Values.ContainsKey(kvp.Key))
{
context.RouteData.Values.Add(kvp.Key, kvp.Value);
}
}
}
}

var actionContext = new ActionContext(context.HttpContext, context.RouteData, actionDescriptor);

var optionsAccessor = services.GetService<IOptionsAccessor<MvcOptions>>();
actionContext.ModelState.MaxAllowedErrors = optionsAccessor.Options.MaxModelValidationErrors;

var contextAccessor = services.GetService<IContextAccessor<ActionContext>>();
using (contextAccessor.SetContextSource(() => actionContext, PreventExchange))
{
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ public virtual async Task<bool> BindModelAsync([NotNull] ModelBindingContext bin

// Only perform validation at the root of the object graph. ValidationNode will recursively walk the graph.
// Ignore ComplexModelDto since it essentially wraps the primary object.
if (newBindingContext.ModelMetadata.ContainerType == null &&
newBindingContext.ModelMetadata.ModelType != typeof(ComplexModelDto))
if (IsBindingAtRootOfObjectGraph(newBindingContext))
{
// run validation and return the model
// If we fell back to an empty prefix above and are dealing with simple types,
Expand All @@ -85,31 +84,57 @@ public virtual async Task<bool> BindModelAsync([NotNull] ModelBindingContext bin
bindingContext.ModelMetadata,
containerMetadata: null);

newBindingContext.ValidationNode.Validate(validationContext, parentNode: null);
try
{
newBindingContext.ValidationNode.Validate(validationContext, parentNode: null);
}
catch (TooManyModelErrorsException)
{
}
}

bindingContext.Model = newBindingContext.Model;
return true;
}

private async Task<bool> TryBind([NotNull] ModelBindingContext bindingContext)
private async Task<bool> TryBind(ModelBindingContext bindingContext)
{
// TODO: RuntimeHelpers.EnsureSufficientExecutionStack does not exist in the CoreCLR.
// Protects against stack overflow for deeply nested model binding
// RuntimeHelpers.EnsureSufficientExecutionStack();

foreach (var binder in ModelBinders)
{
if (await binder.BindModelAsync(bindingContext))
try
{
if (await binder.BindModelAsync(bindingContext))
{
return true;
}
}
catch (TooManyModelErrorsException)
{
return true;
if (!IsBindingAtRootOfObjectGraph(bindingContext))
{
throw;
}
}
}

// Either we couldn't find a binder, or the binder couldn't bind. Distinction is not important.
return false;
}

private static bool IsBindingAtRootOfObjectGraph(ModelBindingContext bindingContext)
{
// We're at the root of the object graph if the model does does not have a container.
// This statement is true for complex types at the root twice over - once with the actual model
// and once when when it is represented by a ComplexModelDto. Ignore the latter case.

return bindingContext.ModelMetadata.ContainerType == null &&
bindingContext.ModelMetadata.ModelType != typeof(ComplexModelDto);
}

private static ModelBindingContext CreateNewBindingContext(ModelBindingContext oldBindingContext,
string modelName,
bool reuseValidationNode)
Expand Down
11 changes: 1 addition & 10 deletions src/Microsoft.AspNet.Mvc.ModelBinding/ModelBindingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public class ModelBindingContext
{
private string _modelName;
private ModelStateDictionary _modelState;
private Dictionary<string, ModelMetadata> _propertyMetadata;
private ModelValidationNode _validationNode;

Expand Down Expand Up @@ -94,15 +93,7 @@ public string ModelName
/// </summary>
public ModelStateDictionary ModelState
{
get
{
if (_modelState == null)
{
_modelState = new ModelStateDictionary();
}
return _modelState;
}
set { _modelState = value; }
get; set;
}

/// <summary>
Expand Down
Loading

0 comments on commit 6945a18

Please sign in to comment.