From 93768c117c283251493449a5f0440deddd5f6435 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 13 Oct 2026 16:08:10 +0000 Subject: [PATCH 1/2] Spawn VEX test CLIs through hermetic builder The uv, PDM/Hatch and Pipenv/pip VEX test helpers started the socket-patch child with a bare Command::new and their own SOCKET_* sweep. Those sweeps had drifted from tests/common/hermetic.rs: none forced the update notifier off, none removed the flag-bound vars by name, and none had the hostile seeds that catch a dropped scrub. The three helpers now start from hermetic::command and keep only their tool scrubs (python/uv/pip config, VIRTUAL_ENV, PIPENV_*). The uv helper's python scrub no longer sweeps SOCKET_*, so it can run after the builder without removing the opt-outs it forces on; its uv/python children get the SOCKET_* sweep through scrub_tool_env. The files leave PENDING_RAW_SPAWNS (32 -> 29) so the ratchet keeps them hermetic. Refs #823 Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/spawn_env_hygiene.rs | 3 -- .../tests/vex_e2e_common/uv.rs | 41 ++++++++++--------- .../tests/vex_pdm_hatch_common/mod.rs | 21 ++++------ .../tests/vex_pipenv_pip_common/mod.rs | 23 +++++------ 4 files changed, 39 insertions(+), 49 deletions(-) diff --git a/crates/socket-patch-cli/tests/spawn_env_hygiene.rs b/crates/socket-patch-cli/tests/spawn_env_hygiene.rs index 4a441ea12..14ffff8a5 100644 --- a/crates/socket-patch-cli/tests/spawn_env_hygiene.rs +++ b/crates/socket-patch-cli/tests/spawn_env_hygiene.rs @@ -55,9 +55,6 @@ const PENDING_RAW_SPAWNS: &[&str] = &[ "vendor/in_process_vendor_bun.rs", "vendor/vendor_gem_lockfile_only_e2e.rs", "vendor/vendor_rerun_no_network_e2e.rs", - "vex_e2e_common/uv.rs", - "vex_pdm_hatch_common/mod.rs", - "vex_pipenv_pip_common/mod.rs", "vlt_e2e_common/mod.rs", "vlt_hosted_common/mod.rs", "vlt_vendor_common/mod.rs", diff --git a/crates/socket-patch-cli/tests/vex_e2e_common/uv.rs b/crates/socket-patch-cli/tests/vex_e2e_common/uv.rs index ce9171e3d..8f9fae0d9 100644 --- a/crates/socket-patch-cli/tests/vex_e2e_common/uv.rs +++ b/crates/socket-patch-cli/tests/vex_e2e_common/uv.rs @@ -82,8 +82,8 @@ use serde_json::{json, Value}; use sha2::{Digest, Sha256}; use crate::vex_e2e_common::{ - assert_attested, assert_not_attested, binary, git_sha256, patch_view, run_vex, statements_for, - strip_ledgers, strip_manifest, Marker, PatchApi, VexOutcome, VexRun, VexVia, + assert_attested, assert_not_attested, binary, git_sha256, hermetic, patch_view, run_vex, + statements_for, strip_ledgers, strip_manifest, Marker, PatchApi, VexOutcome, VexRun, VexVia, }; /// Appended to the installed module by the synthetic patch. @@ -196,7 +196,7 @@ impl Uv { /// oracle), the cache sandbox applied, then `cache` as `UV_CACHE_DIR`. fn command(&self, cwd: &Path, args: &[OsString], cache: &Path) -> Command { let mut cmd = Command::new(&self.exe); - scrub_python_env(&mut cmd); + scrub_tool_env(&mut cmd); cmd.args(args).current_dir(cwd); crate::cache_env::isolate(&mut cmd); cmd.env("UV_CACHE_DIR", cache); @@ -231,14 +231,21 @@ impl Uv { } } +/// [`scrub_python_env`] plus the ambient `SOCKET_*` sweep, for the uv / +/// python / pip children. +fn scrub_tool_env(cmd: &mut Command) { + hermetic::scrub_socket_vars(cmd); + scrub_python_env(cmd); +} + +/// Remove the ambient python / uv / pip config and `VIRTUAL_ENV`. `SOCKET_*` +/// is left to the caller: `hermetic::command` for the binary (sweeping it +/// here would drop the opt-outs that builder forces on) and +/// [`scrub_tool_env`] for everything else. fn scrub_python_env(cmd: &mut Command) { for (key, _) in std::env::vars_os() { let name = key.to_string_lossy(); - if name.starts_with("PYTHON") - || name.starts_with("UV_") - || name.starts_with("PIP_") - || name.starts_with("SOCKET_") - { + if name.starts_with("PYTHON") || name.starts_with("UV_") || name.starts_with("PIP_") { cmd.env_remove(&key); } } @@ -690,17 +697,13 @@ impl ScanApi { } } -/// Run `socket-patch ` with the ambient `SOCKET_*` / python env -/// scrubbed. +/// Run `socket-patch ` with the hermetic `SOCKET_*` environment and +/// the ambient python env scrubbed. fn socket_patch(cwd: &Path, args: &[&str]) -> Output { - let mut cmd = Command::new(binary()); + let mut cmd = hermetic::command(&binary()); scrub_python_env(&mut cmd); let _fixture = prebuilt_common::prepare_command(&mut cmd, cwd, args, &[]); - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") - .current_dir(cwd) - .output() - .expect("spawn socket-patch") + cmd.current_dir(cwd).output().expect("spawn socket-patch") } fn envelope(out: &Output, what: &str) -> Value { @@ -865,7 +868,7 @@ fn build(uv: &Uv, lane: Lane, mode: Mode, tmp: &Path) -> Result { Mode::Vendored => &["six==1.16.0"], }; let mut cmd = Command::new(host_python()); - scrub_python_env(&mut cmd); + scrub_tool_env(&mut cmd); crate::cache_env::isolate(&mut cmd); let out = cmd .args(["-m", "pip", "lock"]) @@ -1063,7 +1066,7 @@ fn install(uv: &Uv, lane: Lane, dir: &Path, cache: &Path, offline: bool, frozen: fn oracle(_uv: &Uv, dir: &Path, _cache: &Path) -> String { let mut cmd = Command::new(venv_python(dir)); - scrub_python_env(&mut cmd); + scrub_tool_env(&mut cmd); let out = cmd .args(["-c", ORACLE]) .current_dir(dir) @@ -2207,7 +2210,7 @@ pub fn production_manifestless(p: &Production<'_>) { strip_manifest(&fresh); let sync = |dir: &Path, cache: &Path, offline: bool| { let mut cmd = Command::new(p.uv); - scrub_python_env(&mut cmd); + scrub_tool_env(&mut cmd); crate::cache_env::isolate(&mut cmd); if let Some(py) = std::env::var_os("SOCKET_PATCH_UV_E2E_PYTHON").filter(|p| !p.is_empty()) { cmd.env("UV_PYTHON", py); diff --git a/crates/socket-patch-cli/tests/vex_pdm_hatch_common/mod.rs b/crates/socket-patch-cli/tests/vex_pdm_hatch_common/mod.rs index f39e29e85..851519a86 100644 --- a/crates/socket-patch-cli/tests/vex_pdm_hatch_common/mod.rs +++ b/crates/socket-patch-cli/tests/vex_pdm_hatch_common/mod.rs @@ -46,8 +46,8 @@ use std::sync::{Arc, Mutex, OnceLock}; use serde_json::Value; use crate::vex_e2e_common::{ - assert_attested, binary, git_sha256, run_vex, statements_for, Marker, PatchApi, VexOutcome, - VexRun, VexVia, + assert_attested, binary, git_sha256, hermetic, run_vex, statements_for, Marker, PatchApi, + VexOutcome, VexRun, VexVia, }; pub const ORG: &str = "test-org"; @@ -175,19 +175,12 @@ pub fn cells(flavors: &[Flavor]) -> Vec<(Flavor, Mode)> { // ── process + fs helpers ────────────────────────────────────────────── -/// The CLI with the ambient environment neutralized: every `SOCKET_*` -/// scrubbed, telemetry and socket-cli config off, no `VIRTUAL_ENV` (a -/// python-crawler input). +/// The CLI with the hermetic `SOCKET_*` environment (`hermetic::command`: +/// telemetry, socket-cli config and the update notifier off) and no +/// `VIRTUAL_ENV` (a python-crawler input). pub fn cli() -> 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_remove("VIRTUAL_ENV"); + let mut cmd = hermetic::command(&binary()); + hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]); cmd } diff --git a/crates/socket-patch-cli/tests/vex_pipenv_pip_common/mod.rs b/crates/socket-patch-cli/tests/vex_pipenv_pip_common/mod.rs index 4d4010952..60ddd3d25 100644 --- a/crates/socket-patch-cli/tests/vex_pipenv_pip_common/mod.rs +++ b/crates/socket-patch-cli/tests/vex_pipenv_pip_common/mod.rs @@ -52,8 +52,8 @@ use std::sync::{Arc, Mutex, OnceLock}; use serde_json::Value; use crate::vex_e2e_common::{ - assert_attested, binary, git_sha256, purl_matches, run_vex, statements_for, Marker, PatchApi, - VexOutcome, VexRun, VexVia, + assert_attested, binary, git_sha256, hermetic, purl_matches, run_vex, statements_for, Marker, + PatchApi, VexOutcome, VexRun, VexVia, }; pub const ORG: &str = "test-org"; @@ -215,22 +215,19 @@ pub fn pipfile_lock(category: &str, extras: Option<&[&str]>) -> String { // ── process + fs helpers ────────────────────────────────────────────── -/// The CLI with the ambient environment neutralized: every `SOCKET_*` / -/// `PIPENV_*` scrubbed, telemetry and socket-cli config off, no -/// `VIRTUAL_ENV` (a python-crawler input), Pipenv's out-of-tree venv lookup -/// pointed at an empty dir. +/// The CLI with the hermetic `SOCKET_*` environment (`hermetic::command`: +/// telemetry, socket-cli config and the update notifier off), every +/// `PIPENV_*` scrubbed, no `VIRTUAL_ENV` (a python-crawler input), and +/// Pipenv's out-of-tree venv lookup pointed at an empty dir. pub fn cli(scratch: &Path) -> Command { - let mut cmd = Command::new(binary()); + let mut cmd = hermetic::command(&binary()); + hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]); for (key, _) in std::env::vars_os() { - let key = key.to_string_lossy().into_owned(); - if key.starts_with("SOCKET_") || key.starts_with("PIPENV_") { + if key.to_string_lossy().starts_with("PIPENV_") { cmd.env_remove(&key); } } - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") - .env("WORKON_HOME", workon_home(scratch)) - .env_remove("VIRTUAL_ENV"); + cmd.env("WORKON_HOME", workon_home(scratch)); cmd } From 9078ed4f0d7ba7ee9513be7f5a88f1452cfde375 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 13 Oct 2026 16:12:35 +0000 Subject: [PATCH 2/2] Scrub ambient PDM config in VEX PDM test CLI The PDM/Hatch VEX helper now also applies hermetic Extra::Pdm, so an ambient PDM_* or UV_PROJECT_ENVIRONMENT can't move the venv the CLI's python crawler reads (found by /code-review high). Refs #823 Assisted-by: Claude Code:claude-opus-5-5 --- crates/socket-patch-cli/tests/vex_pdm_hatch_common/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/socket-patch-cli/tests/vex_pdm_hatch_common/mod.rs b/crates/socket-patch-cli/tests/vex_pdm_hatch_common/mod.rs index 851519a86..3ca2682ac 100644 --- a/crates/socket-patch-cli/tests/vex_pdm_hatch_common/mod.rs +++ b/crates/socket-patch-cli/tests/vex_pdm_hatch_common/mod.rs @@ -176,11 +176,12 @@ pub fn cells(flavors: &[Flavor]) -> Vec<(Flavor, Mode)> { // ── process + fs helpers ────────────────────────────────────────────── /// The CLI with the hermetic `SOCKET_*` environment (`hermetic::command`: -/// telemetry, socket-cli config and the update notifier off) and no -/// `VIRTUAL_ENV` (a python-crawler input). +/// telemetry, socket-cli config and the update notifier off), no +/// `VIRTUAL_ENV` and no `PDM_*` / `UV_PROJECT_ENVIRONMENT` (python-crawler +/// inputs that move the venv it reads). pub fn cli() -> Command { let mut cmd = hermetic::command(&binary()); - hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]); + hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv, hermetic::Extra::Pdm]); cmd }