Repository navigation
A user replace in go.work silently overrides the Socket go.mod replace: Go apply and vendor report success and VEX attests not_affected while the build links the user's target #393
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:goGo modulesGo modules
on Sep 30, 2026 mikolalysenko commented
on Sep 30, 2026 CollaboratorAuthorMore actions[agent] Triaged as priority:p2 (Go). Related to #391/#392 (Socket's go.mod replace isn't the one the build uses), but this covers vendored mode and a user go.work replace, which the
wiring_conflictgate ignores; not clustered until the fix for #391/#392 shows whether ago list -meffective-replace check covers it.
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Re-triage on main
2463257(v5 consolidation, #277): still reproduces. The setup is a go.work withuse .andreplace example.com/upstream v1.0.0 => ../fork. Agentapplyexits 0,go run .printsFORK,apply --checksays "in sync", andvexgivesnot_affected.
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Shares root cause with #458: the agent-mode Go redirect (
patch/redirect/golang_local.rs) never readsgo.work, so a go.workreplace(here) or a go.work that doesn'tusethe root (#458) makes Socket's root go.mod replace inert while apply,apply --checkand VEX still report it patched. Will be fixed together.
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] New information: hosted mode is affected too. Reproduced 2× on main
61cfb9b(Linux, go 1.24.7), with both the versioned (M v1.0.0 => ../fork) and the version-less (M => ../fork) go.work replace.The fixture is the shape of
e2e_golang_hosted_build.rs(get <uuid> --mode hostedagainst a local mock ofview/<uuid>+/patches/packagewith agoproxyoverride).go.workisuse .plusreplace example.com/upstream => ../fork.go run . # OUT: FORK socket-patch get $UUID --mode hosted --yes --json … # exit 0, "redirected": 1, "warnings": [] grep replace go.mod # replace example.com/upstream v1.0.0 => patch.socket.dev/gopatch/<uuid> v1.0.0-socketpatch.1 go run . # OUT: FORK (also FORK on a fresh day-2 machine) socket-patch vex … --product pkg:golang/example.com/consumer # exit 0, "status": "not_affected" (redirected)The hosted rewriter refuses a user-authored replace in go.mod ("User-authored conflicting replacements are preserved and the patch is refused", docs/ecosystems.md). It doesn't look at go.work, though, where the user's replace wins.
Also re-checked on
61cfb9b: the agentapplyvariant still reproduces (apply 0, build FORK,--check0, vexnot_affected).
Generated by Claude Code
mikolalysenko commented
on Oct 2, 2026 CollaboratorAuthorMore actions[agent] Shares root cause with #458, #531: the Go redirect never reads the enclosing
go.work. #531 adds a third symptom: applying in a second workspace member writes a conflicting per-member replace, which breaks the workspace build. Will be fixed together.
Generated by Claude Code
mikolalysenko commented
on Oct 2, 2026 CollaboratorAuthorMore actions[agent] New variant from the Go modules bug-hunt routine (ledger #317), on main
61cfb9b, go 1.24.7, agent mode.The same override happens when the workspace file lives outside the project and is selected with
GOWORK=<path>. socket-patch never readsGOWORK, so even the go.work-aware VEX discovery can't see it:# consumer at $C patched with `socket-patch apply` (go.mod replace => ./.socket/go-patches/...) printf 'go 1.21\n\nuse %s\n\nreplace example.com/upstream v1.0.0 => %s\n' $C /some/other/upstream > $W/ws/go.work GOWORK=$W/ws/go.work go run . # OUT: PRISTINE GOWORK=$W/ws/go.work socket-patch apply --check # exit 0, "in sync" GOWORK=$W/ws/go.work socket-patch vex --product … # status not_affected
Without the user replace in the external go.work,
GOWORK=<path>builds PATCHED (pass).GOWORK=offwith a root go.work that doesn'tuse .also builds PATCHED, and apply and--checkpass.
Generated by Claude Code
mikolalysenko commented
on Oct 3, 2026 CollaboratorAuthorMore actions[agent] Re-triage from the Go modules bug-hunt routine (ledger #317): this still reproduces on main
045d7ecin agent mode, and it now also reproduces on go 1.21.13 and 1.26.8 as well as 1.24.7. Withreplace example.com/upstream v1.0.0 => ../forkin go.work,applyexits 0, the build prints FORK,apply --checkexits 0, andvexwritesnot_affected.
Generated by Claude Code
- added and removed
on Oct 8, 2026 mikolalysenko commented
on Oct 9, 2026 CollaboratorAuthorMore actionsv5 triage: P2, not a release blocker. Retain Go workspace override handling at P2; resolve it as source-selection work after the basic release path.
This follows the maintainer's release scope: one normally completing CLI instance, prioritizing valid-lockfile patch/install behavior, compatibility, and actionable CLI UX.
[agent] Found by the scheduled Go modules bug-hunt routine (ledger #317).
Summary
In Go workspace mode, a
replaceingo.workoverrides anyreplacefor the same module in a member'sgo.mod. When the workspace already has a user-authoredreplace example.com/upstream [v1.0.0] => ../forkingo.work, both agent-modeapplyand vendored-modevendorstill write theirreplaceinto the rootgo.modand report the package patched. That replace is inert:go buildlinks../fork.apply --checkthen says "in sync", andvexattestsnot_affected(with the(vendored)marker in vendored mode).The same user replace in go.mod is correctly refused ("go.mod already has a user-authored
replace example.com/upstream v1.0.0=> ../fork; refusing to overwrite"), andcrates/socket-patch-core/src/vex/discover/golang.rs:65-72notes that "go.work replaces override go.mod replaces in Go", leaving cross-file precedence to the CLI'swiring_conflictgate. That gate never fires for a user (non-Socket) go.work replace.Impact
The patched bytes aren't in the build, yet every signal (apply exit 0, the
apply --checkCI gate, and OpenVEXnot_affected) says the CVE is mitigated. Committedgo.workfiles with local-fork replaces are common in monorepos.Repro (Linux, go 1.24.7, hermetic file GOPROXY,
GOFLAGSunset because workspace mode rejects-mod=mod)The fixture has the same shape as
tests/e2e_golang_build.rs:example.com/upstream@v1.0.0is"PRISTINE", and a hand-staged manifest plus blob patch it to"PATCHED", withsetup.manual: ["golang"].../forkis a local moduleexample.com/upstreamreturning"FORK".This reproduced in 3 fresh fixtures on main:
M v1.0.0 => ../fork), agentapply;M => ../fork), agentapply;socket-patch vendor(vendored mode). There,vendorexit 0 writesreplace … => ./.socket/vendor/golang/<uuid>/…, the build prints FORK, and vex exit 0 givesnot_affected (vendored).For contrast, the same line in go.mod makes
applyfail withuser-authored replace … refusing to overwrite(exit 1).Expected vs actual
go.work, because that is where Go resolves the replace in workspace mode. Failing that, apply, vendor andapply --checkshould report the redirect as overridden, andvexshould omit the patch. README (vex, step 2) says the attestation "only covers patches that are actually applied"; README line 1307 listsgo.workamong the golang files socket-patch reads.not_affected, and the build links the fork.OS × version
applyM v1.0.0 => ../forkapplyM => ../forkvendorM v1.0.0 => ../forkapplyThis is go.mod/go.work text logic, so it doesn't depend on the OS;
go.workexists since Go 1.18. Main isf6b7fb9. Hosted mode wasn't exercised (it needs the patch API, which the sandbox blocks).Suspect code
crates/socket-patch-core/src/vendor/go_mod_edit.rs:173ensure_replace_entry: the user-authored-replace refusal only readsgo.mod.crates/socket-patch-core/src/patch/redirect/golang_local.rs:468verify_go_redirect_stateandcrates/socket-patch-cli/src/commands/vex.rs:1333synthesize_go_patches: neither consultsgo.workreplaces.crates/socket-patch-core/src/vex/discover/golang.rs:65-72defers go.work-over-go.mod precedence to the CLIwiring_conflictgate, which doesn't cover a non-Socket go.work replace.Related: #392 (an inert replace from a build-graph mismatch), #391 (vex doesn't check the replace version).
Backlog review — 2026-10-08
Priority: P2 → P1. A go.work replacement makes the build use unpatched code while VEX attests the Socket replacement. Supported workspace resolution must be respected.