From 00ccd64b002bac8dfbac8cec49069952cf2eef56 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 26 Aug 2026 15:37:43 +0200 Subject: [PATCH 1/3] C#: Add global ASP.NET Core - enable validation for all action methods. --- .../MissingAntiForgeryTokenValidation.cs | 39 +++++++++++++++++++ ...MissingAntiForgeryTokenValidation.expected | 1 + .../MissingAntiForgeryTokenValidation.qlref | 1 + .../CWE-352/global-aspnetcore/options | 2 + 4 files changed, 43 insertions(+) create mode 100644 csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.cs create mode 100644 csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.expected create mode 100644 csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.qlref create mode 100644 csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/options diff --git a/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.cs b/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.cs new file mode 100644 index 000000000000..438ad03f3200 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; + +public class HomeController : Controller +{ + // GOOD: This is validated by the global filter. + [HttpPost] + public ActionResult Login() + { + return View(); + } + + // GOOD: Antiforgery token is validated explicitly. + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult UpdateDetails() + { + return View(); + } +} + +public class Program +{ + public static void Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + + // Register MVC controllers and Razor views. + // The global filter automatically validates antiforgery tokens + // for unsafe HTTP methods such as POST, PUT, PATCH, and DELETE. + builder.Services.AddControllersWithViews(options => + { + options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); + }); + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.expected b/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.expected new file mode 100644 index 000000000000..459ba06f47d0 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.expected @@ -0,0 +1 @@ +| MissingAntiForgeryTokenValidation.cs:11:25:11:29 | Login | Method 'Login' handles a POST request without performing CSRF token validation. | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.qlref b/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.qlref new file mode 100644 index 000000000000..5e1ab2426c65 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.qlref @@ -0,0 +1 @@ +query: Security Features/CWE-352/MissingAntiForgeryTokenValidation.ql diff --git a/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/options b/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/options new file mode 100644 index 000000000000..698ad488b6d4 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.AspNetCore.App/Microsoft.AspNetCore.App.csproj From 22002fd55ac10cb686a22128ea1fb8d67cd58c4a Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 26 Aug 2026 15:40:23 +0200 Subject: [PATCH 2/3] C#: Detect possible global auto validate configuration to remove FPs. --- .../frameworks/microsoft/AspNetCore.qll | 28 +++++++++++++++---- .../MissingAntiForgeryTokenValidation.ql | 14 ++++++++-- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll index abdd81646828..c71e30d6a1b9 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll @@ -144,6 +144,15 @@ class ValidateAntiForgeryAttribute extends Attribute { } } +/** + * The `Microsoft.AspNetCore.Mvc.AutoValidateAntiforgeryTokenAttribute` class. + */ +class AutoValidateAntiforgeryTokenAttribute extends Class { + AutoValidateAntiforgeryTokenAttribute() { + this.hasFullyQualifiedName("Microsoft.AspNetCore.Mvc", "AutoValidateAntiforgeryTokenAttribute") + } +} + /** * A class that has a name like `[Auto...]Validate[...]Anti[Ff]orgery[...Token]` and implements `IFilterMetadata` interface * This class can be added to a collection of global `MvcOptions.Filters` collection. @@ -230,11 +239,20 @@ private Assembly getAnAssemblyFor(Type type) { result = getACompilationFor(type).getOutputAssembly() } -private predicate isMicrosoftAspNetCoreMvcRegistration(MethodCall call) { - call.getTarget() - .hasFullyQualifiedName("Microsoft.Extensions.DependencyInjection", - ["MvcServiceCollectionExtensions", "MvcCoreServiceCollectionExtensions"], - ["AddControllers", "AddControllersWithViews", "AddMvc", "AddMvcCore"]) +/** + * A method that is a registration of an ASP.NET Core MVC service, i.e. `AddControllers`, `AddControllersWithViews`, `AddMvc`, or `AddMvcCore`. + */ +class MicrosoftAspNetCoreMvcRegistration extends Method { + MicrosoftAspNetCoreMvcRegistration() { + this.hasFullyQualifiedName("Microsoft.Extensions.DependencyInjection", + ["MvcServiceCollectionExtensions", "MvcCoreServiceCollectionExtensions"], + ["AddControllers", "AddControllersWithViews", "AddMvc", "AddMvcCore"]) + } +} + +/** Holds if the method call is a registration of an ASP.NET Core MVC service. */ +predicate isMicrosoftAspNetCoreMvcRegistration(MethodCall call) { + call.getTarget() instanceof MicrosoftAspNetCoreMvcRegistration } private predicate isMicrosoftAspNetCoreMvcApplication(Compilation compilation) { diff --git a/csharp/ql/src/Security Features/CWE-352/MissingAntiForgeryTokenValidation.ql b/csharp/ql/src/Security Features/CWE-352/MissingAntiForgeryTokenValidation.ql index 77a3f2b59450..677bba60015c 100644 --- a/csharp/ql/src/Security Features/CWE-352/MissingAntiForgeryTokenValidation.ql +++ b/csharp/ql/src/Security Features/CWE-352/MissingAntiForgeryTokenValidation.ql @@ -36,8 +36,6 @@ private Method getAStartedMethod() { /** * Holds if the project has a global anti forgery filter. - * - * No AspNetCore case here as the corresponding class doesn't seem to exist. */ predicate hasGlobalAntiForgeryFilter() { // A global filter added @@ -49,6 +47,18 @@ predicate hasGlobalAntiForgeryFilter() { // The filter is added by the Application_Start() method getAStartedMethod() = addGlobalFilter.getEnclosingCallable() ) + or + exists(MethodCall addGlobalFilter, MethodCall registrationCall | + addGlobalFilter.getTarget() = + any(AspNetCore::MicrosoftAspNetCoreMvcFilterCollection collection).getAddMethod() and + // The filter is the `AutoValidateAntiforgeryTokenAttribute` filter. + addGlobalFilter.getArgument(0).getType() instanceof + AspNetCore::AutoValidateAntiforgeryTokenAttribute and + // The filter is added in an ASP.NET Core registration call, which is provided as a lambda argument + // to the Mvc registration method. + registrationCall.getTarget() instanceof AspNetCore::MicrosoftAspNetCoreMvcRegistration and + registrationCall.getAnArgument() = addGlobalFilter.getEnclosingCallable() + ) } private class RequireAntiforgeryTokenAttribute extends Attribute { From 35e8b9ba0ecf5dff8e9e113d0289589496f43028 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 26 Aug 2026 16:00:20 +0200 Subject: [PATCH 3/3] C#: Update test expected output. --- .../global-aspnetcore/MissingAntiForgeryTokenValidation.expected | 1 - 1 file changed, 1 deletion(-) diff --git a/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.expected b/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.expected index 459ba06f47d0..e69de29bb2d1 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-352/global-aspnetcore/MissingAntiForgeryTokenValidation.expected @@ -1 +0,0 @@ -| MissingAntiForgeryTokenValidation.cs:11:25:11:29 | Login | Method 'Login' handles a POST request without performing CSRF token validation. |