What policy enforcement means
TrustConfig.json is not just a personal trust list — it is the
organizational record of which packages and publishers are approved for use. When it
is checked into source control and the audit runs in CI, any package not on the
approved list causes the build to fail. That is policy enforcement.
This means you can use TrustConfig.json to express organizational
decisions beyond security:
- Which publishers are pre-approved across all projects
- Which specific package versions have been reviewed and accepted
- Which packages are blocked from use (by omission from the trust list)
- Which packages are being sunset (by removing them from the approved list)
The enforcement is automatic — developers do not need to remember to check. The CI gate and VS pre-build target enforce it on every build and every PR.
The three enforcement levels
Policy is enforced at three points, each catching a different class of violation:
| Level | Mechanism | What it catches |
|---|---|---|
| Developer machine | Directory.Build.targets pre-build target |
Unapproved packages before the first local build after a restore |
| CI/CD | nuget-audit audit --check as a required status check |
Any PR or merge that introduces an unapproved package |
| Lock files | packages.lock.json + RestoreLockedMode=true |
Unauthorized changes to the resolved graph — restore fails if the lock is out of date |
All three levels together mean a package cannot enter the project silently. A developer adding an unapproved package must edit the project file (caught by the lock file on restore), restore (generating a new lock file that differs from the committed one), and build (where the pre-build target fires). The CI gate catches anything that slips through.
Run nuget-audit init --path <dir> to create
Directory.Build.targets and a default TrustConfig.json.
See the zero-trust workflow guide for full setup
instructions.
Forcing package migrations
Removing a package or owner from TrustConfig.json causes CI to fail on
any project that still uses it. This is a controlled mechanism for forcing teams to
migrate away from deprecated or unwanted packages.
Example: migrating off a deprecated package
// Before: Newtonsoft.Json is in trustedPackages
{
"trustedOwners": ["Microsoft", "dotnetfoundation"],
"trustedPackages": [
{ "id": "Newtonsoft.Json", "version": "13.0.3" }
]
}
// After: remove the entry to block further use
{
"trustedOwners": ["Microsoft", "dotnetfoundation"],
"trustedPackages": []
}
Any project that still references Newtonsoft.Json will now fail CI with
an Untrusted status. The failure tells the developer exactly which package
needs attention — migrate off it, or re-approve it if the decision is reversed.
Removing an entry blocks builds immediately. Give teams notice before committing
the change to TrustConfig.json, and document the planned removal in your
project's decision log.
Example: moving an owner from broadly trusted to per-version review
Removing an owner from trustedOwners does not immediately block existing
pinned versions in trustedPackages. Any package from that owner that is
not explicitly pinned will be flagged as VerifiedUnknownOwner
and require review.
This is useful when you want to increase scrutiny of a publisher's packages without immediately blocking builds — moving from "broadly trusted" to "reviewed per version."
Compliance and architectural controls
The same mechanism supports organizational rules beyond security:
Prefer internal or forked packages
If your organization maintains internal forks or mirrors of public packages, keep the
public package out of TrustConfig.json. Any project that accidentally
references the public version will fail CI, directing developers to the internal
package instead.
Enforce System.* over third-party equivalents
For functionality now covered by the .NET runtime — System.Text.Json
replacing Newtonsoft.Json, Microsoft.Extensions.* replacing
earlier third-party equivalents — remove the third-party package from the trust list.
Add a comment to TrustConfig.json documenting the architectural decision
so the reason is visible when someone hits the CI failure.
Compliance-driven exclusions
If legal or compliance requirements prohibit specific packages or licenses, omitting
them from TrustConfig.json creates an automatic enforcement gate. When
a developer adds such a package — intentionally or as a transitive dependency — the
CI failure names the package and owner, providing an audit trail.
Brownfield adoption
Introducing policy enforcement into an existing project requires a one-time approval pass to baseline the current state before enforcement begins.
# 1. See everything currently in the graph and its trust status
nuget-audit audit --all --path .
# 2. Generate copy-paste entries for TrustConfig.json
nuget-audit audit --package-list --include-existing --path .
# 3. Edit TrustConfig.json: add owners to trustedOwners,
# add specific versions to trustedPackages
# 4. Re-run until clean
nuget-audit audit --path .
# 5. Lock the graph and commit
dotnet restore --force-evaluate
git add packages.lock.json TrustConfig.json
git commit -m "Baseline NuGet trust policy"
After the baseline commit, CI enforcement begins. Any new package added to the project
must be approved in TrustConfig.json before CI passes.
--include-existing for a full inventory
--package-list --include-existing shows all packages grouped by trust
status — useful for the initial review pass on a large project with many existing
dependencies.
When policy breaks down
The most common ways the enforcement model fails, and what to do about them:
| Failure mode | How it happens | Prevention |
|---|---|---|
| Lock file not committed | Developer runs --force-evaluate without committing the updated lock file |
CI restore fails if the committed lock file does not match; the advisory check flags missing lock files |
| CI check not required | Audit runs in CI but is not a required status check — PRs can merge even if it fails | Make nuget-audit audit --check a required status check in branch protection rules |
| Pre-build target missing | Directory.Build.targets not created or not at solution root |
--check warns on this condition; run nuget-audit init to create it |
TrustConfig.json grows without review |
Developers add entries to unblock CI without reviewing the package | Require a designated reviewer on TrustConfig.json changes via
CODEOWNERS |
| Multiple solutions, config in wrong location | TrustConfig.json not found at expected path |
Use --trust-config to specify the path explicitly in the CI
command and Directory.Build.targets |
Add a CODEOWNERS entry requiring a designated reviewer for any PR
that modifies TrustConfig.json. This prevents the trust list from
drifting without oversight.
# .github/CODEOWNERS
TrustConfig.json @your-org/security-reviewers
See the zero-trust workflow guide for the complete setup sequence — lock files, CI gates, VS pre-build enforcement, and the ongoing package update workflow.