Bitwarden.Server.Sdk.RestrictedDependencies 0.1.0

Prefix Reserved
dotnet add package Bitwarden.Server.Sdk.RestrictedDependencies --version 0.1.0
                    
NuGet\Install-Package Bitwarden.Server.Sdk.RestrictedDependencies -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Bitwarden.Server.Sdk.RestrictedDependencies" Version="0.1.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Bitwarden.Server.Sdk.RestrictedDependencies" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Bitwarden.Server.Sdk.RestrictedDependencies">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Bitwarden.Server.Sdk.RestrictedDependencies --version 0.1.0
                    
#r "nuget: Bitwarden.Server.Sdk.RestrictedDependencies, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Bitwarden.Server.Sdk.RestrictedDependencies@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Bitwarden.Server.Sdk.RestrictedDependencies&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Bitwarden.Server.Sdk.RestrictedDependencies&version=0.1.0
                    
Install as a Cake Tool

Bitwarden.Server.Sdk.RestrictedDependencies

About

Some types outgrow their purpose: a service that every team injects, a settings class that every team adds a property to. This package lets a repository freeze one, record every existing use in a committed baseline, and make the build reject new ones — so the type can only shrink while it is dissolved into owned replacements.

It ships a Roslyn analyzer, a source generator that emits the attributes, and the baseline document format so a repository can regenerate and diff its own baselines.

How to use

In your repository's root Directory.Build.props, turn the analysis on for the projects you want gated and point it at your committed baselines. RepoRoot must be the repository root. $(MSBuildThisFileDirectory) is the repository root only in that file; in a project file it is the project directory. Gate projects with a Condition on RestrictedDependencyAnalysis, or set it in each project file:

<PropertyGroup>
  
  <RestrictedDependencyAnalysis>true</RestrictedDependencyAnalysis>
  <RepoRoot>$(MSBuildThisFileDirectory)</RepoRoot>
  <RestrictedDependencyBaselinesPath>$(RepoRoot)baselines</RestrictedDependencyBaselinesPath>
  <RestrictedDependencyUpdateCommand>dotnet run --project tools/Baselines -- update</RestrictedDependencyUpdateCommand>
</PropertyGroup>

<ItemGroup Condition="'$(RestrictedDependencyAnalysis)' == 'true'">
  <PackageReference Include="Bitwarden.Server.Sdk.RestrictedDependencies" Version="..." IncludeAssets="analyzers;build" />
</ItemGroup>
Property Meaning
RestrictedDependencyAnalysis true to analyze this project. Anything else is skipped before a symbol is looked at.
RepoRoot Repository root, so paths in messages and AllowedPaths globs are repo-relative.
RestrictedDependencyBaselinesPath Directory of committed <Type>.json baselines.
RestrictedDependencyUpdateCommand Your regenerate command, quoted back in BW0013 and BW0015.

Then mark a type. The attributes are generated into every analyzed compilation, so there is nothing to reference:

[RestrictedDependency(AllowExistingUses = true, AllowNewUses = false,
    Tracking = "PM-43148", Owner = "@my-team",
    SealMembers = true, AllowedPaths = new[] { "src/Core/Services/Implementations/UserService.cs" })]
public interface IUserService
{
    [RestrictedDependency(Replacement = "IHasPremiumAccessQuery.HasPremiumAccessAsync")]
    Task<bool> CanAccessPremium(User user);
}

The (AllowExistingUses, AllowNewUses) pair decides how a use is treated:

Rule Meaning
(true, true) Tracked only. Counted in the baseline as a tracked row, never a diagnostic, and exempt from the shrink-only check.
(true, false) Gated. Baselined uses pass; anything beyond the baseline is an error.
(false, false) Forbidden. Every use is an error, baselined or not.
(false, true) Invalid; rejected by BW0015.

AllowedPaths is a full exemption: a matching file is neither gated, baselined nor observed for this type, so name the implementation file rather than its folder.

Restrict the types a restricted type inherits from too. Member rules, baselines and SealMembers cover only the members the restricted type declares, so a member it inherits from an unrestricted interface or base class is neither counted nor sealed, and code can reach it through that base type without touching the restricted one.

Exceptions

When a new use is unavoidable, put an owned, expiring exception on the site rather than widening the baseline. All three properties are required, and an invalid exception excepts nothing:

[RestrictedDependencyException(typeof(IUserService),
    Owner = "team-billing", Reason = "PM-12345", Expires = "2026-12-01")]
public class ProviderBillingController(IUserService userService);

Expires is advisory. BW0011 is a warning, and excepted sites are deliberately kept out of the baselines, so a shrink-only check never sees them either. Nothing chases an expiry for you. Take an exception because the owner and reason are worth recording, not because the date will be enforced.

Baselines

A baseline is one JSON document per restricted type, listing every use keyed by the documentation-comment id of the type that contains it, so it survives line moves and changes to the parameters or names of the constructors and methods that hold the uses. A member row keeps the restricted member's full id, parameters included, so changing that member's signature reports BW0014 on a sealed type, and BW0006 and BW0013 when the member's rule is gated. It carries no timestamp or commit hash: two runs on the same tree serialize byte-identically. Its type field is the name Compilation.GetTypeByMetadataName accepts: namespace, dot, nested names joined by +, arity suffix included.

A row whose rule is tracked-only carries "tracked": true, which BudgetRatchet.FindGrowth reads to exempt it: that total is expected to rise, so comparing it would fail the check for exactly the code the rule permits. The flag is written only when true, so a document with no tracked-only rows is byte-identical to one written without it. Your tool takes the value off the tracked property of each BW0017 usage row.

BudgetModel, BudgetEntry, BudgetRatchet.FindGrowth, DependencyUsageType and DependencyUsageTypeExtensions are the supported way to read, write and diff those documents from your own tooling. Your tool project references the package without the IncludeAssets trim, so the library is compiled in. The package is a development dependency, so it stays with the project that references it and does not flow to anything that depends on that project.

Your tool hosts the same analyzer the compiler does rather than reimplementing the scan. Take the analyzer from each project's Project.AnalyzerReferences rather than constructing it: the package ships the analyzer assembly only under analyzers/, so it is not on your tool's compile path. Enable BW0017 through CompilationOptions.WithSpecificDiagnosticOptions, supply the analyzer-config keys on AnalyzerConfigConstants (Analysis, RepositoryRoot, SeedTypes, joined by SeedTypeSeparator), and read the rows back off Diagnostic.Properties using ObservationConstants. AttributeConstants gives you the attribute name to discover restricted types by, and the attribute Text and HintName to compile in when the generator has not run. BW0017 is disabled by default, so it emits nothing in a real build. DiagnosticConstants carries the family's ids for your own configuration checks.

Code Roslyn classifies as generated is not analyzed: nothing in it is counted, baselined or reported. Roslyn treats a tree as generated when it opens with an <auto-generated> comment, has a name like *.g.cs or *.designer.cs, or is marked generated_code = true by an .editorconfig, and a symbol when it carries [GeneratedCode].

Diagnostics

ID Severity Fires when
BW0005 Error A constructor takes a restricted type at a site not in the baseline.
BW0006 Error A restricted member is used beyond its baselined count, or at all when forbidden.
BW0007 Error A restricted type is resolved from the service locator at a site not in the baseline.
BW0008 Error An implementing type is referenced directly.
BW0009 Error A restricted type escapes its consumer.
BW0010 Error An exception is missing Owner, Reason or a yyyy-MM-dd Expires.
BW0011 Warning An exception has expired.
BW0012 Error A [SuppressMessage] names one of these ids.
BW0013 Error A baseline entry no longer matches the code.
BW0014 Error A sealed type gained a member, nested type or property.
BW0015 Error An attribute or a baseline is invalid, missing or orphaned.
BW0016 Error One of these ids is configured below error; the setting has no effect.
BW0017 Hidden Never in a real build. The observation channel for baseline tooling.
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 105 10/27/2026