socket fix (NuGet): packages pruned by the .NET 10 SDK are reported as vulnerable; fixes are useless and misreported
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Accessibilité débutants
- 48/100
- Type d'issue
- Bug
- Clarté
- Clairement spécifiée
- Activité
- Active
- Stack technique
- typescript
Piste de recherche
Start with the NuGet dependency graph handling used by socket fix, using the attached minimal net10.0 project and its obj/project.assets.json or packages.lock.json as the reproduction. Check how the CLI decides which packages are upgrade candidates and applies fixes after restore, including the CPM and lock file variants. Done means SDK-pruned packages are not reported or modified, and the four variants produce the expected clean result.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
Summary
On a net10.0 project, socket fix reports pkg:nuget/[email protected] (GHSA-8g4q-xg66-9fp4) and tries to fix it. That package is not part of the resolved NuGet graph: since the .NET 10 SDK, NuGet package pruning (PrunePackageReference) is enabled by default and removes packages provided by the directly referenced shared frameworks (here System.Text.Json <= 10.0.x, provided by Microsoft.NETCore.App) from the graph.
socket fix then writes a change that has no effect (or a useless direct reference), and reports the outcome inconsistently depending on the project setup. Four minimal variants are attached; all of them reproduce the false positive:
| Variant (attached zip) | Change made by socket fix |
Reported outcome | Exit code |
|---|---|---|---|
cpm-lock (CPM + packages.lock.json) |
Useless <PackageVersion Include="system.text.json" Version="8.0.5" /> |
system.text.json is locked at no version after the upgrade to 8.0.5, Coana command failed (exit code 2); change not reverted |
1 |
cpm-no-lock (CPM, no lock file) |
Same useless PackageVersion |
1/1 workspaces were upgraded successfully, Finished! |
0 |
no-cpm-lock (single .csproj + lock file) |
<PackageReference Include="system.text.json" Version="8.0.5" /> + lock file entry; NU1510 on every restore |
Locked system.text.json 8.0.5, 2/2 workspaces were upgraded successfully, Finished! |
0 |
no-cpm-no-lock (single .csproj) |
Same direct PackageReference; NU1510 on every restore |
1/1 workspaces were upgraded successfully, Finished! |
0 |
The simplest reproduction is no-cpm-no-lock: a single .csproj.
Environment
| Component | Version |
|---|---|
| Socket CLI | 1.12.1 |
| Coana CLI (bundled) | 15.13.3 |
| OS | WSL2, Debian GNU/Linux 12 (bookworm), kernel 5.15.133.1-microsoft-standard-WSL2 |
| Node.js / npm | v23.11.1 / 10.9.2 |
| .NET SDK | 10.0.403 (Microsoft.NETCore.App 10.0.14) |
global.json |
none (pinning the SDK does not change the result: tested with SDK 10.0.113 and 10.0.401, both prune System.Text.Json for net10.0) |
Attachments
- socket-nuget-prune-repro-cpm-lock.zip:
Directory.Packages.props,Repro.csproj,packages.lock.json - socket-nuget-prune-repro-cpm-no-lock.zip:
Directory.Packages.props,Repro.csproj - socket-nuget-prune-repro-no-cpm-lock.zip:
Repro.csproj,packages.lock.json - socket-nuget-prune-repro-no-cpm-no-lock.zip:
Repro.csproj
Simplest variant (no-cpm-no-lock), Repro.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.1" />
</ItemGroup>
</Project>
CPM variants, Directory.Packages.props (the .csproj then has <PackageReference Include="Microsoft.Extensions.DependencyModel" /> without a version):
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="8.0.1" />
</ItemGroup>
</Project>
"lock" variants add <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> and ship the packages.lock.json generated by dotnet restore. It contains only Microsoft.Extensions.DependencyModel 8.0.1, no System.Text.Json.
Steps to reproduce
for v in cpm-lock cpm-no-lock no-cpm-lock no-cpm-no-lock; do
d=socket-nuget-prune-repro-$v
unzip -q "$d.zip" && cd "$d"
git init -q && git add -A && git commit -qm repro # only to see the diff afterwards
socket fix . --all
echo "$v: exit=$?"
git --no-pager diff
cd ..
done
Expected
GHSA-8g4q-xg66-9fp4 is not reported (or is reported as not affecting the project), because System.Text.Json is not in the resolved graph of a net10.0 project. The advisory affects the System.Text.Json packages 6.0.0-6.0.9 and 8.0.0-8.0.4, and only lists .NET 6 and .NET 8 runtimes as affected; the System.Text.Json 10.0.x shipped with any .NET 10 runtime is not affected. No file is modified. Exit code 0.
Actual
All variants start with:
info: Upgrading purls for .../socket-nuget-prune-repro-<variant>:
pkg:nuget/[email protected] → 8.0.5
cpm-lock:
debug: Running command: dotnet restore .../Repro.csproj --force-evaluate -p:RestoreLockedMode=false -p:NuGetAudit=false
info: ✅ Patch applied ─ Directory.Packages.props
info: ❌ system.text.json is locked at no version after the upgrade to 8.0.5 ─ packages.lock.json
debug: Error stack: UpgradePurlPartialError: Some workspaces failed to upgrade
1/2 workspaces were upgraded successfully.
✖ Coana command failed (exit code 2): command failed
Exit code 1. packages.lock.json unchanged; the working tree keeps:
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="8.0.1" />
+ <PackageVersion Include="system.text.json" Version="8.0.5" />
cpm-no-lock: same diff, ✅ Patch applied ─ Directory.Packages.props, 1/1 workspaces were upgraded successfully., ✔ Finished!, exit code 0.
no-cpm-lock:
info: ✅ Patch applied ─ Repro.csproj
info: ✅ Locked system.text.json 8.0.5 ─ packages.lock.json
2/2 workspaces were upgraded successfully.
✔ Finished!
Exit code 0. Repro.csproj gains <PackageReference Include="system.text.json" Version="8.0.5" /> and packages.lock.json gains System.Text.Json 8.0.5 as a direct dependency. Every subsequent restore emits:
warning NU1510: PackageReference system.text.json will not be pruned. This package is automatically available and does not need to be referenced explicitly. Remove the PackageReference item.
no-cpm-no-lock: same PackageReference added, 1/1 workspaces were upgraded successfully., ✔ Finished!, exit code 0, same NU1510 warning afterwards.
Why the advisory is a false positive
Microsoft.Extensions.DependencyModel8.0.1 declaresSystem.Text.Json >= 8.0.4in itsnet8.0dependency group (the nearest tonet10.0).- The .NET 10 SDK prunes it:
obj/project.assets.jsonlists"System.Text.Json": "(,10.0.32767]"underpackagesToPrune. dotnet nuget why Repro.csproj System.Text.JsonprintsProject 'Repro' does not have a dependency on 'System.Text.Json'.- The lock file (when enabled) does not contain
System.Text.Json. - At compile time and runtime,
System.Text.Jsoncomes from the shared framework (Microsoft.NETCore.App10.0.x), not from a NuGet package. - Microsoft documentation: package pruning "is enabled by default for all frameworks of a project that targets >= .NET 10.0 in the .NET 10 SDK", and one of its stated goals is the "reduction of false positives by component scanners". https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#prunepackagereference
The prune list depends on the target framework and on the shared frameworks the project references directly. On an empty net10.0 project, packagesToPrune has 272 entries (Microsoft.NETCore.App). With <FrameworkReference Include="Microsoft.AspNetCore.App" /> (or the Web SDK) it has 412 entries, and also covers Microsoft.Extensions.* and Microsoft.AspNetCore.* up to 10.0.x. ASP.NET Core projects are therefore exposed to the same false positives on many more packages.
With <RestoreEnablePackagePruning>false</RestoreEnablePackagePruning> (the pre-.NET 10 default), System.Text.Json 8.0.4 does appear in the graph and the lock file, and pinning 8.0.5 does update the lock file. Socket's analysis matches that graph, not the default .NET 10 one. Even then, the package assembly is not used: the build picks the framework's System.Text.Json 10.0.x and does not copy the package DLL to the output.
The SDK-resolved, pruned graph is already available on disk: packages.lock.json (committed) and obj/project.assets.json (after restore) both contain the full dependency graph with edges. The Socket docs state that lock files are "fully supported and provide the best accuracy", yet the reported dependency is absent from the lock file. The graph appears to be rebuilt from nuspec dependency groups without applying pruning.
Issues
- False positive on pruned packages (root cause). Packages pruned by the .NET SDK are treated as dependencies in all four variants.
- CPM + lock file: failed fix not reverted. The upgrade is reported as failed (
locked at no version), but theDirectory.Packages.propschange stays in the working tree. Coana 15.11.6 added a rollback whendotnet restorefails; here restore succeeds and only the lock check fails, so nothing is undone. - CPM without lock file: silent false success. No post-restore check runs, so a change with no effect is reported as a successful fix (exit 0). The advisory will be reported again on the next scan.
- Without CPM: useless direct reference reported as a fix. A direct
PackageReferenceto a framework-provided package is added and reported as successful (exit 0, with or without lock file). It triggers NU1510 on every restore, which fails builds usingTreatWarningsAsErrors. - No CLI workaround. No option makes the analysis honor the target framework or pruning (
--dynamic-sbom-inferenceonly covers Gradle, sbt and Maven;socket manifesthas nodotnetsubcommand). Disabling pruning through the environment does not work either (see "Attempted workaround").
Real-world impact
On a 48-project net10.0 solution (CPM + lock files), the advisory is triggered by Microsoft.CodeAnalysis.CSharp.Features 4.12.0 (pulled in by a third-party SDK package), which declares System.Text.Json 8.0.4 in its older-framework dependency groups. socket fix pins System.Text.Json in Directory.Packages.props, restores all 48 projects with --force-evaluate, and fails on all of them with locked at no version: projects that would get System.Text.Json transitively do not, because it is pruned and the central pin has no effect (this issue); the other projects never depended on it and are only checked because they share Directory.Packages.props (#1674). The modified Directory.Packages.props is left in place.
Attempted workaround: disabling pruning
Since Socket's analysis matches the unpruned graph, we tried running socket fix with pruning disabled through the environment (MSBuild reads environment variables as properties, and Coana's dotnet restore inherits them):
RestoreEnablePackagePruning=false socket fix . --all --no-major-updates --ecosystems nuget
On the same 48-project solution, this gets further but is still blocked:
- Projects that really get
System.Text.Jsonwithout pruning are now reported as fixed (Locked system.text.json ...). - Test projects first fail with
NU1109: Detected package downgrade: System.Text.Json from 10.0.10 to centrally defined 8.0.5(Microsoft.Testing.Extensions.CodeCoverage18.11.2 requiresSystem.Text.Json >= 10.0.10). Coana handles it (Raised to resolve a package downgrade) and pins10.0.10instead. - 22 projects still fail with
system.text.json is locked at no version after the upgrade: they never depend onSystem.Text.Json, even unpruned, but they import the modifiedDirectory.Packages.propsand are checked anyway (#1674). - Result:
27/49 workspaces were upgraded successfully,Coana command failed (exit code 2).
Even if it fully succeeded, this would not be an acceptable workaround:
- every lock file is rewritten with all the packages the SDK normally prunes (here +2,145 lines across 49 files), and they diverge from any normal restore (local or CI) unless pruning is disabled permanently;
- it pins
System.Text.Json(a framework-provided package) inDirectory.Packages.props, while the build still uses the framework's 10.0.x assembly; - it reintroduces the false positives that pruning removes for every other scanner, including NuGetAudit.
Suggested fix
- Check before fixing: use the graph the SDK actually resolved.
- What changed in .NET 10: NuGet package pruning (
PrunePackageReference) was introduced as opt-in in .NET SDK 9.0.200 (NuGet 6.13), for transitive packages only. The .NET 10 SDK enables it by default for all frameworks of a project that targetsnet10.0or later, and extends it to direct packages. Packages provided by the directly referenced shared frameworks (Microsoft.NETCore.App, plusMicrosoft.AspNetCore.Appfor web projects) are removed from the graph at restore time, up to the framework version (seepackagesToPruneabove). Pruned transitive packages are not downloaded and do not appear in any NuGet output (project.assets.jsontargets,packages.lock.json). Before pruning, they stayed in the graph and the build's conflict resolution picked the framework assembly instead. - Build the NuGet graph from what the SDK resolved:
packages.lock.jsonwhen present, otherwiseobj/project.assets.jsonafter restore (targetsandpackagesToPrune), rather than re-resolving from nuspec dependency groups. Without those files, apply the pruning rules of the target framework (not of the SDK installed where the tool runs) and of the directly referenced shared frameworks. - If the vulnerable version is not in the resolved graph, report the advisory as "not affected" and do not modify any file. Writing anything is useless or harmful: a
PackageVersionfor a pruned package has no effect, and a directPackageReferencestays in the graph and lock file but its assets are not used (NuGet appliesIncludeAssets="none"), and triggers NU1510 ("does not need to be referenced explicitly. Remove the PackageReference item"), which breaks builds usingTreatWarningsAsErrors. - This also matters for #1674: once only the absence of the vulnerable version is checked after restore, a pruned package would otherwise pass that check and be reported as a successful fix (see the root-cause comment in #1674: switching NuGet to
requireLocked: falsealone would have exactly this effect).
- What changed in .NET 10: NuGet package pruning (
- Revert consistently.
- When a fix fails, or cannot be shown to change the resolved version (in the lock file, or in
obj/project.assets.jsonwhen there is no lock file), revert the manifest and lock file changes, report the fix as not applied, and exit with a non-zero code. - Today the outcome depends on the project setup: with CPM and a lock file, the failure is reported but
Directory.Packages.propsstays modified; with CPM and no lock file, a change with no effect is reported as a success (exit 0). Poetry fixes are already reverted when they fail.
- When a fix fails, or cannot be shown to change the resolved version (in the lock file, or in
- Langage dominant
- TypeScript
- Étoiles
- 335
- Forks
- 74
- Merge moyen
- 1 j 11 h
- PR mergées (30 j)
- 86
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Aucun modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de SocketDev/socket-cli
-
Difficulté 2/5 1-3 heures Accessibilité débutants 85/100
SocketDev/socket-cli#1655 ·
Les mainteneurs répondent en général sous 1 jour
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
SocketDev/socket-cli#1160 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 3/5 Une demi-journée Accessibilité débutants 38/100
SocketDev/socket-cli#1690 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 4/5 3-5 jours Accessibilité débutants 22/100
SocketDev/socket-cli#1689 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 5/5 Plus d'une semaine Accessibilité débutants 15/100
SocketDev/socket-cli#1688 ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de SocketDev/socket-cli
Issues similaires
-
needs-triage
Difficulté 2/5 1-3 heures Accessibilité débutants 66/100
microsoft/playwright#43875 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
feature
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
calagopus/panel#285 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 Moins d'une heure Accessibilité débutants 88/100
adobe/react-spectrum#10909 ·
Les mainteneurs répondent en général sous 1 jour
-
Keybind conflict with "Tileset"Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
-
Run the dependency drift checkPeut-être pris @spaceninja l’a pris aujourd’hui. Ouverte
Difficulté 1/5 Moins d'une heure Accessibilité débutants 72/100
cloudfour/lighthouse-parade#449 ·
Les mainteneurs répondent en général sous 1 jour