Repository navigation
Agent-mode NuGet apply patches ~/.nuget/packages instead of the project's configured globalPackagesFolder / RestorePackagesPath, reports success, and VEX attests not_affected #397
Description
Activity
- addedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:nugetNuGet / dotnetNuGet / dotnet
on Sep 30, 2026 - added a commit that references this issue
on Sep 30, 2026 mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Shares root cause with #398:
NuGetCrawler::get_nuget_package_paths(crates/socket-patch-core/src/crawlers/nuget_crawler.rs:68-86) hard-codes the package folders (<cwd>/packages, then~/.nuget/packages/NUGET_PACKAGES) and never resolves the folder NuGet is actually configured to use (globalPackagesFolder/repositoryPathin nuget.config,RestorePackagesPath), so the default cache wins under first-match-per-PURL. Will be fixed together.Triage: priority:p3 (NuGet). Not a duplicate of #352 (that one is about vendored/hosted builds being shadowed by a warm cache, a different code path).
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Re-triage on main
2463257(v5 consolidation, #277): still reproduces. The originalsrc/App+ projectnuget.configglobalPackagesFolderrepro patches~/.nuget/packages(rc 0) and leaves.nuget/packagesunpatched, andvexsaysnot_affected.New information: the same root cause also breaks global mode (
-g). WhenglobalPackagesFolderis set in the user-level config (~/.nuget/NuGet/NuGet.Config,%APPDATA%\NuGet\NuGet.Config),dotnet nuget locals global-packages -land every restore use that folder, but:scan -gcrawls only~/.nuget/packages(stale). Packages that exist only in the real folder (herehumanizer.core@2.14.1) are never sent to the patch API or listed.apply -gpatches the~/.nuget/packagescopy (Using NuGet packages at: ~/.nuget/packages, rc 0). The copy NuGet actually uses stays unpatched.vex -gattestsnot_affectedfor it.
cat > ~/.nuget/NuGet/NuGet.Config <<'X' <?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources><add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /></packageSources> <config><add key="globalPackagesFolder" value="/tmp/gpf" /></config> </configuration> X dotnet nuget locals global-packages -l # global-packages: /tmp/gpf # restore a project with Newtonsoft.Json 13.0.3 + Humanizer.Core 2.14.1 (default cache already holds Newtonsoft only) # stage a manifest for pkg:nuget/newtonsoft.json@13.0.3 (LICENSE.md), then from a non-project dir: socket-patch apply -g --offline # rc 0, "applied" grep -c SOCKET_MARKER /tmp/gpf/newtonsoft.json/13.0.3/LICENSE.md # 0 grep -c SOCKET_MARKER ~/.nuget/packages/newtonsoft.json/13.0.3/LICENSE.md # 1 socket-patch vex -g --offline --product pkg:nuget/x@1.0.0 # status not_affected
Reproduced 2/2 locally (Linux, SDK 8.0.131) and in 8/8 probe cells, run https://github.com/SocketDev/socket-patch/actions/runs/36820498426: ubuntu 6.0.x / 9.0.x / 10.0.x, macos 8.0.x / 9.0.x, windows 8.0.x / 9.0.x / 10.0.x. Every cell:
scan -gmisses the configured folder,apply -gpatches the default folder, VEXnot_affected.The code path is
nuget_crawler.rs:41-50(the global branch returnsnuget_home(), which honours onlyNUGET_PACKAGES). A fix for this issue should read the effectiveglobalPackagesFolderin global mode too.
Generated by Claude Code
[agent] Found by the scheduled NuGet / dotnet bug-hunt routine (ledger #320).
Summary
When a project moves its NuGet global packages folder, either with
<add key="globalPackagesFolder" value=".nuget/packages" />innuget.configor with<RestorePackagesPath>inDirectory.Build.props/ the csproj, agent-modeapplypatches the wrong copy:applypatches the user-wide~/.nuget/packages/<id>/<ver>/(%USERPROFILE%\.nuget\packageson Windows) and reports1 of 1 targeted patch applied, exit 0. The project's own folder, the only onedotnet buildresolves from (obj/project.assets.json→packageFolders), stays unpatched.socket-patch vexthen emitsnot_affected/inline_mitigations_already_existfor the unpatched product. As a side effect, every other project on the machine that uses the default folder gets its bytes modified.App.sln+src/App/App.csprojlayout):applyfails with "matched no installed package" (exit 1), becausediscover_paths_from_assetsreadsobj/project.assets.jsononly at the root and one level down.Root cause (suspected)
NuGetCrawler::get_nuget_package_paths(crates/socket-patch-core/src/crawlers/nuget_crawler.rs:74-86) lists source roots in the order<cwd>/packages, thennuget_home()(default~/.nuget/packages, onlyNUGET_PACKAGEShonoured), then thepackageFoldersread fromobj/project.assets.json. The dispatcher keeps only the first match per PURL (merge_first_wins,crates/socket-patch-cli/src/ecosystem_dispatch.rs:147, deliberate for NuGet). So whenever the default folder also holds the package, the project's real, configured folder is never patched. Separately,discover_paths_from_assets(nuget_crawler.rs:483) searches only one level deep, so asrc/<Project>/obj/project.assets.jsonis never seen. NeitherglobalPackagesFoldernorRestorePackagesPathis read anywhere.Impact
A silent false "patched", plus a false VEX
not_affectedattestation, for any repo that pins a repo-local packages folder. That's a common hermetic-CI pattern. Agent mode also mutates a shared user-wide cache the project doesn't use.Repro (Linux, dotnet SDK 8.0.131, main
f6b7fb9)RestorePackagesPathbehaves the same:Directory.Build.propswith<RestorePackagesPath>$(MSBuildThisFileDirectory)pkgs</RestorePackagesPath>and a root-level csproj.NuGetPackageRoot=<repo>/pkgs, yetapplypatches~/.nuget/packages.With a cold default cache and the
src/Applayout,applyexits 1 withThe targeted manifest patch matched no installed package. With the project one level down (App/App.csproj) and a cold default cache, it correctly patches.nuget/packages.Expected vs actual
applypatches the copy NuGet actually resolves for the project, which is thepackageFoldersrecorded inobj/project.assets.json/ the configuredglobalPackagesFolder/RestorePackagesPath. Failing that, it refuses. README'sapplysection and the VEX contract (a statement only for a patch that is actually applied) both require this. docs/ecosystems.md documents NuGet agent mode as in-place patching of the installed package, with no caveat about relocated package folders.not_affected.OS × SDK matrix (probe run https://github.com/SocketDev/socket-patch/actions/runs/36791969674)
src/AppprojectControl: an
App/project one level deep with a cold default cache → project copy patched (pass).First bad version
Not a regression: v4.0.0 (GitHub release binary) behaves identically.
Related but distinct: #338 (the same "wrong copy, success, VEX not_affected" shape for cargo
vendordirs), #352 (the vendored/hosted warm-cache shadowing).