/* Copyright Phil Haack * * Licensed under the MIT License: https://github.com/Haacked/aspnetmvc-action-checker/blob/master/LICENSE */ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Web.Mvc; public class SystemController : Controller { protected override void OnActionExecuting(ActionExecutingContext filterContext) { if (!ControllerContext.HttpContext.Request.IsLocal) { filterContext.HttpContext.Response.StatusCode = 404; filterContext.HttpContext.Response.Flush(); filterContext.HttpContext.Response.End(); return; } base.OnActionExecuting(filterContext); } public ActionResult Index(string ignore) { ignore = ignore ?? ""; if (!ControllerContext.HttpContext.Request.IsLocal) return HttpNotFound(); var assembly = Assembly.GetExecutingAssembly(); var controllers = assembly.GetTypes() .Where(type => typeof(Controller).IsAssignableFrom(type)) //filter controllers .Select(type => new ReflectedControllerDescriptor(type)); var controllerIssues = new Dictionary>>(); foreach (var controller in controllers) { var actions = controller.GetCanonicalActions(); foreach(var action in actions) { var attributes = action.GetCustomAttributes(true); if (!ignore.Contains("antiforgery")) { CheckAction(() => (ContainsHttpMutateAttribute(attributes) && !ContainsAttribute(attributes)) , controller , action , "HTTP attribute that could mutate a resource does not have a [ValidateAntiForgeryToken] attribute applied." , controllerIssues); } if (!ignore.Contains("authorization")) { CheckAction(() => (ContainsHttpMutateAttribute(attributes) && !ContainsAttribute(attributes) && !ContainsAttribute(controller.GetCustomAttributes(true))) , controller , action , "HTTP attribute that could mutate a resource does not have an [Authorize] attribute applied. You may also want to apply the attribute to the GET action (if any) that corresponds to this action." , controllerIssues); } } } var response = new StringBuilder(); response.Append(""); response.Append("System Check"); response.Append(""); response.Append(""); response.Append(""); response.Append("

System Check: Potential Issues Found

"); response.Append($"

Reflecting over controllers and actions in the assembly {assembly.FullName} found the following potential issues. Note that some of these issues may be by design. For example, you probably DO NOT want an Authorize attribute on a Login action.

"); response.Append(@"

To ignore antiforgery issues click here.

"); response.Append(@"

To ignore authorization issues click here

"); response.Append(@"

To clear the ignore filters click here

"); foreach (var controller in controllerIssues.Keys) { response.Append($"

{controller}Controller

"); foreach (var action in controllerIssues[controller]) { response.Append($"

{action.Key}

"); response.Append("
    "); foreach (var issue in action.Value) { response.Append($"
  • {issue}
  • "); } response.Append("
"); } } response.Append(""); response.Append(""); return Content(response.ToString()); } static bool ContainsAttribute(object[] attributes) where T : Attribute { return attributes.Any(attr => attr as T != null); } static bool ContainsHttpMutateAttribute(object[] attributes) { return ContainsAttribute(attributes) || ContainsAttribute(attributes) || ContainsAttribute(attributes) || ContainsAttribute(attributes); } static void CheckAction(Func check, ControllerDescriptor controller, ActionDescriptor action, string checkTrueMessage, Dictionary>> controllerIssues) { if (check != null && check()) { if (!controllerIssues.ContainsKey(controller.ControllerName)) { controllerIssues[controller.ControllerName] = new Dictionary>(); } var actionDictionary = controllerIssues[controller.ControllerName]; if (!actionDictionary.ContainsKey(action.ActionName)) { actionDictionary[action.ActionName] = new List(); } actionDictionary[action.ActionName].Add(checkTrueMessage); } } }