SDK-Shipped Analyzers
SDK-Shipped Analyzers
Section titled “SDK-Shipped Analyzers”The package ships Purview.BuildSdk.Analyzers.dll (plus a separate code-fix assembly for the
IDE) and adds the analyzer to every C# project as an <Analyzer> item, so the rules surface in both
command-line builds and Visual Studio.
| Rule | Category | Severity | Description |
|---|---|---|---|
PDS0001 | (suppressor) | — | Suppresses CS1591 for EditorBrowsable(Never) members |
PDS0002 | Naming | Warning | Files under a project-root Extensions/ folder reset their namespace |
PDS0003 | Style | Warning | Prefer an explicit type with target-typed new() over var |
PDS0004 | Naming | Warning | Use correct acronym capitalization (Api → API) |
PDS0005 | (suppressor) | — | Suppresses IDE0130 for files rooted under Extensions/ |
PDS0006 | (suppressor) | — | Suppresses CA1815 on a union declaration |
PDS0007 | (suppressor) | — | Suppresses CA1034 on an extension block |
PDS0008 | Security | Warning | A BFF’s authentication cookie must not be set HttpOnly = false |
PDS0009 | Security | Warning | A BFF’s authentication cookie must not be set SameSite = None |
PDS0008 / PDS0009 — BFF authentication cookie
Section titled “PDS0008 / PDS0009 — BFF authentication cookie”Active only in a project the SDK classifies as a BFF (build_property.IsBffProject=true). A BFF keeps
the tokens on the server and hands the browser a session cookie; these rules report the two settings
that undo that:
PDS0008—HttpOnly = false: script running in the page, injected script included, can read the session and replay it from anywhere.PDS0009—SameSite = None: the browser attaches the session to cross-site requests, so every BFF endpoint becomes a cross-site request forgery target.
Only a constant assigned to the cookie of CookieAuthenticationOptions is reported
(options.Cookie.HttpOnly = false, or Cookie = { SameSite = SameSiteMode.None }). The antiforgery
cookie, which an SPA legitimately reads, and the OpenID Connect correlation and nonce cookies, which
legitimately need SameSite=None, are never reported. A cookie configured in another assembly, or
through a local variable holding the builder, is missed rather than guessed at.
A persisted Data Protection key ring is deliberately not checked: the PersistKeysTo* call usually
lives in a shared ServiceDefaults project the BFF’s compilation cannot see, so a rule would report
correctly configured BFFs. Anti-forgery on forwarded routes stays a review obligation.
Style policy
Section titled “Style policy”The SDK ships the modifier/visibility and field-naming policy in .editorconfig and enforces it with
ValidatePurviewStylePolicy (PRSGD0006-PRSGD0009), so a repository cannot neuter it:
dotnet_style_require_accessibility_modifiers = omit_if_default:warning: a declared modifier that matches the language default (privateinside a type,internalat namespace scope,publicinside an interface) is reported byIDE0040and must be removed.- Private instance fields must be
_camelCase(_name, nevername): the_prefix keeps field access unambiguous, so the noisythis.qualifier is never needed. Private constants and privatestatic readonlyfields are type-level state and stay PascalCase; non-private constants andstatic readonlyfields follow the same rule, and local constants stay camelCase. CA1515(public type in an application/test assembly) ships at warning;CA1852(seal internal types) is not suppressed for types exposed throughInternalsVisibleTo;CA1034is not disabled forExtensions/files; the API-surface rulesCA1062/CA1707are not pre-suppressed for repositories. Where the public surface is framework-mandated the SDK exempts itself: Aspire hosts and CLI apps keep their nested public options types (Spectre.Console settings,[ZodSchema]resource-kit options), and test projects use the test-context rule set below. Both exemptions are recorded soPRSGD0008accepts them while still rejecting repository-authored silencing.- Test and shared-testing projects are context aware: they enforce the same strict style contract as
the rest of the repository (
IDE0040, field naming, formatting, theIDE1006naming rules) but not the production API-surface rules.PurviewTestContextNoWarnexemptsCA1002,CA1012,CA1034,CA1047,CA1050,CA1051,CA1062,CA1064,CA1515andCA1707, because test classes and fixtures are legitimately public, test names useMethod_Scenario_Expectation, helpers expose fields and take fixture parameters without null guards, and abstract test bases have public constructors.DisablePurviewTestContextRuleSet=trueopts a repository into the production rules for tests too. - Shared testing projects are helper libraries (their test packages request the test-host shape, and
the SDK reasserts
OutputType=Libraryafter package props). Their fixtures stay public API, which is also whyCA1515does not apply to them;PurviewSharedTestingOutputType=Exeopts back in.
| Code | Reported when |
|---|---|
PRSGD0006 | dotnet_style_require_accessibility_modifiers is set to anything other than omit_if_default. |
PRSGD0007 | A policy rule is downgraded below warning (IDE0040, CA1515, CA1852, CA1034), any policy rule is set to none/silent, or the Style category is disabled in bulk without an explicit IDE0040 severity. |
PRSGD0008 | One of the accessibility rules (IDE0040, CA1515, CA1852, CA1034, CA1012, CA1047, CA1050, CA1051, CA1062, CA1064, CA1707) is added to NoWarn by the repository. Entries the SDK injects itself (the test-context rule set, CA1515 for Aspire hosts and CLI apps) are ignored. |
PRSGD0009 | IDE1006 is hidden, the private instance field naming rule is downgraded below warning, or the _ prefix is removed from the field naming style. |
Opt out with <DisablePurviewStylePolicyValidation>true</DisablePurviewStylePolicyValidation>. A stale
bootstrapped .editorconfig is reported rather than obeyed — refresh it with
PurviewRepoBootstrapMode=Always (or delete it).
PDS0002 — Extensions namespace rule
Section titled “PDS0002 — Extensions namespace rule”When a file is placed under a project-root Extensions/ folder, the analyzer intentionally treats
that folder as a namespace reset point.
- Scope: only files where the first project-relative segment is exactly
Extensions - Expected namespace: derived from subfolders under
Extensions/(file name is ignored) RootNamespaceis deliberately ignored for these files
Examples:
| Project-relative file path | Expected namespace |
|---|---|
Extensions/System/StringExtensions.cs | System |
Extensions/Microsoft/Extensions/Configuration/ConfigurationExtensions.cs | Microsoft.Extensions.Configuration |
Extensions/TopLevel.cs | (global namespace) |
To avoid conflicting guidance, IDE0130 is suppressed (PDS0005) for files in this root Extensions/
scope. The shipped .editorconfig also suppresses the namespace-conflict diagnostics this convention
can trigger (CA1724, CS0436, CS1591, IDE0005), so extension files never need #pragma
suppressions. Outside this scope, normal IDE0130 behaviour remains unchanged.
PDS0003 — Prefer explicit type with target-typed new()
Section titled “PDS0003 — Prefer explicit type with target-typed new()”Flags var declarations that use a target-typed object creation initializer, preferring:
// ❌ flaggedvar service = new Service();// ✅ preferredService service = new();PDS0004 — Acronym capitalization
Section titled “PDS0004 — Acronym capitalization”PDS0004 follows .NET naming guidance for well-known framework spellings (Sql, Guid, Uuid,
Url, Dns, Http, Xml, DbContext, …) while still enforcing uppercase for acronyms such as
Api → API, Ai → AI, Cpu → CPU, Gpu → GPU, Cli → CLI, Gui → GUI, Ram → RAM,
and Ssh → SSH. By default the acronym-like segments Http, Xml, Json, Id, Sdk, Sql,
Uuid, Url, Dns, Tcp, Udp, Csv, Pdf, Html, Css, Ftp, Smtp, Imap, and Db are
exempt — Db is deliberately exempt so the prevalent EF Core/ADO.NET spellings (DbContext,
DbConnection, DbSet, CreateDbContext) are never flagged; a repo that prefers DB can re-enable
it via acronym_map = Db:DB. All options are customisable per repo, project, or folder via
.editorconfig and merge with the shipped defaults — config entries override them (so you can opt
into Sql → SQL or opt out of Cli → CLI without re-declaring every default):
dotnet_analyzer_configuration.pds0004.allowed_words— semicolon-separated segments that are never flagged.dotnet_analyzer_configuration.pds0004.acronym_map— semicolon-separatedKey:Valuecorrections.dotnet_analyzer_configuration.pds0004.allowed_identifiers— semicolon-separated whole identifiers that are never flagged. Matched in the order listed (first match wins) by exact name or word-boundary prefix, so a brand name likeCosmosDbalso coversCosmosDbServer/CosmosDbContext.
Members whose names are mandated by a contract — interface implementations (implicit or explicit) and base-class overrides — are never renamed, since doing so would break the contract.
[*.cs]# Optional overrides; everything not mentioned keeps its shipped default.dotnet_analyzer_configuration.pds0004.allowed_words = Clidotnet_analyzer_configuration.pds0004.acronym_map = Db:DBdotnet_analyzer_configuration.pds0004.allowed_identifiers = ICosmosDBService;CosmosDb;GitHub;YouTubeThe code fix for PDS0004 renames the identifier and all of its references across the solution; see
Code Fixes.