Constants
ACTION_AUTH_PATTERNS = [{/User\.Identity\.IsAuthenticated/, "ASP.NET User.Identity.IsAuthenticated"}, {/User\.IsInRole\s*\(/, "ASP.NET User.IsInRole check"}, {/HttpContext\.User/, "ASP.NET HttpContext.User check"}]
ASP.NET Core middleware auth in action body
ACTION_REFUSAL_RE = /\breturn\s+(?:(?:base|this)\.)?(?:Challenge|Forbid|Unauthorized)\s*\(|\breturn\s+new\s+(?:ChallengeResult|ForbidResult|UnauthorizedResult)\b|\breturn\s+StatusCode\s*\(\s*(?:40[13]\b|StatusCodes\.Status40[13]|\(int\)\s*HttpStatusCode\.(?:Unauthorized|Forbidden))/
return Challenge(); / Forbid() / Unauthorized() / a 401/403 status.
ALLOW_ANONYMOUS_PATTERN = /\[(?:[^\]]*,\s*)?AllowAnonymous\s*[(,\]]/
ATTRIBUTE_BASE_ROOTS = {"AuthorizeAttribute" => :authorize, "IAllowAnonymous" => :anonymous, "AllowAnonymousAttribute" => :anonymous}
AUTHORIZE_KINDS = {"Roles" => "ASP.NET [Authorize(Roles)]", "Policy" => "ASP.NET [Authorize(Policy)]", "AuthenticationSchemes" => "ASP.NET [Authorize(AuthenticationSchemes)]"}
AUTHORIZE_PATTERNS = [{/\[Authorize\]/, "ASP.NET [Authorize]"}, {/\[Authorize\s*\(\s*Roles\s*=/, "ASP.NET [Authorize(Roles)]"}, {/\[Authorize\s*\(\s*Policy\s*=/, "ASP.NET [Authorize(Policy)]"}, {/\[Authorize\s*\(\s*AuthenticationSchemes\s*=/, "ASP.NET [Authorize(AuthenticationSchemes)]"}, {/\[(?:[^\]]*,\s*)?Authorize\s*[(,\]]/, "ASP.NET [Authorize]"}]
[Authorize] on a minimal API handler lambda, read off the route
statement's masked text (attributes on declarations are read as nodes).
CALLER_CALL = /\bGetCurrent(?:User|Customer|Account|Member|Principal)\w*\s*\(|\w+\s*\(\s*(?:(?:\w+\.)*HttpContext\.)?User\s*\)/
A call naming the current caller: an accessor for the signed-in
user/customer, or a lookup handed the ClaimsPrincipal.
CLASS_DECL_NAME_RE = /\bclass\s+([A-Z]\w*)/
CONTROLLER_TECHS = ["cs_aspnet_mvc", "cs_aspnet_core_mvc"] of ::
StringCONTROLLERS_GLOBAL = /
MapControllers\s*\(\s*\)\s*\.
RequireAuthorization\s*\(|
Filters\.
Add\s*(?:<\s*
AuthorizeFilter\s*>|\(\s*new\s+
AuthorizeFilter\b)|filters\.
Add\s*\(\s*new\s+(?:
System\.
Web\.
Mvc\.)?
AuthorizeAttribute\b/
FALLBACK_POLICY = /FallbackPolicy\s*=[^;]*RequireAuthenticatedUser\s*\(/
App-wide authorization that every endpoint without its own
[AllowAnonymous] inherits: ASP.NET Core's FallbackPolicy, the
controller-wide MapControllers().RequireAuthorization() / global
AuthorizeFilter, and MVC 5's filters.Add(new AuthorizeAttribute()).
FILTER_REFUSAL_RE = /\b\w+\.Result\s*=\s*new\s+(?:ChallengeResult|ForbidResult|UnauthorizedResult|UnauthorizedObjectResult)\b|\b\w+\.Result\s*=\s*new\s+StatusCodeResult\s*\(\s*(?:40[13]\b|StatusCodes\.Status40[13]|\(int\)\s*HttpStatusCode\.(?:Unauthorized|Forbidden))/
An MVC filter refusing the request: an authentication challenge, a
forbid, or a 401/403 status.
GROUP_DECLARATION_RE = /\b(\w+)\s*=\s*[\w.]*MapGroup\s*\(/
GROUP_DECLARATION_RECEIVER = /=\s*(\w+)\s*\.\s*MapGroup\b/
var v1 = api.MapGroup("/v1"): a nested group, which inherits api's.
GROUP_STATEMENT_RE = /^\s*(\w+)\s*\.\s*RequireAuthorization\s*\(/
HOST_PROJECT_RE = /Sdk\s*=\s*"Microsoft\.NET\.Sdk\.Web"|<OutputType>\s*(?:Win)?Exe\s*</i
Web SDK projects and executables are apps; the rest are libraries.
LOCAL_ASSIGNMENT_RE = /\b(?:var|[A-Z][\w<>]*\??)\s+(\w+)\s*=\s*([^;]+);/
MEMBER_DECLARATION = /^\s*(?:\[[^\]]*\]\s*)*(?:(?:public|private|protected|internal|static|async|override|virtual|sealed|unsafe|partial|new|extern)\s+)*(?:[\w?<>,\[\]]+\s+)?(?!(?:if|for|foreach|while|switch|catch|using|lock|fixed)\b)[\w]+\s*\(/
A method or local function, with or without an access modifier.
The name is one identifier, so a call (group.MapGet(, Task.Delay()
is not one, and if ( / using ( are not either.
MINIMAL_API_ALLOW_ANON = /\.AllowAnonymous\s*\(/
MINIMAL_API_RECEIVER = /^\s*(\w+)\s*\.\s*Map(?:Get|Post|Put|Delete|Patch|Methods|Group)\b/
MINIMAL_API_REQUIRE_AUTH = /\.RequireAuthorization\s*\(/
Minimal API fluent auth: app.MapGet("/x", h).RequireAuthorization();.
This is a chained method call, never a [...] attribute, so it can't
live in AUTHORIZE_PATTERNS (which is scanned in attribute position).
PERMISSION_CALL = /\A(?:await\s+)?_\w+\s*\.\s*\w*(?:Authorize|Permission)\w*\s*\(/
A permission lookup on an injected service: which roles hold the
permission is data, so configuration decides who gets through.
PRINCIPAL_EXPR = /(?<![\w.])User\b|\bHttpContext\.User\b/
The request's ClaimsPrincipal: the controller's User, or
HttpContext.User (not a model's request.User).
PROJECT_REFERENCE_RE = /<ProjectReference\s[^>]*?Include\s*=\s*"([^"]+)"/i
ROUTE_BUILDER_EXTENSION = /\bstatic\s[^;{}()=]*?\b(\w+)\s*(?:<[^<>()]*>\s*)?\(\s*this\s+(?:RouteGroupBuilder|IEndpointRouteBuilder|WebApplication)\s+(\w+)/
A route-builder extension method, static RouteGroupBuilder MapOrdersApi(this IEndpointRouteBuilder app): its name and the name of
its this builder.
ROUTE_STATEMENT_TECHS = ["cs_aspnet_core_minimal_api", "cs_carter"] of ::
StringTechs whose endpoint line is a route registration statement.
STATEMENT_LINES = 80
The statement starting on line_idx, through the ; outside every
bracket: a block-bodied lambda's own ;s do not end it, so auth
chained after its }) is part of it. A closing bracket the statement
did not open (the end of the enclosing block) ends it too.
TEST_PATH = /\/(?:[Tt]ests?|testassets|[^\/]+\.\w*Tests?)\/|Tests?\.cs\z/
Test hosts (WebApplicationFactory, a Foo.Tests project's Startup)
configure their own auth, which must not leak onto the app. The
conventions of Analyzer::CSharp::Common.csharp_test_path?, plus the
*.Tests project directory.
TYPEOF_RE = /\btypeof\s*\(\s*(\w+)\s*\)/
USING_RE = /^\s*(?:global\s+)?using\s+([\w.]+)\s*;/m