The core risk
When a teammate commits a package change and you pull it, Visual Studio may automatically restore the moment it detects the project file has changed — before you have had a chance to review what is being pulled in. A compromised transitive dependency could be on your machine before you knew it existed.
The two dangerous moments:
- First-time clone — you open the solution in VS and it restores everything.
- Subsequent pull — VS detects a changed
.csprojorDirectory.Packages.propsand restores in the background while you are still reading the diff.
In both cases the first restore of a newly downloaded package is safe — MSBuild
evaluates the project before downloading, so a brand-new package's
.targets are never part of that restore's evaluated graph. But once a
package is in the local cache, its .targets can execute on any subsequent
restore or build. Build is the more common trigger in practice. This
means there is always a window after the first restore where you can audit before the
next action — but only if you know to look.
One-time setup: disable VS auto-restore
This is the single most important setting.
It prevents VS from restoring automatically when it detects project file changes, giving you control over when restore happens.
- In Visual Studio: Tools → Options → NuGet Package Manager → General
- Uncheck "Automatically check for missing packages during build in Visual Studio"
- Click OK
VS will not restore on open or on project file changes. You become responsible for
running dotnet restore manually. Builds will fail if packages are missing
— that is intentional. A missing package means something changed and needs your
attention first.
One-time setup: the pre-build safety net
Even with auto-restore disabled, it is easy to forget to audit before building.
A Directory.Build.targets file at your solution root can automate this —
running --check before every build.
If the team does not use lock files, the sentinel cannot fire on lock file changes.
In that case the pre-build target should run --check unconditionally:
<!-- Directory.Build.targets — personal copy, do not commit if team isn't using this -->
<Target Name="PersonalAuditGate" BeforeTargets="Build">
<Exec Command="nuget-audit audit --check --path "$(SolutionPath)"" />
</Target>
Add Directory.Build.targets to your .gitignore so it stays
local to your machine and does not affect teammates' builds. If the team later adopts
the full zero-trust workflow, run nuget-audit init to replace this with
the lock-file-aware sentinel version — see the
zero-trust workflow guide.
Workflow 1 — first-time clone
Clone from the command line, preview and restore there, then open VS.
# 1. Clone via CLI — no restore happens here
git clone https://github.com/your-org/your-repo.git
cd your-repo
# 2. Preview what restore will pull in — no download yet
nuget-audit preview-restore --path .
# 3. Review the output carefully:
# - Any Untrusted packages?
# - Any recently published packages (within 14 days)?
# - Any packages you don't recognize?
# If anything looks wrong, stop here and raise it with the team.
# 4. If clean, restore
dotnet restore
# 5. Audit the restored graph to confirm it matches the preview
nuget-audit audit --path .
# 6. Now open in VS — packages are already on disk, nothing new will be pulled
By default, preview-restore runs an exact dotnet restore
into a temp directory — the resolved graph matches what would actually be installed.
Use --fast for a quicker approximate result when speed matters more than
exact accuracy. Either way, always follow preview with nuget-audit audit
after restoring (step 5) to confirm the final graph.
Workflow 2 — subsequent pulls
Always pull from the CLI with VS closed, or at minimum with auto-restore disabled (step above).
# 1. Pull via CLI (VS closed, or auto-restore disabled)
git pull
# 2. Check whether any package-related files changed in this pull
git diff HEAD@{1} -- "*.csproj" "*.props" "packages.lock.json" "Directory.Packages.props"
If nothing package-related changed
No action needed. Open VS and build normally. The pre-build sentinel (if configured) will confirm there is nothing to audit.
If packages.lock.json changed (team uses lock files)
The resolved graph changed. You know exactly what changed because the lock file diff shows it. Restore against the new lock file, then audit before building:
dotnet restore
nuget-audit audit --path .
If the pre-build sentinel is in place, it will run --check before your
first build anyway — this is your backup if you forget.
If .csproj or Directory.Packages.props changed (no lock files)
You can see which packages changed but not the full transitive impact. For each changed package, run preview first:
# For each added or version-bumped package identified in the diff:
nuget-audit preview-update <ChangedPackage> --version <NewVersion> --path .
# If preview looks clean for all changed packages, restore
dotnet restore --force-evaluate
# Audit after restore to confirm
nuget-audit audit --path .
Add --fast to preview-update for a quicker approximate
result. For packages from a private feed, --version is required and
credentials must be configured via NuGet.Config or the Azure Artifacts
Credential Provider.
If multiple packages changed and running preview for each is impractical, skip straight to restore + audit — the window between restore and build still exists and the pre-build sentinel enforces it.
Reading the audit output after a pull
Focus on these flags when auditing after pulling a teammate's changes:
| Flag | What it means | What to do |
|---|---|---|
Untrusted |
Package not on your trusted list | Look up the owner on nuget.org; check the project URL; add to
trustedPackages if legitimate |
VersionChanged |
A package you previously trusted has a new version | Re-review the package at the new version before accepting |
VerifiedUnknownOwner |
Prefix-verified but owner not in your trusted list | Check who the owner is and whether you recognize them |
| Recently published | Any package published within the threshold days | Note the package; consider waiting out the detection window before building against it |
PrivateFeed packages (your org's internal feed) are suppressed by default
— they are trusted by definition.
What about VS Git tools?
The VS Git tools (Git Changes, Git Repository window) run the same underlying git commands as the CLI. The risk is not the git operation itself — it is what VS does after it detects the changed project files.
With auto-restore disabled, VS Git tools are safe to use for pull, fetch, and checkout. The only operation that carries risk is letting VS trigger a restore without your knowledge. With the setting disabled and the pre-build sentinel in place, VS Git tools are fine for day-to-day use.
Summary
| Situation | Key action |
|---|---|
| First-time clone | preview-restore before opening in VS |
| Pull with package changes + lock files | Restore → audit before building |
| Pull with package changes, no lock files | preview-update per changed package → restore → audit |
| Pull with no package changes | Nothing extra needed |
| VS auto-restore | Disable it (Tools → Options → NuGet Package Manager) |
| Forgot to audit | Pre-build sentinel catches it before the build runs |
The zero-trust team workflow describes how to set up the full policy — lock files, VS pre-build enforcement, CI gates — so that the workflow is enforced automatically rather than relying on individual discipline.