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).

No activity

Activity on this issue will appear here.

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