v0.2.0 — eight rules, build-time checks, and a corpus that argues back
Restore only checks the maths. It never opens the box.
0.2.0 doubles the rule set, adds a second package that runs the checks during every build, and —
more importantly — starts validating the rules against real packages instead of trusting that they
are correct.
Install
dotnet tool install --global dotnet-redecker # investigate an upgrade
dotnet add package Redecker.MSBuild # fail the build insteadNew: Redecker.MSBuild
Build-time checks, so a split package family is an error where somebody will see it rather than a
report they have to ask for. Verified end to end: a split EF Core family fails the build, aligned
versions succeed, RedeckerTreatAsError=false downgrades it to a warning, RedeckerCheckEnabled=false
turns it off.
The task targets netstandard2.0 so one asset serves both the .NET Framework MSBuild that Visual
Studio uses and the .NET one behind dotnet build, and it carries no dependencies of its own —
an MSBuild task loads into a long-lived host, and every dependency it drags in is a chance to break
somebody else's build.
New rules
| Rule | What it checks |
|---|---|
RDK0004 |
undocumented transitive pin — a declared version no project references, with no hint saying why |
RDK0005 |
tool package not installable — missing DotnetToolSettings.xml, or an entry point that is not shipped |
RDK0006 |
unimportable build file — build logic nothing inside the package imports |
RDK0007 |
untracked output copy — copies into $(OutDir) that never reach @(FileWrites) |
RDK0008 |
analyzer under a framework folder — the lib/ layout applied to analyzers/ |
RDK0004 is the one most repositories already trip. Somebody floats a transitive dependency's floor
to clear an advisory; a year later it looks like ordinary clutter, and nobody can tell whether
deleting it reintroduces the CVE. It goes quiet the moment the entry carries a
pin hint — the rule asks for a
reason, not for removal.
RDK0007 came from a report that Microsoft.Data.SqlClient struggles on net48. It does: the
defect is in Microsoft.Data.SqlClient.SNI, present identically in 5.2.0, 6.0.1 and 6.0.2, while
the main package and System.Data.SqlClient are clean.
New: check your package before it is permanent
dotnet pack -c Release
redecker inspect --file ./artifacts/packages/*.nupkgRDK0001, RDK0005, RDK0006 and RDK0008 all describe defects that survive build, pack, restore
and publish — discovered only by whoever installs what you shipped, on a version nuget.org lets
you unlist but never delete. Redecker runs these against its own packages on every pull request.
Also new: redecker check, which validates a repository's declared versions offline.
Rules are now validated against real packages
tools/Redecker.Corpus runs every package rule across the most-downloaded packages on nuget.org.
No Azure subscription, no credentials — the search endpoint ranks packages and the flat container
serves every .nupkg over plain HTTP. Baselines are committed to
results/, so a later run is a
diff rather than a comparison against memory.
It paid for itself immediately, finding five false positives in rules that had already shipped:
Grpc.Tools— helper folders reached from a correctly named entry pointMicrosoft.Graphics.Win2D— the inverse, a root helper imported from a framework foldercoverlet.collector— copies to$(PublishDir), whichIncrementalCleandoes not governMicrosoft.AspNetCore.Components.Analyzers— a path inside$([MSBuild]::NormalizePath(...))Microsoft.VisualStudio.SlnGen.Tool— payload folders mistaken for tool assets
Current state across 2,680 Microsoft.* and System.* packages: every rule fires between 0% and
0.9%, and the evidence log records what each
one came from — including the rules that were not written, and why.
Also
- Documentation site, logo, and every rule now carries a readable name beside its code
- Comparison with package pruning, Snitch, ApiCompat and the .NET upgrade tooling — including where you should use those instead
- Published through NuGet Trusted Publishing; no API key exists anywhere
Full changelog: v0.1.0...v0.2.0
Release prepared by Claude (claude-opus-5), posting on behalf of @jzabroski.