diff --git a/docs/Analyzer Configuration.md b/docs/Analyzer Configuration.md index 80f2a4be04..4338b6c4e2 100644 --- a/docs/Analyzer Configuration.md +++ b/docs/Analyzer Configuration.md @@ -213,6 +213,28 @@ Examples: |`dotnet_code_quality.excluded_type_names_with_derived_types = MyType1\|MyType2` | Matches all types named either 'MyType1' or 'MyType2' and all of their derived types in the compilation |`dotnet_code_quality.excluded_type_names_with_derived_types = M:NS.MyType` | Matches specific type 'MyType' with given fully qualified name and all of its derived types |`dotnet_code_quality.excluded_type_names_with_derived_types = M:NS1.MyType1\|M:NS2.MyType2` | Matches specific types 'MyType1' and 'MyType2' with respective fully qualified names and all of their derived types + +### Unsafe DllImportSearchPath bits when using DefaultDllImportSearchPaths attribute +Option Name: `unsafe_DllImportSearchPath_bits` + +Configurable Rules: CA5393 + +Option Values: Integer values of System.Runtime.InteropServices.DllImportSearchPath + +Default Value: '770', which is AssemblyDirectory | UseDllDirectoryForDependencies | ApplicationDirectory + +Example: `dotnet_code_quality.CA5393.unsafe_DllImportSearchPath_bits = 770` + +### Exclude ASP.NET Core MVC ControllerBase when considering CSRF +Option Name: `exclude_aspnet_core_mvc_controllerbase` + +Configurable Rules: CA5391 + +Option Values: Boolean values + +Default Value: `true` + +Example: `dotnet_code_quality.CA5391.exclude_aspnet_core_mvc_controllerbase = false` ### Disallowed symbol names Option Name: `disallowed_symbol_names` @@ -370,12 +392,3 @@ Option Values: integral values Default Value: Specific to each configurable rule ('100000' by default for most rules) Example: `dotnet_code_quality.CA5387.sufficient_IterationCount_for_weak_KDF_algorithm = 100000` - -#### Configure unsafe DllImportSearchPath bits when using DefaultDllImportSearchPaths attribute -Option Name: `unsafe_DllImportSearchPath_bits` - -Option Values: Integer values of System.Runtime.InteropServices.DllImportSearchPath - -Default Value: Specific to each configurable rule ('770', which is AssemblyDirectory | UseDllDirectoryForDependencies | ApplicationDirectory, by default for most rules) - -Example: `dotnet_code_quality.CA5392.unsafe_DllImportSearchPath_bits = 770` diff --git a/src/Microsoft.NetCore.Analyzers/Core/Security/UseAutoValidateAntiforgeryToken.cs b/src/Microsoft.NetCore.Analyzers/Core/Security/UseAutoValidateAntiforgeryToken.cs index fa7d51c1f0..69a6c65b9a 100644 --- a/src/Microsoft.NetCore.Analyzers/Core/Security/UseAutoValidateAntiforgeryToken.cs +++ b/src/Microsoft.NetCore.Analyzers/Core/Security/UseAutoValidateAntiforgeryToken.cs @@ -25,7 +25,7 @@ public sealed class UseAutoValidateAntiforgeryToken : DiagnosticAnalyzer typeof(MicrosoftNetCoreAnalyzersResources), nameof(MicrosoftNetCoreAnalyzersResources.UseAutoValidateAntiforgeryToken), nameof(MicrosoftNetCoreAnalyzersResources.UseAutoValidateAntiforgeryTokenMessage), - DiagnosticHelpers.EnabledByDefaultIfNotBuildingVSIX, + false, helpLinkUri: null, descriptionResourceStringName: nameof(MicrosoftNetCoreAnalyzersResources.UseAutoValidateAntiforgeryTokenDescription), customTags: WellKnownDiagnosticTags.Telemetry); @@ -49,12 +49,13 @@ public sealed class UseAutoValidateAntiforgeryToken : DiagnosticAnalyzer WellKnownTypeNames.MicrosoftAspNetCoreMvcHttpDeleteAttribute, WellKnownTypeNames.MicrosoftAspNetCoreMvcHttpPatchAttribute); + // It is used to translate ConcurrentDictionary into ConcurrentHashset, which is not provided. + private const bool placeholder = true; + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create( UseAutoValidateAntiforgeryTokenRule, MissHttpVerbAttributeRule); - public delegate bool RequirementsOfValidateMethod(IMethodSymbol methodSymbol); - public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); @@ -90,8 +91,14 @@ public override void Initialize(AnalysisContext context) return; } + var cancellationToken = compilationStartAnalysisContext.CancellationToken; + var onlyLookAtDerivedClassesOfController = compilationStartAnalysisContext.Options.GetBoolOptionValue( + optionName: EditorConfigOptionNames.ExcludeAspnetCoreMvcControllerBase, + rule: UseAutoValidateAntiforgeryTokenRule, + defaultValue: true, + cancellationToken: cancellationToken); + // A dictionary from method symbol to set of methods calling it directly. - // The bool value in the sub ConcurrentDictionary is not used, use ConcurrentDictionary rather than HashSet just for the concurrency security. var inverseGraph = new ConcurrentDictionary>(); // Ignore cases where a global anti forgery filter is in use. @@ -100,9 +107,9 @@ public override void Initialize(AnalysisContext context) // Verify that validate anti forgery token attributes are used somewhere within this project, // to avoid reporting false positives on projects that use an alternative approach to mitigate CSRF issues. var usingValidateAntiForgeryAttribute = false; - var onAuthorizationAsyncMethodSymbols = new HashSet(); - var actionMethodSymbols = new HashSet<(IMethodSymbol, string)>(); - var actionMethodNeedAddingHttpVerbAttributeSymbols = new HashSet(); + var onAuthorizationAsyncMethodSymbols = new ConcurrentDictionary(); + var actionMethodSymbols = new ConcurrentDictionary<(IMethodSymbol, string), bool>(); + var actionMethodNeedAddingHttpVerbAttributeSymbols = new ConcurrentDictionary(); // Constructing inverse callGraph. // When it comes to delegate function assignment Del handler = DelegateMethod;, inverse call Graph will add: @@ -152,7 +159,7 @@ public override void Initialize(AnalysisContext context) } callers = inverseGraph.GetOrAdd(calledSymbol, (_) => new ConcurrentDictionary()); - callers.TryAdd(owningSymbol, true); + callers.TryAdd(owningSymbol, placeholder); }, OperationKind.Invocation, OperationKind.FieldReference); }); @@ -190,16 +197,18 @@ public override void Initialize(AnalysisContext context) else if (potentialAntiForgeryFilter.AllInterfaces.Contains(iAsyncAuthorizationFilterTypeSymbol) || potentialAntiForgeryFilter.AllInterfaces.Contains(iAuthorizationFilterTypeSymbol)) { - onAuthorizationAsyncMethodSymbols.Add( + onAuthorizationAsyncMethodSymbols.TryAdd( potentialAntiForgeryFilter .GetMembers() .OfType() .FirstOrDefault( - s => (s.Name == "OnAuthorizationAsync" || - s.Name == "OnAuthorization") && - s.ReturnType.Equals(taskTypeSymbol) && + s => (s.Name == "OnAuthorizationAsync" && + s.ReturnType.Equals(taskTypeSymbol) || + s.Name == "OnAuthorization" && + s.ReturnsVoid) && s.Parameters.Length == 1 && - s.Parameters[0].Type.Equals(authorizationFilterContextTypeSymbol))); + s.Parameters[0].Type.Equals(authorizationFilterContextTypeSymbol)), + placeholder); } } } @@ -215,9 +224,10 @@ public override void Initialize(AnalysisContext context) var derivedControllerTypeSymbol = (INamedTypeSymbol)symbolAnalysisContext.Symbol; var baseTypes = derivedControllerTypeSymbol.GetBaseTypes(); - // An subtype of `Microsoft.AspNetCore.Mvc.Controller` or `Microsoft.AspNetCore.Mvc.ControllerBase`). + // An subtype of `Microsoft.AspNetCore.Mvc.Controller`, which probably indicates views are used and maybe cookie-based authentication is used and thus CSRF is a concern. if (baseTypes.Contains(controllerTypeSymbol) || - baseTypes.Contains(controllerBaseTypeSymbol)) + (!onlyLookAtDerivedClassesOfController && + baseTypes.Contains(controllerBaseTypeSymbol))) { // The controller class is not protected by a validate anti forgery token attribute. if (!IsUsingAntiFogeryAttribute(derivedControllerTypeSymbol)) @@ -259,13 +269,14 @@ public override void Initialize(AnalysisContext context) if (httpVerbAttributeTypeSymbolAbleToModify != null) { var attributeName = httpVerbAttributeTypeSymbolAbleToModify.AttributeClass.Name; - actionMethodSymbols.Add( + actionMethodSymbols.TryAdd( (actionMethodSymbol, - attributeName.EndsWith("Attribute", StringComparison.Ordinal) ? attributeName.Remove(attributeName.Length - "Attribute".Length) : attributeName)); + attributeName.EndsWith("Attribute", StringComparison.Ordinal) ? attributeName.Remove(attributeName.Length - "Attribute".Length) : attributeName), + placeholder); } else if (!actionMethodSymbol.GetAttributes().Any(s => s.AttributeClass.GetBaseTypes().Contains(httpMethodAttributeTypeSymbol))) { - actionMethodNeedAddingHttpVerbAttributeSymbols.Add((actionMethodSymbol)); + actionMethodNeedAddingHttpVerbAttributeSymbols.TryAdd(actionMethodSymbol, placeholder); } } } @@ -300,7 +311,7 @@ public override void Initialize(AnalysisContext context) } } - foreach (var (methodSymbol, attributeName) in actionMethodSymbols) + foreach (var (methodSymbol, attributeName) in actionMethodSymbols.Keys) { compilationAnalysisContext.ReportDiagnostic( methodSymbol.CreateDiagnostic( @@ -309,7 +320,7 @@ public override void Initialize(AnalysisContext context) attributeName)); } - foreach (var methodSymbol in actionMethodNeedAddingHttpVerbAttributeSymbols) + foreach (var methodSymbol in actionMethodNeedAddingHttpVerbAttributeSymbols.Keys) { compilationAnalysisContext.ReportDiagnostic( methodSymbol.CreateDiagnostic( @@ -340,7 +351,8 @@ void FindAllTheSpecifiedCalleeMethods(ISymbol methodSymbol, HashSet vis foreach (var child in callingMethods.Keys) { - if (onAuthorizationAsyncMethodSymbols.Contains(child)) + if (child is IMethodSymbol childMethodSymbol && + onAuthorizationAsyncMethodSymbols.ContainsKey(childMethodSymbol)) { results[methodSymbol].Add(child); } diff --git a/src/Microsoft.NetCore.Analyzers/UnitTests/Security/UseAutoValidateAntiforgeryTokenTests.cs b/src/Microsoft.NetCore.Analyzers/UnitTests/Security/UseAutoValidateAntiforgeryTokenTests.cs index eb1a7b4d39..33be2768be 100644 --- a/src/Microsoft.NetCore.Analyzers/UnitTests/Security/UseAutoValidateAntiforgeryTokenTests.cs +++ b/src/Microsoft.NetCore.Analyzers/UnitTests/Security/UseAutoValidateAntiforgeryTokenTests.cs @@ -28,7 +28,7 @@ public void Test_GlobalAntiForgeryFilter_Add_ChildrenOfIAsyncAuthorizationFilter using Microsoft.AspNetCore.Mvc.Filters; [MyValidateAntiForgeryAttribute] -class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase +class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : Controller { } @@ -42,7 +42,7 @@ public Task OnAuthorizationAsync (AuthorizationFilterContext context) } } -class TestClass : ControllerBase +class TestClass : Controller { [HttpDelete] public AcceptedAtActionResult CustomizedActionMethod (string actionName) @@ -73,7 +73,7 @@ public void Test_GlobalAntiForgeryFilter_Add_ChildrenOfIAuthorizationFilter_NotC using Microsoft.AspNetCore.Mvc.Filters; [MyValidateAntiForgeryAttribute] -class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase +class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : Controller { } @@ -81,13 +81,12 @@ class FilterClass : IAuthorizationFilter { public DefaultAntiforgery defaultAntiforgery; - public Task OnAuthorization (AuthorizationFilterContext context) + public void OnAuthorization (AuthorizationFilterContext context) { - return null; } } -class TestClass : ControllerBase +class TestClass : Controller { [HttpDelete] public AcceptedAtActionResult CustomizedActionMethod (string actionName) @@ -104,224 +103,244 @@ public void TestMethod () filterCollection.Add(typeof(FilterClass)); } }", - GetCSharpResultAt(26, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpDelete")); + GetCSharpResultAt(25, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpDelete")); } [Fact] - public void Test_ChildrenOfControllerBase_ActionMethodWithHttpPostAttribute_Diagnostic() + public void Test_ChildrenOfController_ActionMethodWithHttpPostAndHttpGetAttributes_Diagnostic() { VerifyCSharpWithDependencies(@" using Microsoft.AspNetCore.Mvc; [MyValidateAntiForgeryAttribute] -class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase +class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : Controller { } -class TestClass : ControllerBase +class TestClass : Controller { + [HttpGet] [HttpPost] public AcceptedAtActionResult CustomizedActionMethod (string actionName) { return null; } }", - GetCSharpResultAt(12, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpPost")); + GetCSharpResultAt(13, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpPost")); } [Fact] - public void Test_ChildrenOfControllerBase_ActionMethodWithHttpPostAndHttpGetAttributes_Diagnostic() + public void Test_ChildrenOfController_ActionMethodWithHttpPatchAttribute_Diagnostic() { VerifyCSharpWithDependencies(@" +using System; using Microsoft.AspNetCore.Mvc; [MyValidateAntiForgeryAttribute] -class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase +class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : Controller { } -class TestClass : ControllerBase +class TestClass : Controller { - [HttpGet] - [HttpPost] + [HttpPatch] public AcceptedAtActionResult CustomizedActionMethod (string actionName) { return null; } }", - GetCSharpResultAt(13, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpPost")); + GetCSharpResultAt(13, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpPatch")); } [Fact] - public void Test_ChildrenOfControllerBase_ActionMethodWithHttpPutAttribute_Diagnostic() + public void Test_ChildrenOfController_ActionMethodWithHttpPostAttribute_Diagnostic() { VerifyCSharpWithDependencies(@" using Microsoft.AspNetCore.Mvc; [MyValidateAntiForgeryAttribute] -class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase +class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : Controller { } -class TestClass : ControllerBase +class TestClass : Controller { - [HttpPut] + [HttpPost] public AcceptedAtActionResult CustomizedActionMethod (string actionName) { return null; } }", - GetCSharpResultAt(12, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpPut")); + GetCSharpResultAt(12, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpPost")); } [Fact] - public void Test_ChildrenOfControllerBase_ActionMethodWithHttpDeleteAttribute_Diagnostic() + public void Test_ChildrenOfController_ActionMethodWithHttpPutAttribute_Diagnostic() { VerifyCSharpWithDependencies(@" -using System; using Microsoft.AspNetCore.Mvc; [MyValidateAntiForgeryAttribute] -class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase +class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : Controller { } -class TestClass : ControllerBase +class TestClass : Controller { - [HttpDelete] + [HttpPut] public AcceptedAtActionResult CustomizedActionMethod (string actionName) { return null; } }", - GetCSharpResultAt(13, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpDelete")); + GetCSharpResultAt(12, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpPut")); } [Fact] - public void Test_ChildrenOfControllerBase_ActionMethodWithHttpPatchAttribute_Diagnostic() + public void Test_ChildrenOfController_ActionMethodWithHttpDeleteAttribute_Diagnostic() { VerifyCSharpWithDependencies(@" -using System; using Microsoft.AspNetCore.Mvc; [MyValidateAntiForgeryAttribute] -class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase +class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : Controller { } -class TestClass : ControllerBase +class TestClass : Controller { - [HttpPatch] + [HttpDelete] public AcceptedAtActionResult CustomizedActionMethod (string actionName) { return null; } }", - GetCSharpResultAt(13, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpPatch")); + GetCSharpResultAt(12, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpDelete")); } [Fact] - public void Test_ChildrenOfController_ActionMethodWithHttpPostAttribute_Diagnostic() + public void Test_WithoutValidateAntiForgeryAttribute_ActionMethodWithTwoHttpVervAttributes_Diagnostic() { VerifyCSharpWithDependencies(@" +using System.Threading.Tasks; +using Microsoft.AspNetCore.Antiforgery.Internal; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; [MyValidateAntiForgeryAttribute] -class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase +class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : Controller { } class TestClass : Controller { + [HttpDelete] [HttpPost] public AcceptedAtActionResult CustomizedActionMethod (string actionName) { return null; } }", - GetCSharpResultAt(12, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpPost")); + GetCSharpResultAt(17, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpDelete")); } [Fact] - public void Test_ChildrenOfController_ActionMethodWithHttpPutAttribute_Diagnostic() + public void Test_NoValidateAntiForgeryTokenAttribute_ActionMethodMissingHttpVerbAttribute_Diagnostic() { VerifyCSharpWithDependencies(@" +using System.Threading.Tasks; +using Microsoft.AspNetCore.Antiforgery.Internal; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; [MyValidateAntiForgeryAttribute] -class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase +class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : Controller { } class TestClass : Controller { - [HttpPut] public AcceptedAtActionResult CustomizedActionMethod (string actionName) { return null; } }", - GetCSharpResultAt(12, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpPut")); + GetCSharpResultAt(15, 35, UseAutoValidateAntiforgeryToken.MissHttpVerbAttributeRule, "CustomizedActionMethod")); } - [Fact] - public void Test_ChildrenOfController_ActionMethodWithHttpDeleteAttribute_Diagnostic() + [Fact, WorkItem(2844, "https://github.com/dotnet/roslyn-analyzers/issues/2844")] + public void Test_ConcurrencyIssue_Diagnostic() { VerifyCSharpWithDependencies(@" using Microsoft.AspNetCore.Mvc; [MyValidateAntiForgeryAttribute] -class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase +class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : Controller { } class TestClass : Controller { - [HttpDelete] + [HttpPut] public AcceptedAtActionResult CustomizedActionMethod (string actionName) { return null; } -}", - GetCSharpResultAt(12, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpDelete")); - } +} - [Fact] - public void Test_WithoutValidateAntiForgeryAttribute_ActionMethodWithTwoHttpVervAttributes_Diagnostic() - { - VerifyCSharpWithDependencies(@" -using System.Threading.Tasks; -using Microsoft.AspNetCore.Antiforgery.Internal; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; +class TestClass2 : Controller +{ + [HttpPut] + public AcceptedAtActionResult CustomizedActionMethod2 (string actionName) + { + return null; + } +} -[MyValidateAntiForgeryAttribute] -class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase +class TestClass3 : Controller { + [HttpPut] + public AcceptedAtActionResult CustomizedActionMethod3 (string actionName) + { + return null; + } } -class TestClass : ControllerBase +class TestClass4 : Controller { - [HttpDelete] - [HttpPost] - public AcceptedAtActionResult CustomizedActionMethod (string actionName) + [HttpPut] + public AcceptedAtActionResult CustomizedActionMethod4 (string actionName) + { + return null; + } +} + +class TestClass5 : Controller +{ + [HttpPut] + public AcceptedAtActionResult CustomizedActionMethod5 (string actionName) { return null; } }", - GetCSharpResultAt(17, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpDelete")); + GetCSharpResultAt(12, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpPut"), + GetCSharpResultAt(21, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod2", "HttpPut"), + GetCSharpResultAt(30, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod3", "HttpPut"), + GetCSharpResultAt(39, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod4", "HttpPut"), + GetCSharpResultAt(48, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod5", "HttpPut")); } - [Fact] - public void Test_NoValidateAntiForgeryTokenAttribute_ActionMethodMissingHttpVerbAttribute_Diagnostic() + [Theory] + [InlineData("dotnet_code_quality.CA5391.exclude_aspnet_core_mvc_controllerbase = false")] + public void EditorConfigConfiguration_OnlyLookAtDerivedClassesOfController_DefaultValue_Diagnostic(string editorConfigText) { - VerifyCSharpWithDependencies(@" -using System.Threading.Tasks; -using Microsoft.AspNetCore.Antiforgery.Internal; -using Microsoft.AspNetCore.Http; + VerifyCSharpAcrossTwoAssemblies( + ASPNetCoreApis.CSharp, + @" +using System; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; [MyValidateAntiForgeryAttribute] class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase @@ -330,12 +349,14 @@ class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase class TestClass : ControllerBase { + [HttpDelete] public AcceptedAtActionResult CustomizedActionMethod (string actionName) { return null; } }", - GetCSharpResultAt(15, 35, UseAutoValidateAntiforgeryToken.MissHttpVerbAttributeRule, "CustomizedActionMethod")); + GetEditorConfigAdditionalFile(editorConfigText), + GetCSharpResultAt(13, 35, UseAutoValidateAntiforgeryToken.UseAutoValidateAntiforgeryTokenRule, "CustomizedActionMethod", "HttpDelete")); } [Fact] @@ -1355,6 +1376,33 @@ public void TestMethod () }"); } + [Theory] + [InlineData("")] + [InlineData("dotnet_code_quality.CA5391.exclude_aspnet_core_mvc_controllerbase = true")] + public void EditorConfigConfiguration_OnlyLookAtDerivedClassesOfController_NonDefaultValue_NoDiagnostic(string editorConfigText) + { + VerifyCSharpAcrossTwoAssemblies( + ASPNetCoreApis.CSharp, + @" +using System; +using Microsoft.AspNetCore.Mvc; + +[MyValidateAntiForgeryAttribute] +class MakeSureValidateAntiForgeryAttributeIsUsedSomeWhereClass : ControllerBase +{ +} + +class TestClass : ControllerBase +{ + [HttpDelete] + public AcceptedAtActionResult CustomizedActionMethod (string actionName) + { + return null; + } +}", + GetEditorConfigAdditionalFile(editorConfigText)); + } + protected override DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() { return new UseAutoValidateAntiforgeryToken(); diff --git a/src/Test.Utilities/MinimalImplementations/ASPNetCoreApis.cs b/src/Test.Utilities/MinimalImplementations/ASPNetCoreApis.cs index 033a174c2d..2f8947ae14 100644 --- a/src/Test.Utilities/MinimalImplementations/ASPNetCoreApis.cs +++ b/src/Test.Utilities/MinimalImplementations/ASPNetCoreApis.cs @@ -8,7 +8,7 @@ public static class ASPNetCoreApis using System; using System.Threading.Tasks; -class MyValidateAntiForgeryAttribute : Attribute +public class MyValidateAntiForgeryAttribute : Attribute { } @@ -115,7 +115,7 @@ public interface IAsyncAuthorizationFilter : IFilterMetadata public interface IAuthorizationFilter : IFilterMetadata { - Task OnAuthorization (AuthorizationFilterContext context); + void OnAuthorization (AuthorizationFilterContext context); } } diff --git a/src/Utilities/Compiler/Options/EditorConfigOptionNames.cs b/src/Utilities/Compiler/Options/EditorConfigOptionNames.cs index 7d7922b1f9..2c8188cab4 100644 --- a/src/Utilities/Compiler/Options/EditorConfigOptionNames.cs +++ b/src/Utilities/Compiler/Options/EditorConfigOptionNames.cs @@ -104,5 +104,10 @@ internal static partial class EditorConfigOptionNames /// Do not use the OR operator to represent the bitwise combination of its member values, use the integeral value directly. /// public const string UnsafeDllImportSearchPathBits = "unsafe_DllImportSearchPath_bits"; + + /// + /// Boolean option to configure whether to exclude aspnet core mvc ControllerBase when considering CSRF. + /// + public const string ExcludeAspnetCoreMvcControllerBase = "exclude_aspnet_core_mvc_controllerbase"; } }