From eae55f389acefe3584d151117e8d27a2143e6265 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 12 Oct 2026 22:52:35 +0000 Subject: [PATCH 1/3] Start fix for #823 (slice 7) Assisted-by: Claude Code:claude-opus-5-5 From ebeda932e62a96f100615f157fa43269fe731eb3 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 12 Oct 2026 23:19:29 +0000 Subject: [PATCH 2/3] Spawn VEX test children hermetically The VEX suites (e2e_vex_lockfile, e2e_vex_build, e2e_vex, e2e_vex_redirect, e2e_embedded_vex) and the shared run_vex helper built the socket-patch child with their own SOCKET_* sweeps. The sweeps differed by file: some kept an ambient SOCKET_NO_CONFIG, none forced the update check off, several left telemetry on, and two hatch revert spawns scrubbed nothing at all. A developer's shell could change what those tests exercised. They now go through tests/common/hermetic.rs, and 16 files leave the PENDING_RAW_SPAWNS ratchet. Test-only; no product change. Refs #823 Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/e2e_embedded_vex.rs | 9 +-------- crates/socket-patch-cli/tests/e2e_vex.rs | 9 +-------- .../tests/e2e_vex_build/deno.rs | 10 +--------- .../tests/e2e_vex_build/hatch.rs | 6 ++++-- .../tests/e2e_vex_build/poetry.rs | 11 ++++++----- .../tests/e2e_vex_lockfile/bun.rs | 16 ++++------------ .../tests/e2e_vex_lockfile/cargo.rs | 17 ++++------------- .../tests/e2e_vex_lockfile/golang.rs | 16 ++++------------ .../tests/e2e_vex_lockfile/maven.rs | 17 ++++------------- .../tests/e2e_vex_lockfile/npm.rs | 18 ++---------------- .../tests/e2e_vex_lockfile/nuget.rs | 13 +++---------- .../tests/e2e_vex_lockfile/pnpm.rs | 10 +--------- .../tests/e2e_vex_lockfile/poetry.rs | 9 +++------ .../tests/e2e_vex_lockfile/uv.rs | 12 ++++-------- .../tests/e2e_vex_lockfile/yarn.rs | 15 +++------------ .../tests/e2e_vex_redirect.rs | 9 +-------- .../tests/spawn_env_hygiene.rs | 16 ---------------- .../tests/vex_e2e_common/mod.rs | 19 ++++++++----------- 18 files changed, 54 insertions(+), 178 deletions(-) diff --git a/crates/socket-patch-cli/tests/e2e_embedded_vex.rs b/crates/socket-patch-cli/tests/e2e_embedded_vex.rs index 4ca9b2aaa..325bfd977 100644 --- a/crates/socket-patch-cli/tests/e2e_embedded_vex.rs +++ b/crates/socket-patch-cli/tests/e2e_embedded_vex.rs @@ -12,7 +12,6 @@ #[path = "common/mod.rs"] mod common; -use common::binary; use std::collections::HashMap; use std::path::Path; @@ -41,13 +40,7 @@ use socket_patch_core::manifest::schema::{ /// need no serialization) makes the explicit CLI flags the sole source of /// truth. fn cli() -> Command { - let mut cmd = Command::new(binary()); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") && key != "SOCKET_NO_CONFIG" { - cmd.env_remove(key); - } - } - cmd + common::hermetic::binary_command() } fn write_manifest(cwd: &Path, manifest: &PatchManifest) { diff --git a/crates/socket-patch-cli/tests/e2e_vex.rs b/crates/socket-patch-cli/tests/e2e_vex.rs index cb46e851f..839e8d1c0 100644 --- a/crates/socket-patch-cli/tests/e2e_vex.rs +++ b/crates/socket-patch-cli/tests/e2e_vex.rs @@ -16,7 +16,6 @@ #[path = "common/mod.rs"] mod common; -use common::binary; use std::collections::HashMap; use std::path::Path; @@ -46,13 +45,7 @@ use socket_patch_core::manifest::schema::{ /// (the parent env is never mutated, so tests stay independent and need no /// serialization) makes the explicit CLI flags the sole source of truth. fn cli() -> Command { - let mut cmd = Command::new(binary()); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") && key != "SOCKET_NO_CONFIG" { - cmd.env_remove(key); - } - } - cmd + common::hermetic::binary_command() } /// Write `manifest` to `/.socket/manifest.json`. diff --git a/crates/socket-patch-cli/tests/e2e_vex_build/deno.rs b/crates/socket-patch-cli/tests/e2e_vex_build/deno.rs index 762e2b060..c87225695 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_build/deno.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_build/deno.rs @@ -161,17 +161,9 @@ impl Deno { } fn socket_patch(cwd: &Path, deno_dir: &Path, args: &[&str]) -> (Option, Value, String) { - let mut cmd = Command::new(binary()); - for (key, _) in std::env::vars_os() { - if key.to_string_lossy().starts_with("SOCKET_") { - cmd.env_remove(key); - } - } - let out = cmd + let out = crate::vex_e2e_common::hermetic::binary_command() .args(args) .current_dir(cwd) - .env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") .env("DENO_DIR", deno_dir) .output() .expect("spawn socket-patch"); diff --git a/crates/socket-patch-cli/tests/e2e_vex_build/hatch.rs b/crates/socket-patch-cli/tests/e2e_vex_build/hatch.rs index 48b96ae4b..2e3ed3f02 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_build/hatch.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_build/hatch.rs @@ -334,7 +334,8 @@ fn flow(flavor: Flavor, mode: Mode) { .replacen("dependencies = [", "dependencies = [\"idna==3.7\", ", 1) }; std::fs::write(edited.join("pyproject.toml"), edit(&wired)).unwrap(); - let out = std::process::Command::new(vex_e2e_common::binary()) + let out = crate::vex_e2e_common::hermetic::binary_command() + .env_remove("VIRTUAL_ENV") .args(["vendor", "--revert", "--json", "--cwd"]) .arg(&edited) .current_dir(&edited) @@ -381,7 +382,8 @@ fn flow(flavor: Flavor, mode: Mode) { 1, ); std::fs::write(copied.join("pyproject.toml"), &with_copy).unwrap(); - let out = std::process::Command::new(vex_e2e_common::binary()) + let out = crate::vex_e2e_common::hermetic::binary_command() + .env_remove("VIRTUAL_ENV") .args(["vendor", "--revert", "--json", "--cwd"]) .arg(&copied) .current_dir(&copied) diff --git a/crates/socket-patch-cli/tests/e2e_vex_build/poetry.rs b/crates/socket-patch-cli/tests/e2e_vex_build/poetry.rs index 6b45a22ec..5001e9147 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_build/poetry.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_build/poetry.rs @@ -545,10 +545,13 @@ fn socket_patch( service: &PatchService, args: &[&str], ) -> (Option, Value) { - let mut cmd = Command::new(binary()); + let mut cmd = crate::vex_e2e_common::hermetic::binary_command(); + crate::vex_e2e_common::hermetic::scrub_extra( + &mut cmd, + &[crate::vex_e2e_common::hermetic::Extra::Venv], + ); for (key, _) in std::env::vars_os() { - let k = key.to_string_lossy(); - if k.starts_with("SOCKET_") || k.starts_with("POETRY_") || k == "VIRTUAL_ENV" { + if key.to_string_lossy().starts_with("POETRY_") { cmd.env_remove(&key); } } @@ -556,8 +559,6 @@ fn socket_patch( cmd.env(key, value); } let out = cmd - .env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") .current_dir(project) .args(args) .args(["--json", "--yes", "--cwd"]) diff --git a/crates/socket-patch-cli/tests/e2e_vex_lockfile/bun.rs b/crates/socket-patch-cli/tests/e2e_vex_lockfile/bun.rs index 0947c009c..86fec68e7 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_lockfile/bun.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_lockfile/bun.rs @@ -497,19 +497,11 @@ impl Api { // ── running the CLI ────────────────────────────────────────────────────── -/// The CLI with the ambient `SOCKET_*` environment scrubbed, telemetry and -/// socket-cli config off, and no ambient API token. +/// The CLI with the hermetic `SOCKET_*` environment: ambient vars scrubbed, +/// telemetry, socket-cli config and the update check off, and no ambient +/// API token. fn cli() -> Command { - let mut cmd = Command::new(env!("CARGO_BIN_EXE_socket-patch")); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") { - cmd.env_remove(key); - } - } - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") - .env("SOCKET_NO_UPDATE_CHECK", "1"); - cmd + crate::hermetic::binary_command() } /// Parsed stdout envelope of a `--json` run. diff --git a/crates/socket-patch-cli/tests/e2e_vex_lockfile/cargo.rs b/crates/socket-patch-cli/tests/e2e_vex_lockfile/cargo.rs index 15b222c76..dbcef78ef 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_lockfile/cargo.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_lockfile/cargo.rs @@ -51,8 +51,6 @@ //! manifest-less lockfile-wired project. -use crate::common::binary; - use crate::vex_e2e_common; use std::collections::HashMap; @@ -137,17 +135,10 @@ impl Fx { /// Hermetic child: ambient `SOCKET_*` scrubbed, no token / config / /// telemetry, the fixture's private caches. fn command(&self) -> Command { - let mut cmd = Command::new(binary()); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") && key != "SOCKET_NO_CONFIG" { - cmd.env_remove(key); - } - } - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") - .env("SOCKET_NO_API_TOKEN", "1") - .env("CARGO_HOME", &self.cargo_home) - .env_remove("VIRTUAL_ENV"); + let mut cmd = crate::hermetic::binary_command(); + crate::hermetic::scrub_extra(&mut cmd, &[crate::hermetic::Extra::Venv]); + cmd.env("SOCKET_NO_API_TOKEN", "1") + .env("CARGO_HOME", &self.cargo_home); cmd } diff --git a/crates/socket-patch-cli/tests/e2e_vex_lockfile/golang.rs b/crates/socket-patch-cli/tests/e2e_vex_lockfile/golang.rs index 8ed5174b5..440f11b83 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_lockfile/golang.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_lockfile/golang.rs @@ -43,7 +43,6 @@ //! go-patches redirect (no uuid — a documented limitation, asserted below). //! Embedded: `scan --vex` and `apply --vex` on a manifest-less project. -use crate::common::binary; use crate::common::envelope::codes_in; use crate::vex_e2e_common; @@ -132,19 +131,12 @@ impl Fx { /// Hermetic child: ambient `SOCKET_*` scrubbed, no token / config / /// telemetry, the fixture's private caches. fn command(&self) -> Command { - let mut cmd = Command::new(binary()); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") && key != "SOCKET_NO_CONFIG" { - cmd.env_remove(key); - } - } - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") - .env("SOCKET_NO_API_TOKEN", "1") + let mut cmd = crate::hermetic::binary_command(); + crate::hermetic::scrub_extra(&mut cmd, &[crate::hermetic::Extra::Venv]); + cmd.env("SOCKET_NO_API_TOKEN", "1") .env("GOMODCACHE", &self.gomodcache) .env("GOPATH", &self.gopath) - .env_remove("GOFLAGS") - .env_remove("VIRTUAL_ENV"); + .env_remove("GOFLAGS"); cmd } diff --git a/crates/socket-patch-cli/tests/e2e_vex_lockfile/maven.rs b/crates/socket-patch-cli/tests/e2e_vex_lockfile/maven.rs index 09d21ea79..64c46fcf9 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_lockfile/maven.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_lockfile/maven.rs @@ -41,8 +41,6 @@ //! alive by a ledger under `--no-verify`, and agent-mode (`apply`) patches, //! which have no lockfile wiring to discover. -use crate::common::binary; - #[path = "../prebuilt_common/mod.rs"] mod prebuilt_common; @@ -81,17 +79,10 @@ const CVE: &str = "CVE-2026-7100"; /// ecosystem stores the crawlers read pinned to `store` — an ambient /// `~/.m2` must never become installed evidence. fn cli(store: &Path) -> Command { - let mut cmd = Command::new(binary()); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") && key != "SOCKET_NO_CONFIG" { - cmd.env_remove(key); - } - } - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") - .env("MAVEN_REPO_LOCAL", store.join("m2")) - .env_remove("M2_HOME") - .env_remove("VIRTUAL_ENV"); + let mut cmd = crate::hermetic::binary_command(); + crate::hermetic::scrub_extra(&mut cmd, &[crate::hermetic::Extra::Venv]); + cmd.env("MAVEN_REPO_LOCAL", store.join("m2")) + .env_remove("M2_HOME"); cmd } diff --git a/crates/socket-patch-cli/tests/e2e_vex_lockfile/npm.rs b/crates/socket-patch-cli/tests/e2e_vex_lockfile/npm.rs index bb6baaa04..3798bc612 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_lockfile/npm.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_lockfile/npm.rs @@ -454,15 +454,8 @@ fn standalone_vex_dry_run_and_stdout_output_work_without_a_manifest() { assert_no_manifest_or_ledgers(p); // `-O -`: the document is stdout. - let mut cmd = std::process::Command::new(binary()); - for (key, _) in std::env::vars_os() { - if key.to_string_lossy().starts_with("SOCKET_") { - cmd.env_remove(key); - } - } + let mut cmd = crate::hermetic::binary_command(); let out = cmd - .env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") .env("SOCKET_NO_API_TOKEN", "1") .current_dir(p) .args(["vex", "--cwd"]) @@ -700,15 +693,8 @@ fn manifestless_apply_prints_its_summary_before_vex_warnings() { std::fs::write(p.join("package-lock.json"), "not json").unwrap(); let log = p.join("combined.log"); let file = std::fs::File::create(&log).unwrap(); - let mut cmd = std::process::Command::new(binary()); - for (key, _) in std::env::vars_os() { - if key.to_string_lossy().starts_with("SOCKET_") { - cmd.env_remove(key); - } - } + let mut cmd = crate::hermetic::binary_command(); let status = cmd - .env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") .env("SOCKET_NO_API_TOKEN", "1") .current_dir(p) .args(["apply", "--offline", "--cwd"]) diff --git a/crates/socket-patch-cli/tests/e2e_vex_lockfile/nuget.rs b/crates/socket-patch-cli/tests/e2e_vex_lockfile/nuget.rs index 618394daf..3151d7453 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_lockfile/nuget.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_lockfile/nuget.rs @@ -74,16 +74,9 @@ const CVE: &str = "CVE-2026-7100"; /// global packages folder pinned to `store` — an ambient `~/.nuget/packages` /// must never become installed evidence. fn cli(store: &Path) -> Command { - let mut cmd = Command::new(binary()); - for (key, _) in std::env::vars_os() { - if key.to_string_lossy().starts_with("SOCKET_") { - cmd.env_remove(key); - } - } - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") - .env("NUGET_PACKAGES", store.join("nuget")) - .env_remove("VIRTUAL_ENV"); + let mut cmd = crate::hermetic::binary_command(); + crate::hermetic::scrub_extra(&mut cmd, &[crate::hermetic::Extra::Venv]); + cmd.env("NUGET_PACKAGES", store.join("nuget")); cmd } diff --git a/crates/socket-patch-cli/tests/e2e_vex_lockfile/pnpm.rs b/crates/socket-patch-cli/tests/e2e_vex_lockfile/pnpm.rs index c7bb1fb3a..ae197e954 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_lockfile/pnpm.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_lockfile/pnpm.rs @@ -37,7 +37,6 @@ mod prebuilt_common; use std::path::{Path, PathBuf}; -use std::process::Command; use serde_json::Value; use sha2::{Digest, Sha512}; @@ -185,18 +184,11 @@ fn install(root: &Path, index: &[u8]) { } fn socket(root: &Path, args: &[&str]) -> (Option, Value, String) { - let mut cmd = Command::new(binary()); - for (k, _) in std::env::vars_os() { - if k.to_string_lossy().starts_with("SOCKET_") { - cmd.env_remove(k); - } - } + let mut cmd = crate::hermetic::binary_command(); let _fixture = prebuilt_common::prepare_command(&mut cmd, root, args, &[]); let out = cmd .arg("--cwd") .arg(root) - .env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") .current_dir(root) .output() .expect("spawn socket-patch"); diff --git a/crates/socket-patch-cli/tests/e2e_vex_lockfile/poetry.rs b/crates/socket-patch-cli/tests/e2e_vex_lockfile/poetry.rs index 6a4110406..77c0f2fc4 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_lockfile/poetry.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_lockfile/poetry.rs @@ -42,7 +42,6 @@ use crate::vex_e2e_common; use std::collections::HashMap; use std::path::{Path, PathBuf}; -use std::process::Command; use serde_json::{json, Value}; use sha2::{Digest, Sha256}; @@ -1360,9 +1359,10 @@ impl ScanApi { /// An authenticated `socket-patch --json --yes` in `p` against /// `api` → (exit, envelope). fn run_authed(p: &Proj, api: &ScanApi, args: &[&str]) -> (Option, Value) { - let mut cmd = Command::new(binary()); + let mut cmd = crate::hermetic::binary_command(); + crate::hermetic::scrub_extra(&mut cmd, &[crate::hermetic::Extra::Venv]); for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") || key.starts_with("POETRY_") { + if key.starts_with("POETRY_") { cmd.env_remove(key); } } @@ -1370,9 +1370,6 @@ fn run_authed(p: &Proj, api: &ScanApi, args: &[&str]) -> (Option, Value) { cmd.env(key, value); } let out = cmd - .env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") - .env_remove("VIRTUAL_ENV") .current_dir(&p.root) .args(args) .args([ diff --git a/crates/socket-patch-cli/tests/e2e_vex_lockfile/uv.rs b/crates/socket-patch-cli/tests/e2e_vex_lockfile/uv.rs index bcdd2d7db..575478c12 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_lockfile/uv.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_lockfile/uv.rs @@ -39,8 +39,6 @@ //! Plus the embedded entry points (`scan --mode hosted --vex`, `scan --mode vendored //! --vex`, `apply --vex`). -use crate::common::binary; - use crate::vex_e2e_common; use std::collections::HashMap; @@ -669,16 +667,14 @@ impl Api { /// The CLI with the ambient `SOCKET_*` / python / uv discovery environment /// scrubbed and uv's cache pointed into `p.home`. fn cli(p: &Proj) -> Command { - let mut cmd = Command::new(binary()); + let mut cmd = crate::hermetic::binary_command(); + crate::hermetic::scrub_extra(&mut cmd, &[crate::hermetic::Extra::Venv]); for (key, _) in std::env::vars() { - if (key.starts_with("SOCKET_") && key != "SOCKET_NO_CONFIG") || key.starts_with("UV_") { + if key.starts_with("UV_") { cmd.env_remove(key); } } - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") - .env_remove("VIRTUAL_ENV") - .env("UV_CACHE_DIR", p.home.join("uv-cache")); + cmd.env("UV_CACHE_DIR", p.home.join("uv-cache")); cmd } diff --git a/crates/socket-patch-cli/tests/e2e_vex_lockfile/yarn.rs b/crates/socket-patch-cli/tests/e2e_vex_lockfile/yarn.rs index 55b365309..861e8a72f 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_lockfile/yarn.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_lockfile/yarn.rs @@ -43,8 +43,6 @@ //! //! Runs on every OS; no toolchain, no network. -use crate::common::binary; - use crate::vex_e2e_common; use std::collections::HashMap; @@ -512,16 +510,9 @@ fn write_vendor_ledger(cwd: &Path, flavor: Flavor, rel: &str, rec: PatchRecord) /// are the sole source of truth), no token, no socket-cli config, telemetry /// off. fn cli() -> Command { - let mut cmd = Command::new(binary()); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") && key != "SOCKET_NO_CONFIG" { - cmd.env_remove(key); - } - } - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") - .env("SOCKET_NO_API_TOKEN", "1") - .env_remove("VIRTUAL_ENV"); + let mut cmd = crate::hermetic::binary_command(); + crate::hermetic::scrub_extra(&mut cmd, &[crate::hermetic::Extra::Venv]); + cmd.env("SOCKET_NO_API_TOKEN", "1"); cmd } diff --git a/crates/socket-patch-cli/tests/e2e_vex_redirect.rs b/crates/socket-patch-cli/tests/e2e_vex_redirect.rs index c9c64e3da..743e1fcbc 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_redirect.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_redirect.rs @@ -32,7 +32,6 @@ #[path = "common/mod.rs"] mod common; -use common::binary; #[path = "vex_e2e_common/mod.rs"] mod vex_e2e_common; @@ -100,13 +99,7 @@ fn write_hosted_package_lock(cwd: &Path, deps: &[(&str, &str, &str)], pinned: bo /// CLI invocation with the ambient `SOCKET_*` environment scrubbed (explicit /// flags must be the sole source of truth). fn cli() -> Command { - let mut cmd = Command::new(binary()); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") && key != "SOCKET_NO_CONFIG" { - cmd.env_remove(key); - } - } - cmd + common::hermetic::binary_command() } /// Patch record with one npm-shaped file (`package/…`) and one vulnerability. diff --git a/crates/socket-patch-cli/tests/spawn_env_hygiene.rs b/crates/socket-patch-cli/tests/spawn_env_hygiene.rs index 234371a09..862d27199 100644 --- a/crates/socket-patch-cli/tests/spawn_env_hygiene.rs +++ b/crates/socket-patch-cli/tests/spawn_env_hygiene.rs @@ -33,7 +33,6 @@ const PENDING_RAW_SPAWNS: &[&str] = &[ "e2e_cargo.rs", "e2e_composer.rs", "e2e_composer_version_identity.rs", - "e2e_embedded_vex.rs", "e2e_gem.rs", "e2e_golang.rs", "e2e_golang_build.rs", @@ -65,21 +64,6 @@ const PENDING_RAW_SPAWNS: &[&str] = &[ "e2e_vendor_npm_build.rs", "e2e_vendor_pypi_build.rs", "e2e_vendored_production.rs", - "e2e_vex.rs", - "e2e_vex_build/deno.rs", - "e2e_vex_build/hatch.rs", - "e2e_vex_build/poetry.rs", - "e2e_vex_lockfile/bun.rs", - "e2e_vex_lockfile/cargo.rs", - "e2e_vex_lockfile/golang.rs", - "e2e_vex_lockfile/maven.rs", - "e2e_vex_lockfile/npm.rs", - "e2e_vex_lockfile/nuget.rs", - "e2e_vex_lockfile/pnpm.rs", - "e2e_vex_lockfile/poetry.rs", - "e2e_vex_lockfile/uv.rs", - "e2e_vex_lockfile/yarn.rs", - "e2e_vex_redirect.rs", "e2e_vex_vendor.rs", "hosted_memory_common/mod.rs", "hosted_memory_engine.rs", diff --git a/crates/socket-patch-cli/tests/vex_e2e_common/mod.rs b/crates/socket-patch-cli/tests/vex_e2e_common/mod.rs index 13c61564a..a07ec9f2a 100644 --- a/crates/socket-patch-cli/tests/vex_e2e_common/mod.rs +++ b/crates/socket-patch-cli/tests/vex_e2e_common/mod.rs @@ -70,7 +70,6 @@ use std::ffi::OsString; use std::fmt; use std::path::{Path, PathBuf}; -use std::process::Command; use serde_json::Value; @@ -108,6 +107,11 @@ mod oracle; #[allow(unused_imports)] pub use oracle::{binary, git_sha256}; +/// The hermetic `SOCKET_*` environment [`run_vex`] spawns the binary with; +/// suites that load no `common` reach it through here. +#[path = "../common/hermetic.rs"] +pub mod hermetic; + // ── Project shaping ─────────────────────────────────────────────────── /// Delete `.socket/manifest.json` (if present): the shape a hosted / @@ -419,16 +423,9 @@ pub fn run_vex(bin: &Path, project: &Path, run: &VexRun) -> VexOutcome { } args.extend(run.extra_args.iter().map(OsString::from)); - let mut cmd = Command::new(bin); - for (key, _) in std::env::vars_os() { - if key.to_string_lossy().starts_with("SOCKET_") { - cmd.env_remove(key); - } - } - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") - .env("SOCKET_NO_API_TOKEN", "1") - .env_remove("VIRTUAL_ENV"); + let mut cmd = hermetic::command(bin); + hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]); + cmd.env("SOCKET_NO_API_TOKEN", "1"); for (key, value) in &run.envs { cmd.env(key, value); } From 68732affe060b41006207a8254c88b20015e9e41 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 12 Oct 2026 23:36:03 +0000 Subject: [PATCH 3/3] Tidy VEX spawns after code review Drop the opt-out env sets that the hermetic builder already forces, route the remaining VIRTUAL_ENV removals through Extra::Venv, mark the extra hermetic.rs load in vex_e2e_common as an intended duplicate, and say what cli() really does in the doc comments. Refs #823 Assisted-by: Claude Code:claude-opus-5-5 --- crates/socket-patch-cli/tests/e2e_embedded_vex.rs | 6 ++++-- crates/socket-patch-cli/tests/e2e_vex.rs | 6 ++++-- crates/socket-patch-cli/tests/e2e_vex_build/hatch.rs | 10 ++++++---- crates/socket-patch-cli/tests/e2e_vex_redirect.rs | 11 +++++------ crates/socket-patch-cli/tests/vex_e2e_common/mod.rs | 1 + 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/crates/socket-patch-cli/tests/e2e_embedded_vex.rs b/crates/socket-patch-cli/tests/e2e_embedded_vex.rs index 325bfd977..560033bb9 100644 --- a/crates/socket-patch-cli/tests/e2e_embedded_vex.rs +++ b/crates/socket-patch-cli/tests/e2e_embedded_vex.rs @@ -23,8 +23,10 @@ use socket_patch_core::manifest::schema::{ PatchFileInfo, PatchManifest, PatchRecord, VulnerabilityInfo, }; -/// Build a `Command` for the CLI with the entire `SOCKET_*` environment -/// scrubbed from the child process. +/// Build a `Command` for the CLI with the hermetic `SOCKET_*` environment of +/// `tests/common/hermetic.rs`: ambient `SOCKET_*` vars (other than a +/// telemetry opt-out) are removed from the child, and the config, +/// update-check and telemetry opt-outs are forced on. /// /// Every embedded-VEX flag has an env fallback (`--vex`/`SOCKET_VEX`, /// `--vex-product`/`SOCKET_VEX_PRODUCT`, `--vex-no-verify`/ diff --git a/crates/socket-patch-cli/tests/e2e_vex.rs b/crates/socket-patch-cli/tests/e2e_vex.rs index 839e8d1c0..8d83f74c9 100644 --- a/crates/socket-patch-cli/tests/e2e_vex.rs +++ b/crates/socket-patch-cli/tests/e2e_vex.rs @@ -27,8 +27,10 @@ use socket_patch_core::manifest::schema::{ PatchFileInfo, PatchManifest, PatchRecord, VulnerabilityInfo, }; -/// Build a `Command` for the CLI with the entire `SOCKET_*` environment -/// scrubbed from the child process. +/// Build a `Command` for the CLI with the hermetic `SOCKET_*` environment of +/// `tests/common/hermetic.rs`: ambient `SOCKET_*` vars (other than a +/// telemetry opt-out) are removed from the child, and the config, +/// update-check and telemetry opt-outs are forced on. /// /// Every flag these tests rely on has an env fallback: `--product`/ /// `SOCKET_VEX_PRODUCT`, `--no-verify`/`SOCKET_VEX_NO_VERIFY`, `--doc-id`/ diff --git a/crates/socket-patch-cli/tests/e2e_vex_build/hatch.rs b/crates/socket-patch-cli/tests/e2e_vex_build/hatch.rs index 2e3ed3f02..789e7c522 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_build/hatch.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_build/hatch.rs @@ -334,8 +334,9 @@ fn flow(flavor: Flavor, mode: Mode) { .replacen("dependencies = [", "dependencies = [\"idna==3.7\", ", 1) }; std::fs::write(edited.join("pyproject.toml"), edit(&wired)).unwrap(); - let out = crate::vex_e2e_common::hermetic::binary_command() - .env_remove("VIRTUAL_ENV") + let mut cmd = vex_e2e_common::hermetic::binary_command(); + vex_e2e_common::hermetic::scrub_extra(&mut cmd, &[vex_e2e_common::hermetic::Extra::Venv]); + let out = cmd .args(["vendor", "--revert", "--json", "--cwd"]) .arg(&edited) .current_dir(&edited) @@ -382,8 +383,9 @@ fn flow(flavor: Flavor, mode: Mode) { 1, ); std::fs::write(copied.join("pyproject.toml"), &with_copy).unwrap(); - let out = crate::vex_e2e_common::hermetic::binary_command() - .env_remove("VIRTUAL_ENV") + let mut cmd = vex_e2e_common::hermetic::binary_command(); + vex_e2e_common::hermetic::scrub_extra(&mut cmd, &[vex_e2e_common::hermetic::Extra::Venv]); + let out = cmd .args(["vendor", "--revert", "--json", "--cwd"]) .arg(&copied) .current_dir(&copied) diff --git a/crates/socket-patch-cli/tests/e2e_vex_redirect.rs b/crates/socket-patch-cli/tests/e2e_vex_redirect.rs index 743e1fcbc..863009f46 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_redirect.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_redirect.rs @@ -96,8 +96,9 @@ fn write_hosted_package_lock(cwd: &Path, deps: &[(&str, &str, &str)], pinned: bo .unwrap(); } -/// CLI invocation with the ambient `SOCKET_*` environment scrubbed (explicit -/// flags must be the sole source of truth). +/// CLI invocation with the hermetic `SOCKET_*` environment of +/// `tests/common/hermetic.rs` (explicit flags must be the sole source of +/// truth). fn cli() -> Command { common::hermetic::binary_command() } @@ -729,10 +730,8 @@ fn vex_json_env(cwd: &Path, extra: &[&str], envs: &[(&str, &Path)]) -> (Option