Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,16 @@ jobs:
suite: e2e_composer
- os: ubuntu-latest
suite: e2e_nuget
# Cargo project-local [patch]-redirect backend + fail-closed guard.
# `guard_build_integration` is hermetic (a shell stub + `cargo build
# --offline` against a zero-dep path dep), so it exercises the
# build.rs-panic-aborts-a-real-build seam with no network.
# `e2e_cargo_coexist`'s real-cargo proofs fetch a crate (cached) and
# skip on fetch failure. Both suites are `#[cfg(unix)]`.
- os: ubuntu-latest
suite: guard_build_integration
- os: ubuntu-latest
suite: e2e_cargo_coexist
# The live-API smoke suites (e2e_npm, e2e_pypi, e2e_gem,
# e2e_scan) are intentionally NOT in the PR matrix — their
# `#[ignore]`-gated tests hit the real public proxy at
Expand Down
53 changes: 53 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,49 @@ in this file — see `.github/workflows/release.yml` (`version` job).

### Added

- **Project-local cargo `[patch]`-redirect backend (local mode).** Patching a
Rust dependency from the registry cache no longer mutates the shared
`$CARGO_HOME` registry in place. Instead `apply` writes a project-local
patched **copy** under `.socket/cargo-patches/<name>-<version>/` and a managed
`[patch.crates-io]` entry (+ `[env] SOCKET_PATCH_ROOT`) into
`.cargo/config.toml`, so patches are project-scoped and the registry stays
pristine for sibling projects. `rollback` cleanly drops the entry + copy
(leaving `setup` state — the guard dependency + `[env]` — intact).
`apply --check` is a new read-only, lock-free, offline auditor that verifies
the committed copies/config match the manifest **and** cross-checks
`Cargo.lock` (flagging a patched dependency that silently resolved to an
unpatched version); it exits non-zero on drift (for CI / GitHub-App use).
Vendored crates (`vendor/`) and `--global` cargo keep the existing in-place
`.cargo-checksum.json` rewrite path unchanged. **`cargo` is now a default
feature** (alongside the always-on npm + PyPI support), so released binaries and
a plain `cargo install socket-patch-cli` patch Rust dependencies and run the
guard out of the box; `golang`/`maven`/`composer`/`nuget`/`deno` remain opt-in.
A binary built `--no-default-features` (no cargo) now fails `apply --check`
closed rather than reporting "in sync", so it can never make the guard pass
vacuously.
- **`socket-patch-guard` crate + `setup` cargo support.** `socket-patch setup`
now also configures Rust projects: it adds a tiny `socket-patch-guard`
dependency (a normal `[dependencies]` entry, not a `[build-dependencies]` one,
so cargo always compiles it and runs its build script) to every workspace
member and writes `[env] SOCKET_PATCH_ROOT`. The guard's build script runs `socket-patch apply --check`
on every relevant `cargo build` and is **fail-closed**: if the committed
patched copies are out of sync with `.socket/manifest.json` (a stale copy, or
a patched dependency that resolved to an unpatched version), the build
**fails** rather than silently compiling stale/unpatched sources — closing the
CI footgun where a one-shot build could ship an unpatched binary. The fix is
run-order-independent (it checks the static committed state, not when the
build script happens to run). It is a single fail-closed mode with no
`warn`/`off` escape: on drift it regenerates the copies then fails the build
with a "re-run" message (the retry is clean), and an unrecoverable state or a
missing `socket-patch` CLI also fails the build. In normal use the guard never
fires, since changing a patch goes through `get`/`apply` (which regenerate the
copies). The user's own `build.rs` is never touched. For CI, run
`socket-patch apply --check --ecosystems cargo` as an explicit pipeline gate.
`setup --check` / `setup --remove` cover the
round-trip. *(A guarded repo requires `socket-patch` on the build machine —
wire it into apps/workspaces you control, not a published library. Pre-GA:
`socket-patch-guard` will be published to crates.io; airgapped users vendor
it.)*
- **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.
Expand All @@ -28,6 +71,16 @@ in this file — see `.github/workflows/release.yml` (`version` job).
command exit non-zero even when the apply/scan itself succeeded, surfacing a
stable error code in the envelope.

### Changed

- **Local cargo `apply` now redirects instead of patching in place.** Registry
crates patched by a previous (in-place) version leave a mutated shared
registry + rewritten `.cargo-checksum.json` behind; the new local backend
never touches the registry, so those stay dirty until cargo re-fetches.
`apply` now prints a one-line **warning** when it detects such a crate
(suppressed under `--offline`, so the build-time guard stays quiet) and points
at restoring the pristine copy. No automatic registry cleanup is performed.

## [3.2.0] — 2026-05-29

A repo-wide correctness, security, and filesystem-safety hardening pass: every
Expand Down
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
[workspace]
members = ["crates/socket-patch-core", "crates/socket-patch-cli"]
members = [
"crates/socket-patch-core",
"crates/socket-patch-cli",
"crates/socket-patch-guard",
]
resolver = "2"

[workspace.package]
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,11 @@ pip install socket-patch
cargo install socket-patch-cli
```

By default this builds with npm and PyPI support. For additional ecosystems:
By default this builds with npm, PyPI, and Cargo support. For additional
ecosystems:

```bash
cargo install socket-patch-cli --features cargo,golang,maven,composer,nuget
cargo install socket-patch-cli --features golang,maven,composer,nuget,deno
```

## Quick Start
Expand Down
7 changes: 6 additions & 1 deletion crates/socket-patch-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ regex = { workspace = true }
tempfile = { workspace = true }

[features]
default = []
# Shipped defaults: npm + PyPI are always compiled in (no feature gate); `cargo`
# is on by default so released binaries and `cargo install socket-patch-cli`
# patch Rust deps and run the build-time guard out of the box. The remaining
# ecosystems stay opt-in. Build `--no-default-features` for a minimal
# (npm + PyPI only) binary — its `apply --check` then fails closed.
default = ["cargo"]
cargo = ["socket-patch-core/cargo"]
golang = ["socket-patch-core/golang"]
maven = ["socket-patch-core/maven"]
Expand Down
Loading
Loading