GeezerVison is showing github.com — chore(release): bump version to 4.0.0 by mikolalysenko · Pull Request #228 · SocketDev/socket-patch
Conversation
Stamps 4.0.0 into every packaging site (scripts/version-sync.sh) and
rolls CHANGELOG.md's [Unreleased] notes into the ## [4.0.0] section.
CI's release-readiness job verifies the chores on this PR; the Release
workflow re-verifies them before anything publishes.
Also new in the CHANGELOG: an intro at the top of the 4.0.0 section
describing the three patch-application modes (agent / vendored / hosted)
and their v4.0 ecosystem coverage, and the (BREAKING — lands in v4.0)
heading qualifiers trimmed to (BREAKING) now that this IS v4.0.
Validation (local, on this exact tree)
cargo test -p socket-patch-core: green (exit 0).
cargo test -p socket-patch-cli: green except e2e_redirect_bun_build
(2 failures) — pre-existing toolchain drift, not caused by this PR:
bun 1.4.0 now writes bun.lock lockfileVersion: 2, and the hosted
rewriter fail-closes on anything ≠ 1
(redirect_bun_lock_unsupported, patch/redirect/mod.rs:2456).
Reproduced standalone with bun install --save-text-lockfile on a
scratch project. Follow-up: support lockfileVersion 2 (or pin bun < 1.4
in the suite).
- Docker e2e matrix (all 9 ecosystems + vendor capstones) and the live
production hosted/vendored suites are running against this tree; results
will be posted as a comment on this PR.
Release notes (rolled over from [Unreleased])
v4.0 is the three-modes release. What began as an agent-style tool that
patches installed packages in place now offers three deployment modes,
selected with scan --mode <agent|vendored|hosted> (each mode's detailed
entries follow below; per-ecosystem mechanics live in docs/ecosystems.md):
-
Agent mode (the default, and the original behavior): apply patches
installed packages in place on the current machine — node_modules/,
site-packages, the cargo registry cache, and so on — tracked in the local
.socket/ manifest and re-applied after installs by the hooks setup
configures. Requires socket-patch (and Socket API access, or pre-fetched
blobs) on every machine that installs dependencies.
-
Vendored mode (vendor, or scan --mode vendored): ejects each
patched package into a committed
.socket/vendor/<ecosystem>/<patch-uuid>/<artifact> and rewires the
ecosystem's lockfile so the project consumes the vendored copy. After
committing, a fresh checkout builds with the patched dependency on
machines with no socket-patch installed and no Socket API access — fully
offline/airgap-friendly, and the strictest install flags (npm ci,
--frozen-lockfile, --locked, --deploy, …) verify the vendored
artifact like any other. A committed ledger records the verbatim original
lockfile fragments, so vendor --revert restores them byte-exactly.
Covered in v4.0: the whole npm family (npm, yarn classic, yarn berry
node-modules, pnpm, bun), pypi (uv, requirements, poetry, pdm, pipenv),
cargo, go, composer, gem, maven, and nuget.
-
Hosted mode (scan --mode hosted, new in v4.0): rewrites lockfiles /
registry configs so ONLY the patched dependencies resolve to
Socket-hosted, integrity-pinned artifacts on patch.socket.dev — no
artifact bytes land in the repo and no CI changes are needed. The package
manager's own integrity checking pins the patched bytes (tamper fails the
native install), and re-runs are idempotent. Covered in v4.0: the npm
family (package-lock/shrinkwrap, pnpm — including Rush monorepos — yarn
classic, yarn berry, bun), pypi (requirements, uv), cargo, composer, gem,
nuget, maven (fail-closed version suffixing + Trusted Checksums), and
golang for references carrying a goproxy registry override (free tier;
otherwise Go stays vendored — see the documented NO-GO analysis).
All three modes feed VEX attestation, with a provenance marker per mode:
plain (agent), (vendored), and (redirected) (hosted).
Removed (BREAKING)
- The
unlock subcommand. Folded into repair, which now deletes the
leftover <.socket>/apply.lock file as its final housekeeping step (skipped
under --dry-run, refused with lock_held while another live socket-patch
process holds the lock). Rationale: a leftover lock file from a crashed run
never blocked acquisition in the first place — the OS releases a dead
holder's advisory lock along with its file handle — so unlock's inspect
path had no recovery scenario, and its --release file deletion is now
automatic. Migration: unlock --release → repair; the probe-style
"is anything holding the lock?" check → run the mutating command (optionally
with --lock-timeout) and branch on errorCode: lock_held.
SOCKET_UNLOCK_RELEASE is gone with the subcommand, and the
patch_unlocked / patch_unlock_failed telemetry events are retired.
- The global
--break-lock flag and SOCKET_BREAK_LOCK env var. It never
stole a live holder's lock (deliberately, since that defeats mutual
exclusion) and a stale file never contends, so all it did was emit a
lock_broken audit event for a reclaim that plain acquisition performs
anyway. The lock_broken warning event and rollback's warnings[]
lock_broken entry are no longer emitted (warnings stays present, now
always empty). The lock_held stderr hint now advises waiting /
--lock-timeout instead of pointing at the removed commands.
Changed (BREAKING)
--help command order is now workflow-first: scan, apply, vex,
vendor, setup, then rollback, get, list, remove, repair.
Added
-
Hosted mode for Go (free tier). scan --mode hosted now redirects
golang dependencies when the reference carries a goproxy registry
override: a fork-style
replace <mod> <ver> => patch.socket.dev/gopatch/<uuid> <ver>-socketpatch.<n>
in go.mod plus the socket module's two h1: lines in go.sum (and the
replaced original's lines pruned — the tidy-stable state). Day-2 machines
need no configuration: go consults the checksum database only for modules
absent from go.sum, so the committed pair is the whole redirect —
validated end-to-end in e2e_golang_hosted_build.rs (fresh caches, bogus
GOSUMDB tripwire, go mod tidy byte-level no-op, tampered-hash
SECURITY ERROR). Fails closed (per-dep redirect_golang_* warnings, no
partial writes) on missing hashes, an out-of-namespace module path, a
require-version mismatch, or a user-authored replace conflict; references
without the override keep the historical redirect_golang_unsupported
warning (paid tier stays vendored — see docs/design/golang-hosted.md).
Wire schema gains integrity.goModH1 and
registryOverride.identifiers.goModuleVersion (additive). Requires
server-side publication of the grant-free gopatch artifact flavor —
production publishes no golang hosted modules yet, so behavior is unchanged
until it does.
-
Version-bump automation + release-readiness gate.
scripts/bump-version.sh <X.Y.Z> --pr performs the whole bump chore —
stamps every packaging site via version-sync.sh, rolls [Unreleased]
into a dated ## [X.Y.Z] CHANGELOG section, and opens the release/vX.Y.Z
PR (also dispatchable from the Actions tab as the Version Bump
workflow). A new release-readiness CI job runs scripts/release-lint.sh
on every PR: version-coherence always (version-sync must be a no-op, so a
hand-edited version in any one packaging site fails CI), plus the full
gate — non-empty CHANGELOG section, no pre-existing tag — on PRs that bump
the workspace version. The Release workflow's version job now runs the
same script, so the publish gate and the PR gate cannot drift. Playbook:
docs/releasing.md.
-
socket-patch --update — self-update. Downloads the release for the
compiled target from GitHub Releases, verifies it against the published
SHA256SUMS before extraction, sanity-execs the staged binary, and
atomically swaps it in place (Windows uses the rename-dance via
self-replace; a setuid/setgid install is refused). --update 3.4.0
(or SOCKET_PATCH_VERSION) pins a version, up or down; bare --update
never downgrades; --force reinstalls. --dry-run is a check-only
probe (zero downloads, updateAvailable in the --json details).
Package-manager-managed installs (npm, pip, cargo, the gem launcher
cache, Homebrew) are detected from the canonicalized executable
path and refused with that manager's own upgrade command; --force
overrides. --offline refuses up front and --force cannot bypass it.
Concurrent updates are single-flighted via an advisory lock; every
failure path leaves the installed binary untouched.
-
Passive update notice. Interactive runs mention a newer release at
most once a day, on stderr only, after the command's own output:
suppressed under --json/--silent/--offline, in CI, when stderr is
not a terminal, or with SOCKET_NO_UPDATE_CHECK=1 (suppressed means
zero network I/O). The background check can never alter a command's
exit code, stdout, or add more than ~500 ms; state corruption degrades
to "never checked". An explicit --update refreshes the notice's cache.
-
shellcheck scripts/install.sh in CI (and a fix for the SC2144
glob-with--e musl-loader probe it found).
-
socket login now configures socket-patch. The JS Socket CLI's
persisted config (<data dir>/socket/settings/config.json) is read —
never written — as a fallback layer below env vars for apiToken,
defaultOrg, and apiBaseUrl: precedence per key is CLI flag > env var
socket-cli config > built-in default. Four SOCKET_CLI_* env names
are accepted as silent peer aliases (SOCKET_CLI_API_TOKEN,
SOCKET_CLI_ORG_SLUG, SOCKET_CLI_API_BASE_URL,
SOCKET_CLI_NO_API_TOKEN); the canonical SOCKET_* names win. Two new
env-only toggles: SOCKET_NO_API_TOKEN ignores ambient tokens (env +
config; an explicit --api-token still authenticates) and
SOCKET_NO_CONFIG disables the config layer. A corrupt config file
warns once on stderr and is ignored; --json stdout is unaffected. The
telemetry endpoint now resolves the API base through the same chain as
client construction, so a config-supplied apiBaseUrl applies to both.
Design notes: docs/design/configuration.md.
-
Hosted patch mode: scan --mode hosted (a.k.a. the hidden --redirect).
The third patch-application mode: instead of applying in place (agent) or
committing artifacts (vendored), scan rewrites lockfiles / registry
configs so ONLY the patched dependencies resolve to Socket-hosted,
integrity-pinned packages on patch.socket.dev — no artifact bytes land in
the repo and no CI changes are needed. Per ecosystem: npm rewrites
package-lock.json/npm-shrinkwrap.json resolved+integrity (v2 legacy
dependencies mirror included), pnpm-lock.yaml inline resolutions, and
yarn classic resolved/integrity blocks; pypi rewrites requirements.txt
pins to name @ <url> --hash=sha256:… (pip-compile continuation lines are
refused rather than corrupted) and uv.lock wheel entries; cargo defines a
per-patch sparse registry in .cargo/config.toml plus Cargo.toml
registry = keys and Cargo.lock source/checksum surgery; composer
rewrites the lock entry's dist url/shasum; nuget adds a nuget.config
source + packageSourceMapping and repins packages.lock.json
contentHash; gem adds a per-dep source block + a CHECKSUMS pin
(bundler ≥ 2.6). A dep counts as redirected only when its hosted URL (or
per-dep registry index) actually landed in a project file; re-runs are
idempotent (zero new edits over already-rewritten output). The Rust
rewriters are held byte-identical to the depscan backend's TS twins (the
GitHub-app hosted PR flow) by shared golden fixtures under
tests/fixtures/redirect/. JSON output gains a redirect sub-object with
mode: "hosted", redirected, rewrittenFiles, skipped, warnings.
-
scan --mode <hosted|vendored|agent>: the documented mode selector. One
value-enum flag replaces the boolean spellings (--redirect == hosted,
--vendor == vendored, --apply/--sync == agent), which remain supported
as aliases. Combining --mode with a boolean of a DIFFERENT mode is a
usage error (exit 2); the same mode spelled both ways is accepted, and
--detached now requires vendored mode in either spelling.
-
VEX support for hosted mode: the (redirected) provenance marker + the
redirect ledger. scan --mode hosted persists its recorded file edits and
the full patch records (file hashes + vulnerabilities) into
.socket/vendor/redirect-state.json (merge-on-rewrite, append-only edits —
the pre-redirect originals a future revert needs are never clobbered).
Redirected patches carry the impact-statement marker "Patched via Socket
patch <uuid> (redirected)", completing the provenance trio (plain =
agent, (vendored), (redirected)). In-run scan --mode hosted --vex
attests confirmed redirects from the ledger WITHOUT hash verification (the
bytes are fetched at install time; the JSON vex summary carries
verified: false), while a post-install socket-patch vex reads the ledger
back and hash-verifies the redirected patches against the installed tree.
A confirmed redirect whose record fetch failed surfaces a
record_fetch_failed warning (the patch is missing from VEX until a
re-run).
-
NuGet + Maven vendor backends (vendor / scan --mode vendored).
NuGet: the uuid dir is a committed folder feed holding a deterministically
rebuilt .nupkg (embedded signature dropped; unsigned is accepted under
NuGet's default validation), wired via a nuget.config source +
packageSourceMapping and a packages.lock.json contentHash repin —
dotnet restore --locked-mode then fails NU1403 on tamper. Maven: the uuid
dir is a committed maven2 file:// repository (rebuilt .jar + the
verbatim upstream pom so transitives survive + .sha1 sidecars), wired via
a pom.xml <repository> with checksumPolicy=fail; multi-module
aggregator poms (vendor_maven_multimodule_unsupported) and gradle-only
projects (vendor_gradle_unsupported) are refused fail-closed, and the
always-on vendor_maven_local_cache_shadow advisory carries the
mvn dependency:purge-local-repository one-liner (a warm ~/.m2 copy
silently shadows any repository). Both are proven by docker capstones
against the real .NET SDK / Apache Maven (cold-cache, --network none,
RED + TAMPER probes). nuget and maven are now DEFAULT compile features;
the SOCKET_EXPERIMENTAL_NUGET / SOCKET_EXPERIMENTAL_MAVEN runtime
opt-ins that briefly gated in-place agent apply were retired later in this
cycle (see the "promoted to fully available" entry under Changed). The vendored
path convention + uuid recovery rule now covers nuget and maven dirs,
and --vendor-source prebuilt downloads cover nuget.
-
Maven hosted rewriter (pom projects) — fail-closed version suffixing +
Trusted Checksums. Hosted mode's maven leg pins the patched jar the only
way a lockfile-less ecosystem can: the serve route exposes the patch under a
Socket-only <version>-socket.<hex8> suffix (existing ONLY on the injected
socket-patch-<uuid> repository), and the rewriter pins that version
explicitly — it rewrites the literal <version>, or (for a transitive /
managed dependency with no literal version) adds a <dependencyManagement>
entry — alongside the <repository> insert (releases enabled,
checksumPolicy=fail, snapshots disabled). An outage or tamper on the Socket
repo then HARD-FAILS the build: the suffixed version resolves nowhere else,
so there is no silent fall-through to Central (the base version 404s). A
${property} version is refused (redirect_maven_dep_unpinned — a literal
edit would break the reference and a depMgmt pin could strand sibling
artifacts); a literal version matching neither the base nor the suffixed
value is skipped (redirect_maven_dep_version_mismatch); a non-jar <type>
is skipped (redirect_maven_unsupported_packaging). When the serve route
supplies both the jar and pom sha256, the rewriter also emits Maven 3.9+
Trusted Checksums files — .mvn/maven.config resolver args (originAware=false,
failIfMissing=false) + .mvn/checksums/checksums.sha256 entries pinning
both artifacts under the suffixed version's local-repo path, merging into any
pre-existing user config / checksum set (a conflicting value is never
overridden — redirect_maven_trusted_checksums_conflict). The .mvn/* files
are silently inert below Maven 3.9 (the version suffixing is still fail-closed
on its own); on 3.9.0–3.9.8 a mismatch is enforced but reported unclearly
(readability fixed in 3.9.9, MNG-8182). When the upstream pom is unavailable /
unsuffixable the rewriter falls back to the legacy same-GAV repository
injection with a redirect_maven_same_gav_fallback warning (NOT fail-closed:
a Socket-repo failure falls back to the unpatched artifact). Gradle build
scripts are never edited: a present build.gradle* / settings.gradle*
emits a paste-able exclusiveContent snippet carrying the suffixed version
(redirect_gradle_manual_snippet) plus a reminder to bump the dependency
declaration — fail-closed by repository exclusivity.
-
Hosted mode now rewrites yarn-berry and bun lockfiles. The hosted npm
family gains two flavors beyond package-lock / pnpm / yarn-classic. yarn
berry (__metadata: v2+ lock): the rewriter edits ONLY the lock entry —
resolution: gains yarn's own ::__archiveUrl=<encodeURIComponent(url)>
binding and checksum: becomes the precomputed yarnBerry10c0 cache-zip
sha512 — leaving the descriptor key and package.json untouched, so yarn install --immutable --check-cache passes and tamper fails YN0018. Whole-file
gates refuse a cacheKey ≠ 10c0 or a .yarnrc.yml compressionLevel ≠ 0
(redirect_yarn_berry_cache_unsupported) — no offline-reproducible checksum.
Validated e2e against real corepack [email protected] on the node-modules linker;
PnP is not exercised for hosted (the lock rewrite fires, but PnP's
.yarn/cache resolution is untested). bun (text bun.lock v1): the
packages-entry registry 4-tuple ["name@ver","<reg>",{deps},"sha512-…"] is
rewritten to a URL 3-tuple ["name@<url>",{deps},"sha512-…"], fail-closed on
any grammar deviation; bun install --frozen-lockfile then installs the
hosted bytes and tamper fails the integrity check. A binary bun.lockb with
no text lock is auto-migrated first via the user's own bun install --save-text-lockfile --frozen-lockfile --lockfile-only (deletes bun.lockb,
recorded as a removed ledger edit, offline, fails closed;
redirect_bun_lockb_would_migrate on --dry-run,
redirect_bun_lockb_unsupported if the migration is unavailable). The Rust
rewriters are byte-identical to the depscan backend's TS twins via shared
golden fixtures.
-
Hosted mode supports Rush monorepos. A Rush repo has no root
package.json/lockfile pair — its pnpm source-of-truth lock lives at
common/config/rush/pnpm-lock.yaml (plus one per subspace under
common/config/subspaces/<name>/). scan --mode hosted discovers those
locks when rush.json is present and repoints them in place (the pnpm
rewriter is now basename-generalized, so nested locks rewrite path-generically).
Editing a Rush lock outside rush update desyncs the pnpmShrinkwrapHash in
common/config/rush/repo-state.json, so a redirect_rush_repo_state_stale
warning fires when a lock was touched and that file exists — rush install
fails under preventManualShrinkwrapChanges until rush update refreshes it,
but the redirect survives the refresh (pnpm keeps locked resolutions for
unchanged specifiers). Agent mode already works through Rush's generated
project symlink farm; vendored mode is refused (vendor_rush_unsupported)
because rush install copies the lock into common/temp, so vendor's
relative file: specs can't survive — the refusal routes to hosted mode.
-
pnpm hosted rewriter generalized to nested lockfiles. The
pnpm-lock.yaml rewriter now matches any pnpm-lock.yaml at the project
root OR at any nested path (*/pnpm-lock.yaml), so Rush subspace locks and
other nested-lock layouts are rewritten in place under their repo-relative
keys. Write-back and confirmed-redirect gating are path-generic.
-
Golang hosted mode is a documented NO-GO. Hosted redirect for Go is
deliberately unsupported — sumdb hard-fails the patched pseudo-version on
every day-2 machine and the only escapes are uncommittable machine-local
config; Go's module-path identity would force per-grant artifacts against
the build-once converter; and the default GOPROXY chain would leak
licensed bytes / tokened URLs to the public mirror. The full analysis lives
in docs/design/golang-hosted-no-go.md; both the CLI rewriter and the
depscan backend twin emit redirect_golang_unsupported naming the remedy
(use vendored mode, which gives Go everything hosted promises elsewhere).
The one sanctioned exception — an ephemeral-CI GOPROXY recipe — is
documentation-only and never written into a repository.
-
vendor now supports every major npm and pypi package manager. The npm
ecosystem gained four lockfile flavors beyond package-lock.json — yarn
classic (yarn.lock v1), yarn berry with the node-modules linker
(resolutions + a cache-zip 10c0 checksum reproduced offline from the
vendored tarball), pnpm (pnpm.overrides + pnpm-lock.yaml surgery, pnpm 9
& 10), and bun (bun.lock) — all sharing the one vendored tarball and
selected by a content-sniffing probe (yarn-berry PnP and bun's binary
bun.lockb are refused with pointers to the native flow). The pypi
ecosystem gained poetry, pdm, and pipenv (lock-only [[package]] / entry
splices, like the existing uv/requirements flavors). Every lockfile
checksum/reference field for a vendored package is now recomputed
coherently (the v2 "update checksums and references" directive); the gem
backend handles bundler ≥ 2.6's optional CHECKSUMS section; composer's
dist.reference carries the patch UUID into installed.json. Each flavor
has a real-package-manager build-proof capstone (fresh-checkout, cold-cache,
strictest-install — --frozen/--immutable/--deploy/--locked — with
byte-identical revert). vendor --force/--revert accept empty env vars
(SOCKET_FORCE=) as false, matching the global-flag contract.
-
New vendor subcommand: committable vendoring of patched dependencies.
Where apply patches installed packages in place (machine-local state),
socket-patch vendor ejects each patched package into a committed
.socket/vendor/<ecosystem>/<patch-uuid>/<artifact> and rewires the
ecosystem's lockfile so the project consumes the vendored copy — after
committing, a fresh checkout builds with the patched dependency on machines
with no socket-patch installed and no Socket API access. Per ecosystem
(each mechanism validated against the real package manager): npm rewrites
package-lock.json only (deterministic patched tarball, recomputed
integrity, npm ci-verified); cargo writes a [patch.crates-io] entry in
.cargo/config.toml plus surgical Cargo.lock edits so cargo build --locked --offline works; golang reuses the replace-directive engine
pointed at the vendor tree; composer rewrites the lock entry to a
dist: path copy; gem edits the Gemfile + Gemfile.lock pair in bundler's
canonical form; pypi rebuilds a valid wheel (regenerated RECORD) wired
through uv's pyproject.toml/uv.lock pair (uv-first) or
requirements.txt (pip / uv pip). The patch UUID is recoverable from the
lockfile path string alone (a documented convention for external tools), a
committed .socket/vendor/state.json ledger records the verbatim original
lockfile fragments, and vendor --revert restores them byte-exactly.
vendor --vex mirrors apply --vex; VEX generation attests vendored
patches by hashing the committed artifacts, and apply yields ownership of
vendored packages (vendored skip reason).
-
Cargo support (cargo is now a default feature). apply patches a Rust
dependency in place wherever the crawler finds it — the project vendor/
directory or the shared $CARGO_HOME registry cache — rewriting the crate's
.cargo-checksum.json sidecar so cargo build accepts the modified files.
rollback restores the original bytes from the beforeHash blobs, like
npm/PyPI/gem. cargo ships on by default (alongside the always-on npm + PyPI
- Ruby gems support), so released binaries and a plain
cargo install socket-patch-cli patch Rust dependencies out of the box;
maven/composer/nuget/deno remain opt-in.
-
Project-local Go replace-redirect backend (golang, default feature).
The Go module cache is shared, read-only and checksum-verified, so in-place
patching would fail go.sum at build time. Instead apply writes a
project-local patched copy under .socket/go-patches/<module>@<version>/
and a managed replace directive in the project go.mod, so the patch is
project-scoped and the cache stays pristine for sibling projects. rollback
cleanly drops the replace directive + copy. apply --check is a read-only,
lock-free, offline auditor that verifies the committed redirects match the
manifest, exiting non-zero on drift (for CI / GitHub-App use).
-
Inline OpenVEX generation on apply and scan via --vex <path>. A
single successful apply/scan can now both patch and emit the OpenVEX
0.2.0 attestation, instead of requiring a separate socket-patch vex step.
The --vex-product / --vex-no-verify / --vex-doc-id / --vex-compact
flags mirror the standalone vex knobs (and reuse the SOCKET_VEX_* env
vars). The document is always written to the given path (never stdout, so it
never races --json), built from the post-run manifest and verified against
on-disk state. JSON output gains a top-level vex summary
({ path, statements, format }). A requested-but-failed VEX makes the
command exit non-zero even when the apply/scan itself succeeded, surfacing a
stable error code in the envelope.
Changed
-
install.sh can install without reaching github.com. New
SOCKET_PATCH_BASE_URL points the archive downloads at any releases base that
answers GitHub's two asset paths — notably
https://install.socket.dev/patch/SocketDev/socket-patch/releases, which relays them
from the GitHub release, so one URL template covers either origin. A new
release needs no publish for this: the origin resolves "latest" per request.
socket-patch --update can use the same host today through the
SOCKET_UPDATE_BASE_URL override it already has. Also new:
SOCKET_PATCH_INSTALL_DIR to choose the install directory explicitly instead
of taking /usr/local/bin or ~/.local/bin. The default download origin is
still GitHub — see docs/installer-hosting.md.
-
The documented one-liner installs from https://install.socket.dev/patch.
The previous URL was raw.githubusercontent.com, which asks users to trust a
third-party CDN for a script they pipe into a shell and is the first URL a
locked-down egress policy blocks. The hosted copy is byte-for-byte
scripts/install.sh, with its SHA-256 published at
install.socket.dev/patch.sha256; the GitHub raw URL keeps working and serves
the same bytes. Binaries are still downloaded from the GitHub release and
verified against its SHA256SUMS — the trust model is unchanged, only the
script's origin moved. New: docs/installer-hosting.md (how the copy is
published), a CI step that runs the installer end to end instead of only
linting it, and an installer-drift workflow that checks the hosted copy
against this repository weekly.
-
Maven and NuGet promoted to fully available — the
SOCKET_EXPERIMENTAL_MAVEN / SOCKET_EXPERIMENTAL_NUGET runtime gates
are retired. Every flow (scan in all modes, apply, get,
rollback, vendor, repair, vex, setup) now discovers and
patches installed Maven and NuGet packages unconditionally; the
"N patch(es) skipped — support is experimental" warnings are gone, and
the previously #[ignore]d maven/nuget dispatch e2e tests now gate CI.
Setting the old env vars is harmless but does nothing. Behavior notes:
a default scan now walks the local Maven repository (~/.m2 /
MAVEN_REPO_LOCAL) and the NuGet caches, and scan --prune/--sync
now judges maven/nuget manifest entries like any other ecosystem's
(previously they were exempt from pruning while the gate was closed).
The in-place sidecar caveat is unchanged and now documented per mode in
docs/ecosystems.md: agent-mode patching leaves Maven's
.jar.sha1/.jar.md5 stale and NuGet's fixup deletes
.nupkg.metadata + advises on .nupkg.sha512; the vendored/hosted
modes never touch the caches.
-
Release workflow consolidated into a single release.yml. One
dispatch now publishes every package — crates.io, npm, PyPI, and
RubyGems (both gems), all via OIDC trusted publishing — with the
launcher-gem job gated on the GitHub release existing. The separate
release-ecosystems.yml workflow is removed (its release: published
trigger never fired: the release is created with GITHUB_TOKEN, which
suppresses downstream workflow events). The CLI is distributed via
GitHub releases, npm, PyPI, crates.io, and RubyGems only — the
Composer/Packagist, Maven Central, and NuGet launcher channels drafted
earlier in this cycle were dropped before ever shipping in a release.
-
--api-url / --proxy-url no longer carry clap-level defaults: with
neither flag nor env var set they parse as unset and the documented
default URLs are applied at API-client construction (after the
socket-cli config layer). Observable behavior is unchanged unless a
socket-cli login exists.
-
All ecosystem feature flags removed — every ecosystem is always compiled
in. The cargo, golang, maven, composer, nuget, and deno Cargo
features are gone from both crates; npm, PyPI, Ruby gems, Go, Cargo, NuGet,
Maven, Composer, and Deno support is now unconditional. Builds that passed
--features <eco> will get an "unknown feature" error and should simply
drop the flag; --no-default-features no longer produces a minimal binary
(there is nothing left to strip). The SOCKET_EXPERIMENTAL_MAVEN /
SOCKET_EXPERIMENTAL_NUGET runtime gates outlived this entry only briefly —
they are retired in the same release (see the "promoted to fully available"
entry under Changed). The only remaining features are the
test-suite gates docker-e2e and setup-e2e on socket-patch-cli. (MAJOR
for anyone scripting --features; no behavior change for default builds
beyond composer/deno support now being present.)
-
Token-less scan now batch-queries the public proxy. Proxy-mode scans
POST {proxy}/patch/batch (one request per --batch-size chunk, mirroring
the authenticated /v0/orgs/{slug}/patches/batch endpoint) instead of
issuing one GET /patch/by-package/:purl per package. The client
transparently degrades to the legacy per-package GET path against proxies
that predate the batch endpoint, and when the all-or-nothing batch
validation rejects a chunk (e.g. a crawled PURL type the server doesn't
recognize, such as pkg:jsr/… — per-package queries tolerate those
individually, so one exotic package can't fail a whole scan). Rate limits
and over-capacity 503s still surface instead of silently degrading. (MINOR)
Fixed
-
npm @socketsecurity/socket-patch: the ./schema export is now built
at publish. The subpath pointed at a gitignored dist/ directory that
nothing built during release, so it shipped broken; a prepack script
now compiles it as part of npm publish.
-
Release workflow tag-guard and idempotency fixes. The
tag-already-exists guard never fired (it ran git rev-parse in a
shallow, tagless checkout) — it is now a stateless git ls-remote check
that still permits same-commit retries; the GitHub-release step re-runs
cleanly instead of hard-failing when the release already exists; and the
cargo/PyPI/gem publish jobs skip already-published versions, so
"Re-run failed jobs" can resume a partial release safely.
-
NuGet hosted rewriter: creating a packageSourceMapping from scratch now
emits a catch-all for pre-existing sources. packageSourceMapping is
exclusive — once ANY mapping exists, every package must match some source's
pattern or restore hard-fails NU1100. A redirect into a nuget.config with
no prior mapping previously routed only the patched id, breaking every
OTHER package's restore; the rewriter now fans a <package pattern="*" />
mapping out to each pre-existing package source (longest-prefix match still
routes the patched id to the Socket source). Golden fixtures updated on
both the Rust and TS sides.
-
VEX now attests Go replace-redirect patches. socket-patch vex
previously verified golang patches against the pristine module cache
instead of the patched .socket/go-patches/ copy, so redirect-applied
patches were silently omitted from the document (reported not_applied,
or package_not_found on cache-less CI). Verification now follows the
managed replace directive to the committed copy.
-
repair on a hosted-only project is an informational no-op. Hosted
(--mode hosted) mode leaves no local artifacts to repair — the lockfiles
point at patch.socket.dev URLs, and there is no manifest or vendor ledger.
A project whose only .socket/ trace is redirect-state.json (no manifest,
no vendor ledger, no vendored lockfile references) previously errored with
manifest_not_found (exit 1); it now exits 0 with a redirect_only_project
skip pointing at scan --mode hosted. Repair still errors on a bare
directory with no traces at all.
After merging
- Dispatch the Release workflow on the default branch (optionally with
dry-run: true first). It builds all targets, tags v4.0.0, creates
the GitHub release, and publishes every ecosystem package.
- Approve the staged npm versions with 2FA — platform packages first, then
@socketsecurity/socket-patch (link in the run's step summary).
- On a partial failure: fix the cause and use "Re-run failed jobs" on the
same run — every publish job is idempotent.
See docs/releasing.md for the full playbook.
🤖 Generated with Claude Code
Note Medium Risk
Mechanical version/changelog stamp with no runtime logic changes, but it is a major-version publish gate that will ship breaking CLI removals once the Release workflow runs.
Overview
Bumps the workspace from 3.3.0 to 4.0.0 in Cargo, npm platform packages, PyPI, and both Ruby gems, and rolls [Unreleased] into ## [4.0.0] — 2026-08-20.
The new changelog section opens with a three-modes overview (agent / vendored / hosted) and drops the “lands in v4.0” heading qualifiers now that this is v4.0. No product code changes in this PR.
Reviewed by Cursor Bugbot for commit 90a6146. Configure here.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Bump 3.3.0 -> 4.0.0 (major: removes the unlock subcommand and the
--break-lock flag, reorders --help). Synced via scripts/bump-version.sh
across Cargo.toml (workspace version + core dep pin), Cargo.lock, all
npm packages (main + platform shims + lockfile), both pypi pyprojects,
and both gemspecs + the launcher VERSION constant.
Rolls CHANGELOG [Unreleased] into ## [4.0.0] and adds an intro
describing the three patch-application modes (agent / vendored /
hosted) with their v4.0 ecosystem coverage.
Co-Authored-By: Claude Fable 5 <[email protected]>
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Full e2e results (local, this tree, 4.0.0 binary)
| Leg |
Result |
Host suite socket-patch-core |
✅ green |
Host suite socket-patch-cli (~3.9k tests) |
✅ green except e2e_redirect_bun_build (pre-existing, see below) |
Host --ignored e2e (maven, nuget, safety_cargo_build, safety_pnpm, redirect_npm_build, redirect_rush_sim, vendor_gem_build) |
✅ all green |
| Docker e2e matrix — all 9 ecosystems (npm, pypi, gem, cargo, golang, maven, composer, nuget, deno), images rebuilt with the 4.0.0 binary |
✅ all green |
| Docker vendor capstones (composer, gem, maven, nuget, pypi_pm) + harness selftest |
✅ all green |
e2e_hosted_production (live, real registries + patch.socket.dev, 3 attempts) |
⚠️ 14/16 — bun + gem legs (below) |
e2e_vendored_production (live, 3 attempts) |
⚠️ 11/13 — bun + gem legs (below) |
Skipped on host (no toolchain; covered by the Docker matrix): e2e_composer / e2e_vendor_composer_build (no composer), deno host legs. e2e_cargo/e2e_golang select 0 tests under --ignored (their tests ran in the default host suite).
Pre-existing failures — NOT from this PR (reproduce identically on the unbumped tree's code paths)
- bun 1.4 writes
bun.lock lockfileVersion: 2; the hosted rewriter fail-closes on ≠ 1 (redirect_bun_lock_unsupported, socket-patch-core/src/patch/redirect/mod.rs:2456). Fails e2e_redirect_bun_build (2 tests) and the bun legs of both production suites on any machine with bun ≥ 1.4. Reproduced standalone: a scratch bun install --save-text-lockfile emits "lockfileVersion": 2. Follow-up: teach the rewriter (and its depscan TS twin) lockfileVersion 2, or pin bun < 1.4 in the suites.
- Production gem catalog drift:
[email protected] now resolves to patch c1a1cd3c-b670-4e44-b4fa-1a63ecd42db6, not in the suites' pinned any-of sets (15e960b5-…, 6c4141c5-…). The redirect/vendor itself worked (correct patch.socket.dev source block wired); the pins are stale. Follow-up per the tests' own guidance: verify the new patch and extend GEM_UUIDS in both production suites.
🤖 Generated with Claude Code
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
#222/#224/#226 in the 4.0.0 changelog
Production published two more free [email protected] patches on
2026-08-20 — eeb6bf9f (GHSA-9xrj-h377-fr87 / CVE-2026-33195, disk_service)
at 16:14Z and c1a1cd3c (GHSA-r4mg-4433-c7g3 / CVE-2025-24293,
image_processing_transformer) at 20:31Z — and the server-ranked resolver
now wires the newest, so the required hosted-e2e check went red on the
gem leg (this PR's run 32436943099, and every push until the pins learn
the new UUIDs). Both patches' /patch/view blobs live-verified: every
touched file carries the Socket Community Patch header.
- hosted suite: append both UUIDs to the GEM_UUIDS any-of set (the leg
already content-verifies the wired patch against its /patch/view
manifest, so the pin is the only per-patch knowledge it needs).
- vendored suite: the single GEM_UUID pin also hardcoded the marker FILE
(s3_service.rb, specific to the 15e960b5 patch). Replace it with a
GEM_PATCHES (uuid, marker-file) table; the leg now asserts pristine
across every candidate file up front, resolves the wired uuid from the
download envelope, and probes that patch's own file + vendor path.
- docs/testing catalogs updated to match.
Also add the 4.0.0 CHANGELOG entries for the three user-facing changes
merged from main without notes: get --mode hosted|vendored (#226), the
bun 1.4 lockfileVersion-2 acceptance (#224), and the gem multi-copy /
bundle-path-precedence apply fix (#222).
Validated live against production: e2e_hosted_production 16/16,
e2e_vendored_production 13/13, e2e_redirect_bun_build 9/9.
Co-Authored-By: Claude Fable 5 <[email protected]>
CI red diagnosed + fixed; main merged in
What broke: the required hosted-e2e check failed on gem_bundler_hosted_install_proof (all 3 attempts). Production published two more free [email protected] patches on 2026-08-20 — eeb6bf9f… (GHSA-9xrj-h377-fr87 / CVE-2026-33195) and c1a1cd3c… (GHSA-r4mg-4433-c7g3 / CVE-2025-24293) — and the server-ranked resolver now wires the newest, which wasn't in the suites' pinned any-of sets. Test-catalog drift, not a CLI regression (the redirect itself wired correctly).
Fixed in b5d4808 (after live-verifying both new patches' /patch/view blobs carry the Socket Community Patch marker):
- hosted suite: both UUIDs appended to
GEM_UUIDS;
- vendored suite: the single-UUID pin (which also hardcoded the
s3_service.rb marker file, specific to the old patch) generalized to a GEM_PATCHES (uuid → marker-file) table — the leg now resolves the wired patch from the download envelope and probes that patch's own file;
docs/testing/* catalogs updated.
Merged main in 90a6146 (#222, #224, #226, #227) — this also cured the bun failures from the earlier local e2e report: #224 (bun 1.4 lockfileVersion: 2 acceptance) landed on main after this branch was cut. Added the missing 4.0.0 CHANGELOG entries for the three user-facing changes that merged without notes (#222, #224, #226).
Re-validated live against production on this exact tree: e2e_hosted_production 16/16, e2e_vendored_production 13/13, e2e_redirect_bun_build 9/9.
🤖 Generated with Claude Code
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|