Skip to content

Since #446, scan -g --mode agent then rollback -g from a vendored NuGet project reverts the project's patched package in the shared global packages folder; the locked restore stays "up-to-date" and VEX keeps attesting #489

Description

[agent] Found by the scheduled NuGet / dotnet bug-hunt routine (ledger #320).

Summary

#446 (551c362, "Keep -g runs off the project's hosted/vendored state") made global runs ignore the project's vendor ledger: scan -g --mode agent / apply -g now patch "the global copy of a purl the project vendors", and rollback -g rolls that copy back. That works for npm, where the global copy is separate from node_modules. For NuGet it isn't separate. A PackageReference project restores into the global packages folder (~/.nuget/packages, or NUGET_PACKAGES), so the "global copy" is the vendored project's own installed copy.

From a vendored NuGet project on main:

  1. scan -g --mode agent --yes finds Newtonsoft.Json@13.0.3 in the global packages folder. Those bytes are already patched, because the project's locked restore extracted them from the vendored nupkg. The scan reports applied: 1 and writes an agent record to the project's .socket/manifest.json. Before Fix -g touching the cwd project's state (#436, #445) #446 it was skipped as vendored_ownership_retained.
  2. rollback -g --yes then restores the upstream bytes into ~/.nuget/packages/newtonsoft.json/13.0.3/ (rolledBack: 1, exit 0). It leaves the vendored wiring and ledger alone, which is what Fix -g touching the cwd project's state (#436, #445) #446 intended.
  3. The project's dotnet restore --locked-mode says "All projects are up-to-date for restore". The lock still pins the vendored nupkg's contentHash, the .nupkg.sha512 sidecar still matches, and NuGet never re-extracts. So the project builds unpatched, with exit 0 everywhere.
  4. vex --product pkg:nuget/app@1.0.0 still emits not_affected "Patched via Socket patch … (vendored)". It warns that the live tree differs and says to "re-run your package manager's install to resync it", but the restore in step 3 is exactly that, and it doesn't resync.

Impact

A vendored NuGet project gets silently unpatched by a global agent run plus its rollback, both started from the repo root. That's a normal way to manage machine-wide patches, and with a project-level .socket/ present it's the documented place to run it. CI and dev boxes share one global packages folder. Nothing fails and VEX keeps attesting.

Repro (Linux, dotnet SDK 8.0.131, main 9d718cf)

I used a scratch copy of crates/socket-patch-cli/tests/e2e_nuget_dotnet_build.rs, keeping its wiremock Backend stand-in and the real nuget.org fixture restore:

fixture: app.csproj (net8.0, RestorePackagesWithLockFile, Newtonsoft.Json 13.0.3) + nuget.org-only nuget.config
socket-patch scan --mode vendored --vendor-source service --yes --api-url <backend> ...   # exit 0, lock re-pinned, feed wired
NUGET_PACKAGES=<store> dotnet restore --locked-mode     # store/newtonsoft.json/13.0.3/LICENSE.md = PATCHED
NUGET_PACKAGES=<store> socket-patch scan -g --mode agent --json --yes --api-url <backend> ...
    # "applied": 1  (main) | "skipped": 1, vendored_ownership_retained (c7af4df, the parent of #446)
    # main also writes .socket/manifest.json with an agent record for pkg:nuget/Newtonsoft.Json@13.0.3
# (stage the before-blob in .socket/blobs, or run rollback online against a server that serves it)
NUGET_PACKAGES=<store> socket-patch rollback -g --json --yes --offline
    # main: "rolledBack": 1, exit 0 -> store LICENSE.md = PRISTINE; vendored wiring + ledger untouched
NUGET_PACKAGES=<store> dotnet restore --locked-mode     # exit 0, "All projects are up-to-date", store stays PRISTINE
socket-patch vex --offline --product pkg:nuget/app@1.0.0 -o v.json
    # 1 statement, not_affected, "Patched via Socket patch 4f4f… (vendored)" + resync warning

Reproduced 3 times on 9d718cf. The vex result was checked before the rollback (attested, bytes patched) and after it (still attested, bytes pristine).

Expected vs actual

  • Expected: the Fix -g touching the cwd project's state (#436, #445) #446 commit message and the README say a global run leaves "the project's state alone". For NuGet the global packages folder copy is the project's install of a vendored package. The pre-Fix -g touching the cwd project's state (#436, #445) #446 skip (vendored_ownership_retained) protected it, and so should -g, or at least the copy whose bytes match the vendored artifact. An agent apply -g that finds bytes already at afterHash also shouldn't report applied and take ownership of the record. CLI_CONTRACT.md / README VEX: VEX attests only patches that are actually applied to the product.
  • Actual: -g takes over and later reverts the project's installed copy. The restore can't notice, because only the extracted files changed, not the nupkg or its sha512. VEX keeps attesting.

OS × version

OS SDK main 9d718cf c7af4df (before #446)
Linux 8.0.131 silently unpatched (3/3) agent leg skipped (vendored_ownership_retained). rollback -g instead unwound the vendored wiring (#445), so the locked restore failed loudly with NU1403

The layout is the same on macOS and Windows (~/.nuget/packages, %USERPROFILE%\.nuget\packages), but I haven't run it there yet.

First bad commit

551c362 (#446). Before it, the same sequence was loud: #445's wiring unwind led to NU1403. Now it's silent.

Suspect code

  • crates/socket-patch-cli/src/commands/scan/mod.rs:1652-1658 (vendor_owned_purls is emptied under -g)
  • crates/socket-patch-cli/src/commands/apply.rs:1720 (same rule for apply -g)
  • crates/socket-patch-cli/src/commands/rollback.rs:1121
  • crates/socket-patch-cli/src/commands/mod.rs:44 (project_state_in_scope treats "global" and "project" installs as disjoint, which doesn't hold for NuGet's global packages folder)

Related, but a different trigger: #352 (a warm folder shadowing a vendored patch) and #450 (cross-scope rollback).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedbugSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentpm:nugetNuGet / dotnetpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions