Dotnet security review
Skill tunahanaliozturk/secure-dotnet-skills/skills/dotnet-security-review
Aegis — 12 judgment-style agent skills for secure, production-grade .NET on Azure (security, design, performance, concurrency, observability). Works with Claude Code, Codex, Cursor, Gemini.
npx -y skills add tunahanaliozturk/secure-dotnet-skills --skill dotnet-security-reviewAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when reviewing a .NET / ASP.NET Core service, pull request, or diff for security issues — authorization gaps, injection, insecure crypto, secret leakage, unsafe deserialization — before merge or deploy.
SKILL.md
8.7 KB, as published. Nobody here has run it
.NET Security Review
Directs the agent to perform a lens-by-lens security review of an ASP.NET Core service or PR, rating each finding by severity and exploitability and producing a concrete, API-named fix for every issue found.
When to use
- A PR touches auth, data access, serialization, crypto, secret handling, or external calls in an ASP.NET Core service.
- A new endpoint, controller, or middleware is being added that accepts untrusted input.
- A pre-deploy security gate is required for a .NET service targeting any environment.
- Reviewing a diff where the change surface is too large to hold in one read.
Process
- Scope the change and map trust boundaries. Identify every external-input entry point (route params, query strings, request bodies, headers, file uploads), the authentication surface, and outbound calls. Note which actions are state-changing vs read-only.
- Confirm default-deny authorization. Verify a fallback policy (
RequireAuthenticatedUser) is registered inAddAuthorizationand that no sensitive controller or action is silently reachable via[AllowAnonymous]. - Walk the checks lens by lens (authorization → injection → deserialization → crypto → secrets → transport → CORS → antiforgery/headers). Treat each lens as a separate pass; do not skip lenses because earlier findings were found.
- Rate each finding by severity and exploitability. Use Critical / High / Medium / Low. A Critical finding blocks merge. Rate by: data sensitivity, authentication prerequisite, and whether the attacker input is directly reachable.
- Give a concrete fix per finding, naming the .NET API to use. "Replace
FromSqlRawstring-concat withFromSqlInterpolated(or passSqlParameterobjects)" is acceptable; "sanitize input" is not. - Re-scan for the same class of issue elsewhere in the service. If IDOR appears in one handler, grep the project for the same pattern in sibling handlers. Security issues are rarely singleton.
.NET / Azure checks
[Authorize]/[AllowAnonymous]coverage and default-deny. Confirm every controller and action that mutates state or returns sensitive data bears[Authorize](or an explicit policy via[Authorize(Policy = "…")]). ConfirmAddAuthorization(opts => opts.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build())is wired. Flag any[AllowAnonymous]on POST/PUT/PATCH/DELETE endpoints — intentional or accidental?- Broken object-level authorization / IDOR. In each handler that accepts a resource id (route, query, body), confirm the handler loads the resource and asserts
resource.OwnerId == currentUserId(or checks an equivalent claim/scope) before returning or mutating it. Fetching_db.Orders.FindAsync(id)with no ownership predicate after[Authorize]is IDOR. - Overposting / mass assignment. Check whether
[FromBody]binds directly to an EF entity class. If so, the caller can overwriteIsAdmin,OwnerId, or other server-managed fields. Require a dedicated request DTO; map to the entity explicitly (or use_mapper.Map<Entity>(dto)with a profile that excludes protected fields). - SQL injection via
FromSqlRaw/ExecuteSqlRaw. Any call of the formcontext.Set<T>().FromSqlRaw($"SELECT … WHERE id = {userInput}")or string concatenation is injectable. Require eitherFromSqlInterpolated(which parameterizes the interpolated holes automatically) or explicitSqlParameter/DbParameterobjects. LINQ queries compiled to SQL are safe; flag only raw-SQL APIs. - Unsafe deserialization. Flag any use of
BinaryFormatter(deprecated and exploitable for remote code execution) orNetDataContractSerializer. In Newtonsoft.Json / Json.NET, flagTypeNameHandling.All(orAuto) inJsonSerializerSettingswithout a customISerializationBinderallowlist — this enables gadget-chain RCE. PreferSystem.Text.Json(no polymorphic type-name resolution by default). - Cryptographic weaknesses. Flag
MD5.Create()orSHA1.Create()used for password hashing, HMAC verification, or certificate fingerprinting. Flag hardcodedbyte[] key/byte[] ivliterals passed toAes.Create()orTripleDES.Create(). Flag any hand-rolled password hasher; requireIPasswordHasher<T>from ASP.NET Core Identity orMicrosoft.AspNetCore.DataProtection(IDataProtectionProvider). - Secret leakage. Scan
appsettings*.jsonfor connection strings with passwords,ClientSecret,ApiKey, or SAS tokens. Confirm no_logger.Log…($"token={token}")or_logger.Log…(user.PasswordHash)calls. In Azure, secrets must live in Key Vault and be consumed viaAddAzureKeyVault/DefaultAzureCredential— not as plain environment variables holding raw secrets. - Transport and outbound call safety. Flag
HttpClientHandlerorSocketsHttpHandlerinstances whereServerCertificateCustomValidationCallbackreturnstrueunconditionally (disables TLS validation). Flag outbound HTTP calls built from user-supplied URLs without allowlisting the scheme and host — this enables SSRF. RequireIHttpClientFactory-typed clients with a fixedBaseAddress. - CORS misconfiguration. Flag
AllowAnyOrigin()chained withAllowCredentials()— this is rejected by the spec and means the CORS policy silently fails, or in older middleware versions, it leaks credentials. Policies must name explicit origins (WithOrigins("https://app.example.com")) when credentials (cookies orAuthorizationheaders) are sent. - Antiforgery, security headers, and exception detail. For cookie-authenticated apps, confirm
AddAntiforgeryis registered andValidateAntiForgeryToken(orAutoValidateAntiForgeryToken) is applied to state-changing endpoints. Confirmapp.UseExceptionHandler("/error")is used in production — notapp.UseDeveloperExceptionPage(). ConfirmStrict-Transport-Security,X-Content-Type-Options, andX-Frame-Optionsheaders are emitted (via middleware or Azure Front Door / App Service managed headers).
Red flags
| Signal | Why it matters |
|---|---|
context.Orders.FromSqlRaw($"… WHERE Id = {id}") | String-interpolated raw SQL is directly injectable; the {id} hole receives un-parameterized user input. |
AllowAnyOrigin().AllowCredentials() | Violates the CORS spec and can expose authenticated responses to attacker-controlled origins depending on browser / middleware version. |
BinaryFormatter in any serialization path | Enables unauthenticated remote code execution via deserialization gadget chains; .NET itself marks the type obsolete-as-error since .NET 9. |
ServerCertificateCustomValidationCallback = (_, _, _, _) => true | Disables TLS certificate validation on outbound calls, enabling man-in-the-middle interception with no warning. |
[AllowAnonymous] on a POST/PUT/DELETE endpoint | Bypasses the fallback policy; any authentication requirement on that action is silently dropped. Must be intentional and documented. |
Connection string with Password= literal in appsettings.json | Secrets committed to source control are permanently exposed in git history even after removal; rotate immediately and move to Key Vault. |
TypeNameHandling.All or TypeNameHandling.Auto in JsonSerializerSettings | Allows the caller to control which .NET type is deserialized, enabling gadget-chain RCE against any Newtonsoft.Json-based endpoint. |
_db.FindAsync(id) with no ownership predicate after [Authorize] | Authenticated but not authorized — any logged-in user can access any other user's resource by guessing or enumerating ids (IDOR). |
MD5.Create() used to hash passwords | MD5 is cryptographically broken; preimage attacks and rainbow tables make stored hashes trivially reversible. Use IPasswordHasher<T>. |
new byte[] { 0x00, … } hardcoded as AES key or IV | A static key stored in source code is extractable by anyone with repo access; rotate and move to IDataProtectionProvider or Key Vault. |
Example
See examples/dotnet-security-review/.
Related skills
- secrets-config-audit — use for deeper focus on secret handling, Key Vault wiring, and config-layer assignment.
- threat-model-endpoint — use to enumerate per-endpoint STRIDE threats and mitigations before or after a security review.
- auth-flow-review — use for deeper authn/z review covering token validation, policies, and cookie hygiene.