[agent] Found by the scheduled NuGet / dotnet bug-hunt routine (ledger #320).
Summary
For a legacy packages.config project whose nuget.config sets <add key="repositoryPath" value="lib/pkgs" />, nuget.exe restore extracts the package both into lib/pkgs/<Id>.<Version>/ (the copy MSBuild HintPaths reference) and into the user's global packages folder. Agent-mode socket-patch apply never reads repositoryPath. It only looks at <cwd>/packages/, then ~/.nuget/packages, so it patches %USERPROFILE%\.nuget\packages\newtonsoft.json\13.0.3\ and prints 1 of 1 targeted patch applied, exit 0. lib/pkgs/Newtonsoft.Json.13.0.3/ stays unpatched.
Unlike #397, this reproduces with a cold default cache, because nuget.exe's packages.config restore fills the global folder itself. So every repositoryPath user hits it, and it can't be fixed by honouring obj/project.assets.json (packages.config projects don't have one).
Impact
Silent false success, since the built binaries reference the unpatched lib/pkgs copy. It also mutates the user-wide cache. repositoryPath is the standard way for packages.config solutions to relocate packages/ (e.g. into a shared lib/ or ..\packages).
Repro (Windows, nuget.exe 7.9.0.83, main f6b7fb9)
mkdir -p repro/App && cd repro
cat > App/packages.config <<'X'
<?xml version="1.0" encoding="utf-8"?>
<packages><package id="Newtonsoft.Json" version="13.0.3" targetFramework="net472" /></packages>
X
# minimal legacy App/App.csproj + App.sln referencing it (see probe workflow), then:
cat > nuget.config <<'X'
<?xml version="1.0" encoding="utf-8"?>
<configuration><config><add key="repositoryPath" value="lib/pkgs" /></config></configuration>
X
nuget.exe restore App.sln
# stage .socket/manifest.json + blobs patching LICENSE.md of pkg:nuget/[email protected]
socket-patch apply --offline # "1 of 1 targeted patch applied", rc 0
grep -c MARKER lib/pkgs/Newtonsoft.Json.13.0.3/LICENSE.md # 0 (project copy)
grep -c MARKER "$USERPROFILE/.nuget/packages/newtonsoft.json/13.0.3/LICENSE.md" # 1 (wrong copy)
The full script is in the probe workflow: https://github.com/SocketDev/socket-patch/actions/runs/36792188195
Expected vs actual
- Expected: agent
apply patches the package copy the project actually consumes. For packages.config that's the repositoryPath folder (NuGet's documented relocation of packages/). docs/ecosystems.md lists NuGet agent mode as in-place patching of the installed package, and the README's legacy-layout support covers packages/<Id>.<Version>. At minimum it should refuse rather than claim success.
- Actual: it patches
~/.nuget/packages and reports success.
OS × version matrix
| OS image |
nuget.exe |
no repositoryPath (packages/) |
repositoryPath=lib/pkgs, cold default cache |
repositoryPath=lib/pkgs, warm default cache |
| windows-2022 |
7.9.0.83 |
pass (packages/ patched) |
fail: wrong copy patched, rc 0 |
fail: wrong copy patched, rc 0 |
| windows-latest (win25) |
7.9.0.83 |
pass |
fail |
fail |
Linux/macOS not tested (nuget.exe needs Mono, which isn't on the current runner images). The crawler logic is OS-independent.
Suspect code
crates/socket-patch-core/src/crawlers/nuget_crawler.rs:68-72: only <cwd>/packages is considered for the legacy layout. repositoryPath (and a packages/ next to a solution that isn't the scan root) is never resolved.
nuget_crawler.rs:74-78: the default global folder is then added as a source root, and merge_first_wins (crates/socket-patch-cli/src/ecosystem_dispatch.rs:147) picks it.
Not a regression as far as I can tell: the same crawler order exists in v4.0.0 (#397 reproduces identically there). Related: #397 (the same first-wins wrong copy for PackageReference globalPackagesFolder / RestorePackagesPath).
[agent] Found by the scheduled NuGet / dotnet bug-hunt routine (ledger #320).
Summary
For a legacy
packages.configproject whosenuget.configsets<add key="repositoryPath" value="lib/pkgs" />,nuget.exe restoreextracts the package both intolib/pkgs/<Id>.<Version>/(the copy MSBuildHintPaths reference) and into the user's global packages folder. Agent-modesocket-patch applynever readsrepositoryPath. It only looks at<cwd>/packages/, then~/.nuget/packages, so it patches%USERPROFILE%\.nuget\packages\newtonsoft.json\13.0.3\and prints1 of 1 targeted patch applied, exit 0.lib/pkgs/Newtonsoft.Json.13.0.3/stays unpatched.Unlike #397, this reproduces with a cold default cache, because nuget.exe's packages.config restore fills the global folder itself. So every
repositoryPathuser hits it, and it can't be fixed by honouringobj/project.assets.json(packages.config projects don't have one).Impact
Silent false success, since the built binaries reference the unpatched
lib/pkgscopy. It also mutates the user-wide cache.repositoryPathis the standard way for packages.config solutions to relocatepackages/(e.g. into a sharedlib/or..\packages).Repro (Windows, nuget.exe 7.9.0.83, main
f6b7fb9)The full script is in the probe workflow: https://github.com/SocketDev/socket-patch/actions/runs/36792188195
Expected vs actual
applypatches the package copy the project actually consumes. For packages.config that's therepositoryPathfolder (NuGet's documented relocation ofpackages/). docs/ecosystems.md lists NuGet agent mode as in-place patching of the installed package, and the README's legacy-layout support coverspackages/<Id>.<Version>. At minimum it should refuse rather than claim success.~/.nuget/packagesand reports success.OS × version matrix
repositoryPath(packages/)repositoryPath=lib/pkgs, cold default cacherepositoryPath=lib/pkgs, warm default cacheLinux/macOS not tested (nuget.exe needs Mono, which isn't on the current runner images). The crawler logic is OS-independent.
Suspect code
crates/socket-patch-core/src/crawlers/nuget_crawler.rs:68-72: only<cwd>/packagesis considered for the legacy layout.repositoryPath(and apackages/next to a solution that isn't the scan root) is never resolved.nuget_crawler.rs:74-78: the default global folder is then added as a source root, andmerge_first_wins(crates/socket-patch-cli/src/ecosystem_dispatch.rs:147) picks it.Not a regression as far as I can tell: the same crawler order exists in v4.0.0 (#397 reproduces identically there). Related: #397 (the same first-wins wrong copy for PackageReference
globalPackagesFolder/RestorePackagesPath).