Claude Skill

sota-dotnet

State-of-the-art C# / .NET engineering rules (2026 baseline, .NET 10 LTS / C# 14) that Claude applies when writing or auditing .NET code. Covers modern idioms (records, nullable reference types, pattern matching, spans, file-scoped namespaces), API/null/immutability/`IDisposable`

LLM Mart · 0 points · 9 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download martinholovsky-SOTA-skills-skills_sota-dotnet-ec2abf6.zip · 18 KB
Part of martinholovsky/sota-skills — 39 skills

Install

skills CLI npx skills add https://github.com/martinholovsky/SOTA-skills/tree/main/skills/sota-dotnet
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install martinholovsky-sota-skills@llmmart
Git git clone https://github.com/martinholovsky/SOTA-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole martinholovsky/sota-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

SOTA C# / .NET (2026)

Expert-level rules for producing and auditing production .NET. The runtime is memory-safe, so risk concentrates in injection, deserialization, async correctness, and dependency supply chain. Baseline: .NET 10 LTS (released Nov 2025, supported to Nov 2028) and C# 14 (records, nullable reference types, pattern matching, spans, extension members, the field keyword) — flag where a control needs a specific version. Every rule states the why; every rules file ends with an audit checklist of grep/analyzer patterns.

Purpose

Two consumers, one source of truth:

  • BUILD mode — generating C#/.NET: follow the rules as defaults. Enable nullable reference types and treat analyzer warnings as errors; prefer immutability and the async-all-the-way model. Deviate only with a comment.
  • AUDIT mode — reviewing existing code: hunt violations with the audit checklists, classify by severity, report in the finding format below. SQL string-building and legacy deserialization are presumed exploitable.

BUILD mode

  1. Before writing, read the rules files relevant to the task (see index). A web API touching untrusted input + a DB + async needs 02, 03, 04.
  2. Apply the top-10 non-negotiables (below) unconditionally.
  3. New projects: target the current LTS (net10.0), <Nullable>enable</Nullable>, <TreatWarningsAsErrors>true</TreatWarningsAsErrors>, <AnalysisLevel>latest-Recommended</AnalysisLevel>, NuGet lockfile + RestoreLockedMode in CI, and dotnet format from day one (rules/06).
  4. Async all the way down — never block on async (.Result/.Wait()/ GetAwaiter().GetResult()) (rules/03). Use CancellationToken end to end.
  5. Prefer the BCL and well-known libraries; parameterize all data access; use the framework's auth/Data Protection rather than rolling your own (rules/04).
  6. When you take a sharp path (reflection, unsafe, DynamicMethod, suppressing a nullable/analyzer warning), leave a // NOTE(sota): explaining why.

AUDIT mode

Work each relevant rules file's audit checklist against the target. Run the greps and the Roslyn analyzers (incl. the security CA rules); confirm hits manually. Check the dependency tree against known-CVE databases.

Severity conventions

Severity Meaning Examples
CRITICAL Exploitable on reachable input SQL via string interpolation/concat into FromSqlRaw/ExecuteSqlRaw/Dapper, BinaryFormatter/NetDataContractSerializer/LosFormatter or JSON TypeNameHandling.All on untrusted data, command injection, deserialization gadget
HIGH Likely incident or security weakness Missing auth on an endpoint, disabled cert validation (ServerCertificateCustomValidationCallback => true), MD5/SHA-1 or DES/ECB for security, Random for tokens, blocking on async causing deadlock/thread-pool starvation, secrets in config/source
MEDIUM Correctness/maintainability hazard async void (non-handler), missing ConfigureAwait(false) in a library, IDisposable not disposed / no using, nullable warnings suppressed with !, swallowed exceptions, mutable static state
LOW Idiom/perf debt Sync-over-collection LINQ on hot path, needless allocations/boxing, class where a record/struct fits, not using Span/pooling on hot path
INFO Style/doc/hygiene formatting, naming, missing XML docs, nullable annotations absent (not enabled)

Finding format

[SEVERITY] File.cs:LINE — short title
  Rule: rules/NN-name.md § section
  Evidence: the offending line(s), verbatim
  Impact: one sentence — what executes/leaks/deadlocks, under what input
  Fix: concrete replacement code or action
  Effort: trivial | small | medium | large

Group findings by severity, CRITICAL first. End with: counts per severity, the three highest-leverage fixes, and which checklists/analyzers were run.

Rules index

File Read this when...
rules/01-idioms.md Writing/reviewing any C#: records & record struct, nullable reference types, pattern matching/switch expressions, spans, LINQ discipline, var, expression vs statement, file-scoped namespaces, error handling, modern C# 12–14 features
rules/02-design-api.md Designing types/APIs: nullable reference type discipline, int? over a magic int and using TryParse's bool (its out is 0 on failure), immutability, IDisposable/IAsyncDisposable and using, exceptions, value vs reference types, internal/visibility, DI (the built-in container), options pattern
rules/03-async-concurrency.md Anything async/Task/threads: async-all-the-way, never block (.Result/.Wait()), ConfigureAwait(false) in libraries, CancellationToken flow, async void, Channel<T>, IAsyncEnumerable, TPL/Parallel, thread-safety, ValueTask
rules/04-security.md Any input crossing a trust boundary: SQL (EF Core/Dapper parameterization), legacy serializers (BinaryFormatter removed .NET 9) + JSON TypeNameHandling, command/path injection, ASP.NET Core authn/authz, antiforgery/CORS, Data Protection, crypto (RandomNumberGenerator, AES-GCM), secrets; OWASP .NET
rules/05-performance.md Latency/throughput/memory work: GC (gen/SOH/LOH, server vs workstation), allocation reduction, Span<T>/Memory<T>/ArrayPool, struct/record struct, BenchmarkDotNet, async overhead, Native AOT / trimming, string handling
rules/06-build-tooling-ci.md Setting up or auditing build/CI: SDK/TFM targeting, Directory.Build.props, nullable + warnings-as-errors, Roslyn analyzers (incl. security CA rules), dotnet format, NuGet lockfiles + supply chain (lock mode, source mapping, signed packages, CVE scan), SBOM. Test strategy lives in sota-testing; this owns .NET build/test mechanics (xUnit/NUnit, Testcontainers).

Top-10 non-negotiables

  1. No string-built SQL. Parameterize: EF Core LINQ or parameters (FromSql/interpolated FromSql, never FromSqlRaw/ExecuteSqlRaw with concatenation); Dapper with parameters. Interpolated/concatenated SQL is CRITICAL. (rules/04)
  2. No unsafe deserialization. BinaryFormatter is removed in .NET 9+ (throws); never reintroduce it or NetDataContractSerializer/LosFormatter/SoapFormatter, and never use Json.NET TypeNameHandling.Auto/All or JsonSerializer with an unrestricted type resolver on untrusted data. Use System.Text.Json with known types. (rules/04)
  3. Async all the way; never block on async. No .Result, .Wait(), .GetAwaiter().GetResult() on a hot/request path (deadlock + thread-pool starvation). async void only for event handlers. (rules/03)
  4. Flow CancellationToken end to end through async APIs and honor it. (rules/03)
  5. ConfigureAwait(false) in library code (code with no sync context that doesn't need to resume on the original context). (rules/03)
  6. Nullable reference types enabled and honored. <Nullable>enable</Nullable>; don't paper over warnings with the null-forgiving !. (rules/01, rules/02)
  7. Deterministic disposal. Everything IDisposable/IAsyncDisposable is in a using/await using or owned by a DI-managed lifetime — HttpClient via IHttpClientFactory, not new-per-call. (rules/02)
  8. Crypto uses the right primitives. RandomNumberGenerator (never Random) for tokens/keys/IVs; AES-GCM (not ECB); no MD5/SHA-1 for security; ASP.NET Core Data Protection for at-rest tokens; never disable TLS cert validation. (rules/04)
  9. AuthN/AuthZ enforced server-side on every non-public endpoint ([Authorize]/policies/endpoint auth), antiforgery for cookie-auth POSTs, CORS locked to known origins. (rules/04)
  10. Analyzers + nullable + lockfile gate CI. TreatWarningsAsErrors, Roslyn analyzers (incl. security CA rules), dotnet format --verify-no-changes, NuGet locked-mode restore + CVE scan. (rules/06)
Files (sota-skills)
  • rules
    • 01-idioms.md 4.2 KB
      # 01 — Idioms: modern C#, nullability, error handling
      
      Write to current C# (14 / .NET 10), not C# 7. The through-line is
      **immutability, the nullable-aware type system, and expression-oriented code**.
      Reference: [What's new in C#](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/),
      [.NET 10](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10/overview).
      
      ## 1. Records and immutability
      
      - **`record`** (reference) and **`record struct`** for immutable data carriers —
        value equality, `with` expressions, deconstruction, concise `ToString`.
        Replace hand-written DTO/value classes. Use `init`-only setters for
        immutable-after-construction.
      - Prefer immutable by default: `readonly` fields, `init` properties, immutable
        collections where it matters. Immutable types are thread-safe (`rules/03`).
      - `record class` for entities with identity-by-value semantics; plain `class` for
        mutable services/stateful objects; `struct`/`record struct` for small values
        (`rules/05`).
      
      ## 2. Nullable reference types (NRT)
      
      - Enable `<Nullable>enable</Nullable>` solution-wide. The compiler then tracks
        null-flow: `string` is non-null, `string?` may be null. This eliminates a huge
        class of `NullReferenceException` at compile time.
      - **Honor the warnings** — don't silence with the null-forgiving operator `!`
        unless you've genuinely proven non-null (and comment why). Scattered `!` is a
        MEDIUM finding: it disables the very safety you enabled.
      - Annotate APIs precisely (`?`, `[NotNullWhen]`, `[MaybeNull]`); guard external/
        deserialized input at the boundary (it can be null regardless of annotations).
      
      ## 3. Pattern matching & expressions
      
      - Prefer `switch` **expressions** and pattern matching (type/property/relational/
        list patterns) over if-cascades and `switch` statements — exhaustive, concise,
        value-returning.
      - Use expression-bodied members, target-typed `new`, collection expressions
        (`[1, 2, 3]`), and `nameof`. Use `var` when the type is obvious from the RHS.
      
      ## 4. C# 12–14 niceties
      
      - Primary constructors (classes/structs), collection expressions, file-scoped
        namespaces (`namespace Foo;`), required members (`required`), raw string
        literals.
      - C# 14: **extension members** (extension properties/operators/static members),
        the **`field`** contextual keyword (access the synthesized backing field in an
        accessor without declaring it), and broader `Span<T>`/`ReadOnlySpan<T>`/`T[]`
        conversions. Use where they clarify; don't chase novelty.
      
      ## 5. LINQ discipline
      
      - LINQ for clarity over hand loops, but beware: multiple enumeration of an
        `IEnumerable` (materialize with `ToList()` once if iterated repeatedly), hidden
        N+1 with `IQueryable` (`rules/04`/perf), and allocation/closure cost on hot
        paths (`rules/05`). Know when a query executes (deferred vs eager).
      
      ## 6. Error handling
      
      - Throw the most specific exception type; don't catch `Exception`/`Exception e`
        just to swallow or log-and-continue on a path that must abort. Preserve stack
        with `throw;` (not `throw ex;`).
      - Don't use exceptions for control flow. Use the `TryParse`/`Try...` pattern or a
        result type for expected failures on hot paths. `ArgumentNullException.ThrowIfNull`
        and `ArgumentException.ThrowIf...` for guard clauses.
      - Exceptions are unchecked in C#; document what a public API throws.
      
      ## Audit checklist
      
      ```bash
      # Null-forgiving overuse — MEDIUM (defeats NRT)
      grep -rnE '[A-Za-z0-9_)\]]\!\.' --include='*.cs' . | grep -v '!=' | head    # x!.Member
      # Is NRT even enabled?
      grep -rniE '<Nullable>\s*enable' . --include='*.csproj' --include='Directory.Build.props' || echo "NRT not enabled — HIGH"
      
      # Swallowed exceptions / throw ex — MEDIUM
      grep -rnzoE 'catch\s*\([^)]*\)\s*\{\s*\}' --include='*.cs' .
      grep -rnE 'throw ex;' --include='*.cs' .                     # loses stack trace
      grep -rnE 'catch \(Exception' --include='*.cs' . | head
      
      # Legacy idioms — LOW
      grep -rnE '\bclass\b' --include='*.cs' . | head             # DTOs that should be records?
      grep -rnE 'namespace [A-Za-z0-9_.]+\s*\{' --include='*.cs' .  # non-file-scoped namespaces
      
      # Multiple enumeration / LINQ on hot path — LOW (verify)
      grep -rnE '\.Where\(|\.Select\(|\.Count\(\)' --include='*.cs' . | head
      
      # Broad analyzer pass (idioms): enable .NET analyzers + IDE rules in CI (rules/06)
      ```
      
    • 02-design-api.md 5.3 KB
      # 02 — API design, disposal, exceptions, DI
      
      Good .NET APIs make nullability and lifetimes explicit and lean on the built-in
      DI and options patterns. Idioms are in `01`, async in `03`.
      
      ## 1. Nullability as contract
      
      - With NRT enabled (`rules/01`), the API signature *is* the null contract:
        return `T?` only when null is meaningful; accept `T` to require non-null.
        Annotate with `[NotNullWhen(true)]` etc. for `Try` patterns.
      - Validate external input at the boundary regardless of annotations
        (deserialized/wire data can violate them): `ArgumentNullException.ThrowIfNull`,
        range/format guards.
      
      ## 1a. In-band sentinels vs `int?` and the Try pattern
      
      `String.IndexOf` returns `-1` for not-found, and `Int32.TryParse` sets its `out`
      parameter to **zero on failure** — *"contains the … value equivalent … if the
      conversion succeeded, or zero if the conversion failed"* (learn.microsoft.com,
      `System.Int32.TryParse`, .NET 10; some overloads say *"an undefined value on
      failure"*). So `TryParse` is the right shape — the `bool` return is out-of-band —
      but the `out` value **is** an in-band sentinel the moment the `bool` is ignored.
      
      - §1's nullability contract extends to value types: `int?`/`Nullable<T>` is the
        answer for an absent number, not `-1` or `0`. `int?` participates in
        `HasValue`/pattern matching; a magic `int` participates in nothing.
      - Prefer `TryParse` over `Parse`, and **use the `bool`** — `if (int.TryParse(s, out
        var n))`, never `int.TryParse(s, out var n); use(n);`. A discarded `bool` converts
        a correct API into the class in `sota-architecture` rules/02 §8a.
      - Nullable reference types cover references only; NRT being on says nothing about a
        `-1` in an `int`. Don't let a clean NRT build read as absence being modeled.
      - Audit: `grep -rnE 'return -1;|out var [a-z]+\);' --include='*.cs' src/` and
        comparisons where one operand is `-1`-filtered.
      
      ## 2. Immutability & value semantics
      
      - Prefer immutable public types (records, `init` properties, `IReadOnlyList<T>`/
        `IReadOnlyDictionary<T>` return types). Don't expose mutable internal
        collections — return read-only views/copies.
      - Choose `struct`/`record struct` for small, short-lived values; `class`/`record`
        for entities and larger objects (`rules/05` for the perf trade-offs).
      
      ## 3. Disposal & resource lifetime
      
      - Everything `IDisposable`/`IAsyncDisposable` is scoped with `using`/`await using`
        or owned by a DI lifetime — never a manual `Dispose()` you can skip on an
        exception. Implement the dispose pattern correctly (and `IAsyncDisposable` for
        async cleanup).
      - **`HttpClient`**: never `new HttpClient()` per call (socket exhaustion) — use
        `IHttpClientFactory` (typed/named clients) or a single long-lived instance.
      - Don't dispose objects you don't own (e.g. injected/DI-managed singletons,
        `HttpClient` from the factory).
      
      ## 4. Exceptions in the contract
      
      - Throw specific BCL exceptions (`ArgumentException`, `InvalidOperationException`,
        `ArgumentNullException`) before custom ones; document thrown types. Don't leak
        low-level exceptions across an abstraction — wrap, preserving `InnerException`.
      - Validate arguments with guard helpers (`ArgumentNullException.ThrowIfNull`,
        `ArgumentOutOfRangeException.ThrowIf...`).
      
      ## 5. Dependency injection & options
      
      - Use the built-in `Microsoft.Extensions.DependencyInjection` container;
        register with the correct **lifetime** (`Singleton`/`Scoped`/`Transient`).
        Classic bug: a `Scoped` (e.g. `DbContext`) captured by a `Singleton` →
        captive dependency / cross-request state. Don't inject `IServiceProvider`
        and resolve manually (service-locator anti-pattern) except at composition
        roots.
      - Constructor injection over property/field; avoid mutable static state (MEDIUM
        — concurrency + testability hazard). Use the **options pattern**
        (`IOptions<T>`/`IOptionsMonitor<T>`) for configuration, validated on start
        (`ValidateOnStart`).
      
      ## 6. Visibility & API surface
      
      - Keep the public surface minimal: `internal` by default, `public` deliberately;
        `sealed` classes not designed for inheritance. Use `InternalsVisibleTo` for
        test access rather than widening visibility.
      
      ## Audit checklist
      
      ```bash
      # HttpClient per-call — HIGH (socket exhaustion)
      grep -rnE 'new HttpClient\(' --include='*.cs' . | head           # prefer IHttpClientFactory
      
      # IDisposable not in using — MEDIUM
      grep -rnE 'new (SqlConnection|FileStream|StreamReader|StreamWriter|MemoryStream|HttpResponseMessage)\(' --include='*.cs' . \
        | head     # verify using/await using
      
      # DI lifetime bugs — MEDIUM/HIGH (captive dependency)
      grep -rnE 'AddSingleton|AddScoped|AddTransient' --include='*.cs' . | head
      grep -rnE 'GetService|GetRequiredService|IServiceProvider' --include='*.cs' . | head  # service locator?
      
      # Mutable static state — MEDIUM
      grep -rnE 'static (?!readonly|class|void|async|partial)[A-Za-z<>\[\]?]+ [A-Za-z]' --include='*.cs' . | head
      
      # throw ex / swallow — MEDIUM (see rules/01)
      grep -rnE 'throw ex;' --include='*.cs' .
      
      # Mutable collection exposed — LOW
      grep -rnE 'public (List|Dictionary|HashSet)<' --include='*.cs' . | head   # prefer IReadOnly* / encapsulate
      
      # In-band sentinels (§1a) — `int?` over a magic int; TryParse's bool is the signal
      grep -rnE 'return -1;' --include='*.cs' .                       # producer; prefer int?
      grep -rnE 'TryParse\([^)]*out var [a-z]+\);' --include='*.cs' .   # bool DISCARDED -> out is 0 on failure
      ```
      
    • 03-async-concurrency.md 3.8 KB
      # 03 — Async/await and concurrency
      
      `async`/`await` is the .NET concurrency model, and misusing it causes the two
      classic production failures: **deadlocks** (blocking on async under a sync
      context) and **thread-pool starvation** (sync-over-async at scale). The rules
      are mechanical — follow them. Reference:
      [async guidance](https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/).
      
      ## 1. Async all the way; never block on async
      
      - Once a call chain is async, keep it async to the entry point. **Never** block:
        `.Result`, `.Wait()`, `.GetAwaiter().GetResult()` on a Task in request/UI/hot
        paths deadlocks under a sync context (classic ASP.NET/UI) and starves the
        thread pool under load. CRITICAL/HIGH depending on reachability.
      - Expose async APIs (`...Async`) for I/O; don't wrap sync I/O in `Task.Run` to
        "make it async" on the server (it just burns a thread).
      
      ## 2. ConfigureAwait
      
      - In **library** code (no dependence on a sync context), `await ... .ConfigureAwait(false)`
        so continuations don't capture/marshal back to a context — avoids deadlocks
        and overhead. In app code (ASP.NET Core has no sync context; modern UI differs)
        it matters less, but libraries should always do it.
      
      ## 3. async void and exceptions
      
      - **`async void`** only for event handlers. Elsewhere it's fire-and-forget with
        unobservable exceptions that can crash the process — use `async Task`. A
        non-handler `async void` is a MEDIUM–HIGH finding.
      - Don't fire-and-forget Tasks without observing them (lost exceptions, no
        back-pressure). If intentional, document and handle faults.
      
      ## 4. CancellationToken everywhere
      
      - Flow a `CancellationToken` through every async method and pass it to inner
        calls (I/O, EF Core, HttpClient); honor it (`ThrowIfCancellationRequested`,
        or the token-aware API). Endpoints should bind the request-aborted token.
        Ignoring cancellation wastes work and delays shutdown.
      
      ## 5. Concurrency primitives
      
      - Shared mutable state needs synchronization: `lock` (don't `await` inside a
        `lock` — use `SemaphoreSlim` for async mutual exclusion), `Interlocked` for
        counters, `Concurrent*` collections (`ConcurrentDictionary`) over manual
        locking. Immutability avoids the problem.
      - `System.Threading.Channels` (`Channel<T>`) for producer/consumer with
        back-pressure; `IAsyncEnumerable<T>` + `await foreach` for async streams.
      - `Task.WhenAll` for parallel awaits (observe all exceptions — `WhenAll`
        aggregates); `Parallel.ForEachAsync` for bounded data parallelism. Don't
        spin unbounded concurrent work — bound it (`SemaphoreSlim`, channel,
        `Parallel` options).
      - `ValueTask` for very hot, often-synchronous paths — but don't await a
        `ValueTask` twice or store it (`rules/05`).
      
      ## Audit checklist
      
      ```bash
      # Blocking on async — CRITICAL/HIGH (deadlock / thread-pool starvation)
      grep -rnE '\.(Result|Wait\(\))|GetAwaiter\(\)\.GetResult\(\)' --include='*.cs' . | head
      grep -rnE 'Task\.Run\(' --include='*.cs' . | head             # sync wrapped as async on server?
      
      # async void (non-handler) — MEDIUM/HIGH
      grep -rnE 'async void ' --include='*.cs' . | grep -viE 'EventHandler|_Click|on[A-Z]' | head
      
      # Missing ConfigureAwait(false) in libraries — MEDIUM
      grep -rnE 'await ' --include='*.cs' . | grep -v 'ConfigureAwait' | head    # in library projects
      
      # Cancellation not flowed — MEDIUM
      grep -rnE 'async Task[<A-Za-z, >]* [A-Za-z]+\([^)]*\)' --include='*.cs' . | grep -v 'CancellationToken' | head
      
      # await inside lock — HIGH (won't compile for lock, but SemaphoreSlim misuse / sync-over-async)
      grep -rnE 'lock\s*\(' --include='*.cs' . | head
      grep -rnE 'new (Dictionary|List)<' --include='*.cs' . | grep -i 'static\|shared'  # non-concurrent shared
      
      # Unbounded parallelism — MEDIUM (verify bounding)
      grep -rnE 'Task\.WhenAll|Parallel\.(For|ForEach)' --include='*.cs' . | head
      ```
      
    • 04-security.md 7 KB
      # 04 — Security: injection, deserialization, ASP.NET Core, crypto
      
      The CLR is memory-safe, so the dominant .NET vulnerabilities are **injection,
      unsafe deserialization, auth gaps, and crypto misuse**. Treat every byte from
      network/file/DB/config as untrusted. Reference:
      [OWASP .NET cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/DotNet_Security_Cheat_Sheet.html),
      [ASP.NET Core security](https://learn.microsoft.com/en-us/aspnet/core/security/).
      
      ## 1. SQL injection
      
      - **EF Core**: LINQ is parameterized and safe. `FromSql`/`ExecuteSql` (the
        *interpolated* `FromSqlInterpolated`-style) parameterize interpolated values.
        **`FromSqlRaw`/`ExecuteSqlRaw` with string concatenation/interpolation is
        CRITICAL** — they don't parameterize a built string.
      - **Dapper / ADO.NET**: always pass parameters (`new { id }` / `SqlParameter`),
        never concatenate input into the SQL text. Identifiers (table/column/ORDER BY)
        can't be parameters — allowlist them.
      
      ## 2. Deserialization
      
      - **`BinaryFormatter` is removed in .NET 9+** (the API throws
        `PlatformNotSupportedException`); it was a notorious RCE vector. Never
        reintroduce it (or `NetDataContractSerializer`, `SoapFormatter`, `LosFormatter`,
        `ObjectStateFormatter`) — CRITICAL on sight.
      - **JSON**: prefer `System.Text.Json` with known types. Newtonsoft
        `TypeNameHandling.Auto/All/Objects` (or `System.Text.Json` with an
        unrestricted polymorphic type resolver) on untrusted input enables gadget-style
        RCE — don't. Bind to explicit DTOs.
      - **`XmlSerializer`/`DataContractSerializer`** with attacker-controlled types is
        risky; disable DTD processing on XML readers (XXE) — `XmlReaderSettings {
        DtdProcessing = DtdProcessing.Prohibit, XmlResolver = null }`.
      
      ## 3. Command / path / other injection
      
      - **OS command**: avoid shelling out; if you must, use `ProcessStartInfo` with
        `ArgumentList` (no `UseShellExecute`, no concatenated `Arguments`/shell).
      - **Path traversal**: combine with a known root and verify the resolved
        `Path.GetFullPath` stays under it; reject `..`. Don't pass user input straight
        to file APIs.
      - **LDAP/XPath/regex (ReDoS)**: parameterize/escape; bound regex with timeouts
        (`Regex` `matchTimeout`) on untrusted input.
      
      ## 4. ASP.NET Core authn/authz & web
      
      - **AuthZ on every non-public endpoint**: `[Authorize]`/policies/role checks /
        endpoint authorization; default-deny. Missing auth is HIGH.
      - **Antiforgery** for cookie-authenticated state-changing requests
        (`[ValidateAntiForgeryToken]` / the antiforgery middleware). **CORS** locked
        to specific origins — never `AllowAnyOrigin()` with credentials.
      - **Passkeys**: ASP.NET Core Identity has built-in passkey (WebAuthn) support
        in .NET 10+ — prefer them for new interactive logins (depth:
        `sota-code-security`, `sota-identity-access`).
      - Validate/bind model input (data annotations / explicit validation); don't
        over-post (use DTOs/`[Bind]` allowlists, not the EF entity directly). Set
        security headers/HSTS; don't leak stack traces in production responses.
      - **Secrets**: never in source/`appsettings.json` committed to git — use user
        secrets (dev), env, or a vault (`sota-secrets-management`); don't log them.
      - **Runtime patch level is an audit surface**: the memory-safe runtime's
        residual risk includes framework CVEs — e.g. CVE-2025-55315 (Kestrel HTTP
        request smuggling, fixed in 8.0.21/9.0.10/10.0 RC2) and CVE-2026-45591
        (SignalR/Blazor Server MessagePack nested-array DoS, fixed in
        8.0.28/9.0.17/10.0.9, June 2026). Self-contained/AOT-published apps embed
        the framework — they need a rebuild and redeploy, not just host patching.
      
      ## 5. Cryptography & transport
      
      - **Randomness**: `System.Security.Cryptography.RandomNumberGenerator` (e.g.
        `RandomNumberGenerator.GetBytes`) for tokens/keys/IVs/salts — never
        `System.Random` (HIGH).
      - **Symmetric**: AES-GCM (`AesGcm`) for authenticated encryption; never ECB,
        never unauthenticated CBC. **Hashing**: SHA-256+; passwords via a KDF
        (`Rfc2898DeriveBytes`/PBKDF2, or Argon2/bcrypt via a library) — never plain
        MD5/SHA-1 (HIGH).
      - Use ASP.NET Core **Data Protection** for at-rest tokens/cookies rather than
        hand-rolled crypto — and keep the package patched:
        `Microsoft.AspNetCore.DataProtection` 10.0.0–10.0.6 let attackers forge
        authentication cookies and decrypt protected payloads (CVE-2026-40372,
        fixed in **10.0.7**). Patching alone isn't enough after exposure: forged
        artifacts stay valid, so revoke the key ring (`RevokeAllKeys()`) and rotate
        tokens/API keys issued during the vulnerable window. Constant-time compare
        (`CryptographicOperations.FixedTimeEquals`) for MACs/tokens.
      - **Post-quantum**: .NET 10 ships PQC in the BCL — `MLKem` (FIPS 203) plus
        `MLDsa`/`SlhDsa`/`CompositeMLDsa` (FIPS 204/205; still `[Experimental]`,
        SYSLIB5006), backed by OpenSSL 3.5+ or Windows CNG with PQC support. For new
        long-lived signatures/key exchange, plan migration on these built-ins rather
        than unvetted packages.
      - **TLS**: never disable validation — `ServerCertificateCustomValidationCallback`
        returning `true` (or `HttpClientHandler` accepting all certs) is HIGH/CRITICAL.
      
      ## Audit checklist
      
      ```bash
      # SQL injection — CRITICAL
      grep -rnE 'FromSqlRaw|ExecuteSqlRaw' --include='*.cs' . | head
      grep -rnE '(FromSqlRaw|ExecuteSqlRaw|CommandText|new SqlCommand)\([^)]*(\+|\$")' --include='*.cs' .
      grep -rnE '\.Query[^(]*\(\s*\$?"[^"]*\{' --include='*.cs' .       # Dapper string-interpolated SQL
      
      # Deserialization — CRITICAL
      grep -rnE 'BinaryFormatter|NetDataContractSerializer|LosFormatter|SoapFormatter|ObjectStateFormatter' --include='*.cs' .
      grep -rnE 'TypeNameHandling\.(Auto|All|Objects|Arrays)' --include='*.cs' .
      
      # XXE / command / path — HIGH/CRITICAL
      grep -rnE 'DtdProcessing|XmlResolver|new XmlDocument|XmlReader' --include='*.cs' . | head
      grep -rnE 'Process\.Start|ProcessStartInfo|UseShellExecute' --include='*.cs' . | head
      
      # Auth / CORS / antiforgery — HIGH
      grep -rnE 'AllowAnyOrigin|AllowAnyHeader|AllowAnyMethod' --include='*.cs' .
      grep -rnLE '\[Authorize\]|RequireAuthorization|\[AllowAnonymous\]' --include='*Controller.cs' . | head  # endpoints w/o auth?
      
      # Crypto misuse — HIGH
      grep -rnE '\bnew Random\(|System\.Random' --include='*.cs' . | grep -iE 'token|key|iv|salt|nonce|password|secret'
      grep -rnE 'MD5|SHA1|TripleDES|\bDES\b|CipherMode\.ECB' --include='*.cs' .
      grep -rnE 'ServerCertificateCustomValidationCallback|RemoteCertificateValidationCallback' --include='*.cs' . | head
      
      # Secrets in config/source — HIGH
      grep -rniE '(password|pwd|secret|apikey|api_key|connectionstring)\s*[=:]' appsettings*.json --include='*.cs' . | head
      
      # Vulnerable framework/package patch levels — HIGH
      grep -rnE 'Microsoft\.AspNetCore\.DataProtection' --include='*.csproj' --include='packages.lock.json' .  # 10.0.0–10.0.6 = CVE-2026-40372 (need 10.0.7+); if exposed while vulnerable: key ring revoked + tokens rotated?
      dotnet --list-runtimes  # ASP.NET Core < 8.0.21/9.0.10 (CVE-2025-55315) or < 8.0.28/9.0.17/10.0.9 (CVE-2026-45591)? Check container base-image tags; self-contained/AOT apps need rebuild
      
      # Static security analysis: enable security CA rules + a SAST (rules/06)
      ```
      
    • 05-performance.md 4 KB
      # 05 — Performance: GC, allocations, spans, AOT
      
      .NET performance is mostly about **allocation pressure and the GC**, plus using
      the modern low-allocation primitives. Measure with BenchmarkDotNet and a
      profiler before optimizing. Cross-reference `sota-performance` for methodology.
      
      ## 1. The GC and allocation pressure
      
      - The .NET GC is generational (gen0/1/2) with a separate **Large Object Heap**
        (objects ≥ 85,000 bytes). High allocation rate → frequent gen0 collections;
        large/pinned objects fragment the LOH. The win is usually **allocate less**,
        not tune the GC.
      - **Server GC** (`<ServerGarbageCollection>true`) for throughput on multi-core
        servers; workstation GC for client/low-latency-single-core. Know which you're
        running. Don't tune GC knobs before profiling shows GC is the bottleneck.
      
      ## 2. Reduce allocations
      
      - Avoid needless allocations on hot paths: reuse buffers (`ArrayPool<T>`),
        avoid LINQ allocations in tight loops (closures, iterators, intermediate
        collections), avoid boxing (value type → `object`/non-generic API), and avoid
        `params object[]` / string concatenation in loops (`StringBuilder`, or
        `string.Create`/interpolated handlers).
      - Prefer `struct`/`record struct` for small short-lived values to keep them off
        the heap — but beware large structs being copied (pass by `in`/`ref`).
      
      ## 3. Span and friends
      
      - `Span<T>`/`ReadOnlySpan<T>`/`Memory<T>` for slicing arrays/strings/stack
        buffers **without copying** — parsing, formatting, buffer processing.
        `stackalloc` for small fixed buffers. C# 14 broadens span conversions
        (`rules/01`).
      - `Utf8` APIs, `System.Text.Json` source-gen, and `IBufferWriter<T>` for
        low-allocation I/O. Pool large/reused buffers with `ArrayPool`/`MemoryPool`.
      
      ## 4. Async & throughput
      
      - Async done right scales I/O (`rules/03`); done wrong (blocking, sync-over-async)
        it starves the thread pool. `ValueTask`/`ValueTask<T>` for very hot,
        frequently-synchronous paths to avoid a `Task` allocation — but never await a
        `ValueTask` twice, store it, or block on it.
      
      ## 5. Measuring
      
      - **BenchmarkDotNet** for microbenchmarks — it handles warmup/JIT, isolates
        runs, and reports allocations (`[MemoryDiagnoser]`). Never `Stopwatch` around a
        loop. Profile with `dotnet-trace`/`dotnet-counters`/Visual Studio profiler /
        PerfView for allocation and GC analysis. Report distributions
        (`sota-performance`).
      
      ## 6. Native AOT & trimming
      
      - **Native AOT** (`<PublishAot>true`) ahead-of-time compiles to a native binary:
        fast startup, low memory, small footprint — ideal for CLIs, serverless, and
        containerized microservices. .NET 10 improves AOT (broader support, smaller/
        faster). Costs: no JIT/runtime codegen, so **reflection/dynamic loading must be
        trim-safe** (use source generators; annotate with `IsAotCompatible`); not all
        libraries are AOT/trim-compatible. Test the published binary.
      - Choose AOT for startup/footprint-bound workloads; stay on the JIT for
        long-running throughput-bound compute where peak JIT throughput wins.
      
      ## Audit checklist
      
      ```bash
      # Allocation/boxing on hot paths — LOW/MEDIUM (verify with profiler)
      grep -rnE '\+ ?"' --include='*.cs' . | grep -iE 'for ?\(|foreach|while'      # string concat in loops
      grep -rnE 'string\.Format|\$"' --include='*.cs' . | head                      # hot-path formatting
      grep -rnE '\.ToList\(\)|\.ToArray\(\)' --include='*.cs' . | head              # needless materialization in loops
      
      # Server GC configured for a server app?
      grep -rnE 'ServerGarbageCollection|ConcurrentGarbageCollection' *.csproj runtimeconfig* 2>/dev/null
      
      # Span/pooling opportunities (hot path) — LOW
      grep -rnE 'new byte\[|Substring\(|Split\(' --include='*.cs' . | head           # Span/ArrayPool candidates
      
      # Benchmark hygiene — verify BenchmarkDotNet, not Stopwatch loops
      grep -rnE 'Stopwatch' --include='*.cs' . | grep -i bench | head
      grep -rnE '\[Benchmark\]|MemoryDiagnoser' --include='*.cs' . || echo "no BenchmarkDotNet benchmarks"
      
      # Native AOT / trimming used? verify trim-safety
      grep -rnE 'PublishAot|PublishTrimmed|IsAotCompatible' *.csproj 2>/dev/null
      ```
      
    • 06-build-tooling-ci.md 4.6 KB
      # 06 — Build, tooling, supply chain, CI
      
      .NET project safety lives in the build: nullable + analyzers as errors, NuGet
      locking and CVE scanning, and consistent CI gates. This file owns build/test
      *mechanics*; test **strategy** (suite shape, doubles, coverage philosophy)
      lives in `sota-testing`.
      
      ## 1. Targeting & project hygiene
      
      - Target the current LTS TFM (`<TargetFramework>net10.0</TargetFramework>`); pin
        the SDK with `global.json` so every machine/CI builds with the same version.
      - Centralize settings in **`Directory.Build.props`** and dependency versions in
        **`Directory.Packages.props`** (Central Package Management) so versions are
        consistent and reviewed in one place.
      - Enable broadly: `<Nullable>enable</Nullable>`,
        `<TreatWarningsAsErrors>true</TreatWarningsAsErrors>`,
        `<AnalysisLevel>latest-Recommended</AnalysisLevel>` (or `All`),
        `<EnableNETAnalyzers>true</EnableNETAnalyzers>`,
        `<ImplicitUsings>enable</ImplicitUsings>`.
      
      ## 2. Analyzers & formatting
      
      - **Roslyn analyzers** (.NET analyzers ship in the SDK) catch correctness,
        reliability, and **security CA rules** (CA2100 SQL injection, CA53xx/CA54xx
        crypto, CA2300-series deserialization). Run as errors in CI. Add focused
        analyzers (e.g. for async) where useful.
      - **`dotnet format --verify-no-changes`** in CI so style/whitespace never enters
        review. EditorConfig holds the rules.
      - Consider a SAST (the security CA rules, or a dedicated scanner) for deeper
        taint analysis on web apps.
      
      ## 3. NuGet supply chain
      
      - **Lock dependencies**: enable `<RestorePackagesWithLockFile>true</...>` to
        generate `packages.lock.json`, and restore with **`--locked-mode`** in CI so
        builds are reproducible and a changed transitive dependency fails loudly.
      - **Package Source Mapping** (`nuget.config`) so each package only resolves from
        its intended feed — defeats **dependency confusion** (an internal name
        resolving from nuget.org). Restrict feeds to trusted sources over HTTPS.
      - **NuGetAudit** checks every restore against advisory data automatically
        (since NuGet 6.8/.NET 8 SDK; `NuGetAuditMode` defaults to `all` — transitive
        included — on net10.0+ projects). Gate CI by making the audit warnings errors:
        `<WarningsAsErrors>$(WarningsAsErrors);NU1903;NU1904</WarningsAsErrors>`
        (high/critical; add NU1901/NU1902 to be stricter). Configure `auditSources`
        in `nuget.config` if your feed lacks vulnerability data; suppress an accepted
        advisory explicitly with a `NuGetAuditSuppress` item (last resort); remediate
        with `dotnet package update --vulnerable`. Keep
        `dotnet list package --vulnerable --include-transitive` as the ad-hoc query;
        Dependabot/external scanners complement, not replace.
      - Verify **signed packages**; generate an **SBOM** for releases. See
        `sota-devsecops`.
      
      ## 4. CI gates
      
      - A PR build runs: `dotnet build` with warnings-as-errors (nullable + analyzers),
        `dotnet test` (xUnit/NUnit/MSTest) with coverage (coverlet) and a threshold,
        `dotnet format --verify-no-changes`, `--locked-mode` restore, and the
        NuGetAudit gate (NU190x as errors). Fail on any.
      - **Testcontainers for .NET** for real-dependency integration tests (DB/broker) —
        wire them here; *strategy* is `sota-testing`. Run with fixed culture/timezone
        for determinism (`InvariantGlobalization` where applicable).
      - Build deterministically (`<Deterministic>true</Deterministic>`, ContinuousIntegrationBuild)
        and produce symbols.
      
      ## Audit checklist
      
      ```bash
      # TFM/SDK pinned? settings centralized?
      grep -rnE '<TargetFramework' **/*.csproj 2>/dev/null | head
      ls global.json Directory.Build.props Directory.Packages.props 2>/dev/null | grep -q . || echo "no central build config"
      
      # Nullable + warnings-as-errors + analyzers?
      grep -rniE 'TreatWarningsAsErrors|<Nullable>|EnableNETAnalyzers|AnalysisLevel' **/*.csproj Directory.Build.props 2>/dev/null \
        || echo "nullable/analyzers/warnings-as-errors not enforced — HIGH"
      
      # NuGet locking + source mapping + CVE scan?
      ls packages.lock.json 2>/dev/null && grep -rn 'RestorePackagesWithLockFile' **/*.csproj Directory.Build.props 2>/dev/null \
        || echo "no NuGet lockfile — supply-chain risk"
      grep -rniE 'packageSourceMapping|locked-mode|NuGetAudit|NU190[0-9]|auditSources|dependabot' \
        nuget.config Directory.Build.props **/*.csproj .github/ *.yml 2>/dev/null \
        || echo "no source mapping / locked restore / NuGetAudit CI gate"
      
      # Formatting + deterministic build in CI?
      grep -rniE 'dotnet format|verify-no-changes|Deterministic|ContinuousIntegrationBuild' .github/ *.yml **/*.csproj 2>/dev/null | head
      
      # Test runner + coverage?
      grep -rniE 'xunit|nunit|mstest|coverlet|testcontainers' **/*.csproj 2>/dev/null | head
      ```
      
  • SKILL.md 8.9 KB
    ---
    name: sota-dotnet
    description: >-
      State-of-the-art C# / .NET engineering rules (2026 baseline, .NET 10 LTS /
      C# 14) that Claude applies when writing or auditing .NET code. Covers modern
      idioms (records, nullable reference types, pattern matching, spans, file-scoped
      namespaces), API/null/immutability/`IDisposable` design, async/await &
      concurrency (ConfigureAwait, channels, cancellation, TPL), security (OWASP
      .NET, deserialization — BinaryFormatter removed in .NET 9, EF/Dapper SQL
      injection, ASP.NET Core auth, Data Protection, crypto), performance (GC,
      Span and Memory, BenchmarkDotNet, Native AOT), and build/tooling/CI (dotnet
      CLI, NuGet lockfiles & supply chain, Roslyn analyzers, nullable). Trigger
      keywords - C#, .NET, dotnet, ASP.NET Core, async, await, Task, record,
      nullable reference types, Span, EF Core, Dapper, LINQ, NuGet, Roslyn analyzer,
      BinaryFormatter, Native AOT, BenchmarkDotNet, IDisposable, ConfigureAwait. Use
      for BOTH building .NET services/libraries and auditing them.
    ---
    
    # SOTA C# / .NET (2026)
    
    Expert-level rules for producing and auditing production .NET. The runtime is
    memory-safe, so risk concentrates in **injection, deserialization, async
    correctness, and dependency supply chain**. Baseline: **.NET 10 LTS** (released
    Nov 2025, supported to Nov 2028) and **C# 14** (records, nullable reference
    types, pattern matching, spans, extension members, the `field` keyword) — flag
    where a control needs a specific version. Every rule states the *why*; every
    rules file ends with an audit checklist of grep/analyzer patterns.
    
    ## Purpose
    
    Two consumers, one source of truth:
    
    - **BUILD mode** — generating C#/.NET: follow the rules as defaults. Enable
      **nullable reference types** and treat analyzer warnings as errors; prefer
      immutability and the async-all-the-way model. Deviate only with a comment.
    - **AUDIT mode** — reviewing existing code: hunt violations with the audit
      checklists, classify by severity, report in the finding format below. SQL
      string-building and legacy deserialization are presumed exploitable.
    
    ## BUILD mode
    
    1. Before writing, read the rules files relevant to the task (see index). A web
       API touching untrusted input + a DB + async needs `02`, `03`, `04`.
    2. Apply the **top-10 non-negotiables** (below) unconditionally.
    3. New projects: target the current LTS (`net10.0`), `<Nullable>enable</Nullable>`,
       `<TreatWarningsAsErrors>true</TreatWarningsAsErrors>`,
       `<AnalysisLevel>latest-Recommended</AnalysisLevel>`, NuGet lockfile +
       `RestoreLockedMode` in CI, and `dotnet format` from day one (`rules/06`).
    4. Async all the way down — never block on async (`.Result`/`.Wait()`/
       `GetAwaiter().GetResult()`) (`rules/03`). Use `CancellationToken` end to end.
    5. Prefer the BCL and well-known libraries; parameterize all data access; use
       the framework's auth/Data Protection rather than rolling your own (`rules/04`).
    6. When you take a sharp path (reflection, `unsafe`, `DynamicMethod`, suppressing
       a nullable/analyzer warning), leave a `// NOTE(sota):` explaining why.
    
    ## AUDIT mode
    
    Work each relevant rules file's audit checklist against the target. Run the
    greps and the Roslyn analyzers (incl. the security CA rules); confirm hits
    manually. Check the dependency tree against known-CVE databases.
    
    ### Severity conventions
    
    | Severity | Meaning | Examples |
    |---|---|---|
    | **CRITICAL** | Exploitable on reachable input | SQL via string interpolation/concat into `FromSqlRaw`/`ExecuteSqlRaw`/Dapper, `BinaryFormatter`/`NetDataContractSerializer`/`LosFormatter` or JSON `TypeNameHandling.All` on untrusted data, command injection, deserialization gadget |
    | **HIGH** | Likely incident or security weakness | Missing auth on an endpoint, disabled cert validation (`ServerCertificateCustomValidationCallback => true`), MD5/SHA-1 or `DES`/ECB for security, `Random` for tokens, blocking on async causing deadlock/thread-pool starvation, secrets in config/source |
    | **MEDIUM** | Correctness/maintainability hazard | `async void` (non-handler), missing `ConfigureAwait(false)` in a library, `IDisposable` not disposed / no `using`, nullable warnings suppressed with `!`, swallowed exceptions, mutable static state |
    | **LOW** | Idiom/perf debt | Sync-over-collection LINQ on hot path, needless allocations/boxing, `class` where a `record`/`struct` fits, not using `Span`/pooling on hot path |
    | **INFO** | Style/doc/hygiene | formatting, naming, missing XML docs, nullable annotations absent (not enabled) |
    
    ### Finding format
    
    ```
    [SEVERITY] File.cs:LINE — short title
      Rule: rules/NN-name.md § section
      Evidence: the offending line(s), verbatim
      Impact: one sentence — what executes/leaks/deadlocks, under what input
      Fix: concrete replacement code or action
      Effort: trivial | small | medium | large
    ```
    
    Group findings by severity, CRITICAL first. End with: counts per severity, the
    three highest-leverage fixes, and which checklists/analyzers were run.
    
    ## Rules index
    
    | File | Read this when... |
    |---|---|
    | `rules/01-idioms.md` | Writing/reviewing any C#: records & `record struct`, nullable reference types, pattern matching/`switch` expressions, spans, LINQ discipline, `var`, expression vs statement, file-scoped namespaces, error handling, modern C# 12–14 features |
    | `rules/02-design-api.md` | Designing types/APIs: nullable reference type discipline, `int?` over a magic `int` and **using `TryParse`'s `bool` (its `out` is `0` on failure)**, immutability, `IDisposable`/`IAsyncDisposable` and `using`, exceptions, value vs reference types, `internal`/visibility, DI (the built-in container), options pattern |
    | `rules/03-async-concurrency.md` | Anything `async`/`Task`/threads: async-all-the-way, never block (`.Result`/`.Wait()`), `ConfigureAwait(false)` in libraries, `CancellationToken` flow, `async void`, `Channel<T>`, `IAsyncEnumerable`, TPL/`Parallel`, thread-safety, `ValueTask` |
    | `rules/04-security.md` | Any input crossing a trust boundary: SQL (EF Core/Dapper parameterization), legacy serializers (`BinaryFormatter` removed .NET 9) + JSON `TypeNameHandling`, command/path injection, ASP.NET Core authn/authz, antiforgery/CORS, Data Protection, crypto (`RandomNumberGenerator`, AES-GCM), secrets; OWASP .NET |
    | `rules/05-performance.md` | Latency/throughput/memory work: GC (gen/SOH/LOH, server vs workstation), allocation reduction, `Span<T>`/`Memory<T>`/`ArrayPool`, `struct`/`record struct`, BenchmarkDotNet, async overhead, Native AOT / trimming, string handling |
    | `rules/06-build-tooling-ci.md` | Setting up or auditing build/CI: SDK/TFM targeting, `Directory.Build.props`, nullable + warnings-as-errors, Roslyn analyzers (incl. security CA rules), `dotnet format`, NuGet lockfiles + supply chain (lock mode, source mapping, signed packages, CVE scan), SBOM. **Test *strategy* lives in `sota-testing`; this owns .NET build/test mechanics (xUnit/NUnit, Testcontainers).** |
    
    ## Top-10 non-negotiables
    
    1. **No string-built SQL.** Parameterize: EF Core LINQ or parameters
       (`FromSql`/interpolated `FromSql`, never `FromSqlRaw`/`ExecuteSqlRaw` with
       concatenation); Dapper with parameters. Interpolated/concatenated SQL is
       CRITICAL. (`rules/04`)
    2. **No unsafe deserialization.** `BinaryFormatter` is removed in .NET 9+ (throws);
       never reintroduce it or `NetDataContractSerializer`/`LosFormatter`/`SoapFormatter`,
       and never use `Json.NET` `TypeNameHandling.Auto/All` or `JsonSerializer` with
       an unrestricted type resolver on untrusted data. Use `System.Text.Json` with
       known types. (`rules/04`)
    3. **Async all the way; never block on async.** No `.Result`, `.Wait()`,
       `.GetAwaiter().GetResult()` on a hot/request path (deadlock + thread-pool
       starvation). `async void` only for event handlers. (`rules/03`)
    4. **Flow `CancellationToken` end to end** through async APIs and honor it.
       (`rules/03`)
    5. **`ConfigureAwait(false)` in library code** (code with no sync context that
       doesn't need to resume on the original context). (`rules/03`)
    6. **Nullable reference types enabled and honored.** `<Nullable>enable</Nullable>`;
       don't paper over warnings with the null-forgiving `!`. (`rules/01`, `rules/02`)
    7. **Deterministic disposal.** Everything `IDisposable`/`IAsyncDisposable` is in a
       `using`/`await using` or owned by a DI-managed lifetime — `HttpClient` via
       `IHttpClientFactory`, not new-per-call. (`rules/02`)
    8. **Crypto uses the right primitives.** `RandomNumberGenerator` (never `Random`)
       for tokens/keys/IVs; AES-GCM (not ECB); no MD5/SHA-1 for security; ASP.NET
       Core **Data Protection** for at-rest tokens; never disable TLS cert
       validation. (`rules/04`)
    9. **AuthN/AuthZ enforced server-side** on every non-public endpoint
       (`[Authorize]`/policies/endpoint auth), antiforgery for cookie-auth POSTs,
       CORS locked to known origins. (`rules/04`)
    10. **Analyzers + nullable + lockfile gate CI.** `TreatWarningsAsErrors`,
        Roslyn analyzers (incl. security CA rules), `dotnet format --verify-no-changes`,
        NuGet locked-mode restore + CVE scan. (`rules/06`)
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related