diff --git a/crates/socket-patch-cli/tests/scan_api_retry_e2e.rs b/crates/socket-patch-cli/tests/scan_api_retry_e2e.rs index 340c9ac83..affffa45f 100644 --- a/crates/socket-patch-cli/tests/scan_api_retry_e2e.rs +++ b/crates/socket-patch-cli/tests/scan_api_retry_e2e.rs @@ -11,6 +11,9 @@ //! retries without sleeping (the backoff arithmetic itself is pinned on a //! virtual clock in the core crate's `api_retry_e2e.rs`). +#[path = "common/hermetic.rs"] +mod hermetic; + use std::path::Path; use std::process::Command; @@ -43,15 +46,8 @@ fn encode_purl(purl: &str) -> String { } fn scrubbed_cli() -> Command { - let mut cmd = Command::new(env!("CARGO_BIN_EXE_socket-patch")); - for (key, _) in std::env::vars_os() { - let name = key.to_string_lossy(); - if name.starts_with("SOCKET_") && name != "SOCKET_NO_CONFIG" { - cmd.env_remove(&key); - } - } - cmd.env_remove("VIRTUAL_ENV"); - cmd.env("SOCKET_TELEMETRY_DISABLED", "1"); + let mut cmd = hermetic::binary_command(); + hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]); cmd } diff --git a/crates/socket-patch-cli/tests/scan_pnpm_relocated_store_cwd_e2e.rs b/crates/socket-patch-cli/tests/scan_pnpm_relocated_store_cwd_e2e.rs index 1b049e931..1a2f23100 100644 --- a/crates/socket-patch-cli/tests/scan_pnpm_relocated_store_cwd_e2e.rs +++ b/crates/socket-patch-cli/tests/scan_pnpm_relocated_store_cwd_e2e.rs @@ -11,10 +11,9 @@ #[path = "common/mod.rs"] mod common; -use common::binary; +use common::{binary, hermetic, hermetic_command}; use std::path::{Path, PathBuf}; -use std::process::Command; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; @@ -68,18 +67,10 @@ async fn scan_bodies(cwd: &Path) -> String { }))) .mount(&server) .await; - let mut cmd = Command::new(binary()); + let mut cmd = hermetic_command(&binary()); + hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]); cmd.arg("scan").current_dir(cwd); - for (key, _) in std::env::vars_os() { - if key.to_string_lossy().starts_with("SOCKET_") - && key.to_string_lossy() != "SOCKET_NO_CONFIG" - { - cmd.env_remove(&key); - } - } - cmd.env_remove("VIRTUAL_ENV"); cmd.env("CARGO_HOME", cwd.join(".cargo-home")); - cmd.env("SOCKET_TELEMETRY_DISABLED", "1"); let out = cmd .args([ "--json", diff --git a/crates/socket-patch-cli/tests/scan_requirements_lock_only.rs b/crates/socket-patch-cli/tests/scan_requirements_lock_only.rs index da8f60eaa..a27a00c4b 100644 --- a/crates/socket-patch-cli/tests/scan_requirements_lock_only.rs +++ b/crates/socket-patch-cli/tests/scan_requirements_lock_only.rs @@ -26,8 +26,10 @@ //! vendored mode. The package names are fixtures no interpreter on the //! machine has installed, so every hit is a lock-only one. +#[path = "common/hermetic.rs"] +mod hermetic; + use std::path::Path; -use std::process::Command; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; @@ -63,10 +65,9 @@ fn run_scan( ORG_SLUG, ]; argv.extend_from_slice(extra); - let out = Command::new(env!("CARGO_BIN_EXE_socket-patch")) + let out = hermetic::binary_command() .args(&argv) .current_dir(root) - .env("SOCKET_TELEMETRY_DISABLED", "1") .env_remove("VIRTUAL_ENV") .env_remove("CONDA_PREFIX") .envs(envs.iter().copied()) diff --git a/crates/socket-patch-cli/tests/scan_rollout_e2e.rs b/crates/socket-patch-cli/tests/scan_rollout_e2e.rs index 173a32307..6aa472e58 100644 --- a/crates/socket-patch-cli/tests/scan_rollout_e2e.rs +++ b/crates/socket-patch-cli/tests/scan_rollout_e2e.rs @@ -5,13 +5,12 @@ #[path = "common/mod.rs"] mod common; -use common::{binary, git_sha256}; +use common::{binary, git_sha256, hermetic_command}; #[path = "prebuilt_common/mod.rs"] mod prebuilt_common; use std::path::Path; -use std::process::Command; use serde_json::{json, Value}; use wiremock::matchers::{method, path, path_regex}; @@ -273,14 +272,8 @@ fn run(root: &Path, mock: &MockServer, args: &[&str]) -> (i32, String, String) { ORG, ]; argv.extend_from_slice(args); - let mut cmd = Command::new(binary()); + let mut cmd = hermetic_command(&binary()); cmd.args(&argv).current_dir(root); - 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"); let out = cmd.output().expect("run socket-patch"); ( out.status.code().unwrap_or(-1), @@ -797,18 +790,12 @@ async fn a_malformed_env_cap_is_a_usage_error_unless_the_flag_overrides_it() { mount_api(&mock, |_| Grant::Granted).await; let tmp = tempfile::tempdir().unwrap(); write_project(tmp.path(), &["roll-a", "roll-b"]); - let mut cmd = Command::new(binary()); + let mut cmd = hermetic_command(&binary()); cmd.args(["scan", "--json", "--api-url"]) .arg(mock.uri()) .args(["--api-token", "t", "--org", ORG]) .current_dir(tmp.path()); - 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_MAX_NEW_PATCHES", "lots"); + cmd.env("SOCKET_MAX_NEW_PATCHES", "lots"); let out = cmd.output().unwrap(); assert_eq!(out.status.code(), Some(2)); // Under --json the coded usage error goes to stdout (#704). @@ -822,7 +809,7 @@ async fn a_malformed_env_cap_is_a_usage_error_unless_the_flag_overrides_it() { ); // A flag overrides the env value without reading it. - let mut with_flag = Command::new(binary()); + let mut with_flag = hermetic_command(&binary()); with_flag .args([ "scan", @@ -835,14 +822,7 @@ async fn a_malformed_env_cap_is_a_usage_error_unless_the_flag_overrides_it() { .arg(mock.uri()) .args(["--api-token", "t", "--org", ORG]) .current_dir(tmp.path()); - for (key, _) in std::env::vars_os() { - if key.to_string_lossy().starts_with("SOCKET_") { - with_flag.env_remove(&key); - } - } - with_flag - .env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_MAX_NEW_PATCHES", "lots"); + with_flag.env("SOCKET_MAX_NEW_PATCHES", "lots"); let out = with_flag.output().unwrap(); assert_eq!( out.status.code(), diff --git a/crates/socket-patch-cli/tests/scan_vendor_e2e.rs b/crates/socket-patch-cli/tests/scan_vendor_e2e.rs index 25a9e942b..1be9f3f15 100644 --- a/crates/socket-patch-cli/tests/scan_vendor_e2e.rs +++ b/crates/socket-patch-cli/tests/scan_vendor_e2e.rs @@ -9,7 +9,6 @@ mod prebuilt_common; use std::path::{Path, PathBuf}; -use std::process::Command; use sha2::Digest; use wiremock::matchers::{method, path}; @@ -55,7 +54,7 @@ mod vex_e2e_common; #[path = "common/mod.rs"] mod common; -use common::{binary, git_sha256}; +use common::{binary, git_sha256, hermetic_command}; const ORG_SLUG: &str = "test-org"; const UUID: &str = "11111111-1111-4111-8111-111111111111"; @@ -184,10 +183,10 @@ async fn mount_patch_api(mock: &MockServer, uuid: &str) { /// Spawn the built binary in `root` with `extra_env` injected into the /// child environment. fn run_cli_env(root: &Path, argv: &[&str], extra_env: &[(&str, &str)]) -> (i32, String, String) { - let mut cmd = Command::new(binary()); + let mut cmd = hermetic_command(&binary()); cmd.args(argv).current_dir(root); - // Scrub the ambient `SOCKET_*` surface (prefix scrub — fixed lists rot) - // so a developer's shell can't steer the child, then force the telemetry + // The hermetic builder scrubs the ambient `SOCKET_*` surface so a + // developer's shell can't steer the child, and forces the telemetry // kill-switch: telemetry resolves its endpoint from `SOCKET_API_URL` / // `SOCKET_PROXY_URL` env ONLY (`--api-url` is invisible to it), so an // ambient value would send every run's events to the LIVE API with the @@ -195,14 +194,6 @@ fn run_cli_env(root: &Path, argv: &[&str], extra_env: &[(&str, &str)]) -> (i32, // injections survive the scrub — // `scan_vendor_emits_no_telemetry_even_with_endpoint_env` seeds those // endpoint vars deliberately and proves the kill-switch still holds. - for (key, _) in std::env::vars_os() { - if key.to_string_lossy().starts_with("SOCKET_") - && key.to_string_lossy() != "SOCKET_NO_CONFIG" - { - cmd.env_remove(&key); - } - } - cmd.env("SOCKET_TELEMETRY_DISABLED", "1"); for (k, v) in extra_env { cmd.env(k, v); } @@ -744,7 +735,7 @@ async fn scan_vendor_fetch_failure_reports_error() { let tmp = tempfile::tempdir().unwrap(); write_fixture(tmp.path()); - let out = Command::new(binary()) + let out = hermetic_command(&binary()) .args([ "scan", "--mode", @@ -758,7 +749,6 @@ async fn scan_vendor_fetch_failure_reports_error() { ORG_SLUG, ]) .current_dir(tmp.path()) - .env("SOCKET_TELEMETRY_DISABLED", "1") .output() .expect("run"); let code = out.status.code().unwrap_or(-1); @@ -795,9 +785,8 @@ async fn scan_mode_sync_conflicts_are_usage_errors() { &["scan", "--mode", "vendored", "--sync"][..], &["scan", "--mode", "hosted", "--sync"][..], ] { - let out = Command::new(binary()) + let out = hermetic_command(&binary()) .args(argv) - .env("SOCKET_TELEMETRY_DISABLED", "1") .output() .expect("run"); let code = out.status.code().unwrap_or(-1); @@ -1105,7 +1094,7 @@ async fn scan_prune_reverts_unused_vendored_entry() { // Plain prune scan (read-only discovery + GC; no --mode vendored, no --mode agent). let run_prune = || { - let out = Command::new(binary()) + let out = hermetic_command(&binary()) .args([ "scan", "--json", @@ -1234,7 +1223,7 @@ async fn scan_prune_reverts_vendored_entry_after_version_upgrade() { ) .unwrap(); - let out = Command::new(binary()) + let out = hermetic_command(&binary()) .args([ "scan", "--json", @@ -1456,7 +1445,7 @@ async fn scan_vendor_annotates_mismatched_baseline_and_vendors_anyway() { ) .unwrap(); - let out = Command::new(binary()) + let out = hermetic_command(&binary()) .args([ "scan", "--mode", @@ -1617,11 +1606,10 @@ fn run_vendor(root: &Path, extra: &[&str]) -> (i32, serde_json::Value, String) { let mut argv = vec!["vendor", "--json"]; argv.extend_from_slice(extra); let fixture = prebuilt_common::Server::project(root); - let out = Command::new(binary()) + let out = hermetic_command(&binary()) .env("SOCKET_VENDOR_URL", &fixture.uri) .args(&argv) .current_dir(root) - .env("SOCKET_TELEMETRY_DISABLED", "1") .output() .expect("run vendor"); let stdout = String::from_utf8_lossy(&out.stdout).into_owned(); @@ -1735,7 +1723,7 @@ async fn vendor_auto_takes_a_missing_package_from_the_service_without_the_regist ); seed_manifest_and_blob(tmp.path()); let vendor = |extra: &[&str]| { - let mut cmd = Command::new(binary()); + let mut cmd = hermetic_command(&binary()); cmd.args([ "vendor", "--json", @@ -1750,12 +1738,7 @@ async fn vendor_auto_takes_a_missing_package_from_the_service_without_the_regist ]) .args(extra) .current_dir(tmp.path()); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") && key != "SOCKET_NO_CONFIG" { - cmd.env_remove(key); - } - } - let out = cmd.env("SOCKET_TELEMETRY_DISABLED", "1").output().unwrap(); + let out = cmd.output().unwrap(); let stdout = String::from_utf8_lossy(&out.stdout); let v: serde_json::Value = serde_json::from_str(stdout.trim()) .unwrap_or_else(|e| panic!("{e}: {stdout}\n{}", String::from_utf8_lossy(&out.stderr))); @@ -1944,7 +1927,7 @@ async fn scan_discovers_lockfile_only_packages_with_warning() { ); // JSON shape. - let out = Command::new(binary()) + let out = hermetic_command(&binary()) .args([ "scan", "--json", @@ -1956,7 +1939,6 @@ async fn scan_discovers_lockfile_only_packages_with_warning() { ORG_SLUG, ]) .current_dir(tmp.path()) - .env("SOCKET_TELEMETRY_DISABLED", "1") .output() .expect("run"); let stdout = String::from_utf8_lossy(&out.stdout); @@ -1966,7 +1948,7 @@ async fn scan_discovers_lockfile_only_packages_with_warning() { assert_eq!(v["packages"][0]["notInstalled"], true, "{v}"); // Human output: the table marker + the note. - let out = Command::new(binary()) + let out = hermetic_command(&binary()) .args([ "scan", "--api-url", @@ -1979,7 +1961,6 @@ async fn scan_discovers_lockfile_only_packages_with_warning() { "--yes", ]) .current_dir(tmp.path()) - .env("SOCKET_TELEMETRY_DISABLED", "1") .output() .expect("run"); let stdout = String::from_utf8_lossy(&out.stdout); @@ -2080,7 +2061,7 @@ async fn scan_flags_scoped_lockfile_only_package_despite_api_purl_encoding() { // JSON: the additive notInstalled flag must be set even though the // API spelled the purl percent-encoded. - let out = Command::new(binary()) + let out = hermetic_command(&binary()) .args([ "scan", "--json", @@ -2092,7 +2073,6 @@ async fn scan_flags_scoped_lockfile_only_package_despite_api_purl_encoding() { ORG_SLUG, ]) .current_dir(tmp.path()) - .env("SOCKET_TELEMETRY_DISABLED", "1") .output() .expect("run"); let stdout = String::from_utf8_lossy(&out.stdout); @@ -2106,7 +2086,7 @@ async fn scan_flags_scoped_lockfile_only_package_despite_api_purl_encoding() { // Human table: the [NOT INSTALLED] marker must match through the // encoding difference too. - let out = Command::new(binary()) + let out = hermetic_command(&binary()) .args([ "scan", "--api-url", @@ -2119,7 +2099,6 @@ async fn scan_flags_scoped_lockfile_only_package_despite_api_purl_encoding() { "--yes", ]) .current_dir(tmp.path()) - .env("SOCKET_TELEMETRY_DISABLED", "1") .output() .expect("run"); let stdout = String::from_utf8_lossy(&out.stdout); @@ -2143,7 +2122,7 @@ async fn scan_apply_skips_lockfile_only_without_error() { &sri_of(b"unused"), ); - let out = Command::new(binary()) + let out = hermetic_command(&binary()) .args([ "scan", "--json", @@ -2158,7 +2137,6 @@ async fn scan_apply_skips_lockfile_only_without_error() { ORG_SLUG, ]) .current_dir(tmp.path()) - .env("SOCKET_TELEMETRY_DISABLED", "1") .output() .expect("run"); let stdout = String::from_utf8_lossy(&out.stdout); diff --git a/crates/socket-patch-cli/tests/scan_vendor_requirements_unwired.rs b/crates/socket-patch-cli/tests/scan_vendor_requirements_unwired.rs index f4552aaf5..0d4b05fd9 100644 --- a/crates/socket-patch-cli/tests/scan_vendor_requirements_unwired.rs +++ b/crates/socket-patch-cli/tests/scan_vendor_requirements_unwired.rs @@ -13,8 +13,10 @@ //! package name is a fixture no interpreter on the machine has installed //! (a crawled install would bypass the supplement). +#[path = "common/hermetic.rs"] +mod hermetic; + use std::path::Path; -use std::process::Command; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; @@ -98,10 +100,9 @@ fn run_scan_vendored(root: &Path, mock_uri: &str, extra: &[&str]) -> (i32, serde mock_uri, ]; argv.extend_from_slice(extra); - let out = Command::new(env!("CARGO_BIN_EXE_socket-patch")) + let out = hermetic::binary_command() .args(&argv) .current_dir(root) - .env("SOCKET_TELEMETRY_DISABLED", "1") .env_remove("VIRTUAL_ENV") .env_remove("CONDA_PREFIX") .output() @@ -259,7 +260,7 @@ async fn human_prune_reverts_unwired_entry_when_no_package_is_patched() { ) .unwrap(); - let out = Command::new(env!("CARGO_BIN_EXE_socket-patch")) + let out = hermetic::binary_command() .args([ "scan", "--mode", @@ -278,7 +279,6 @@ async fn human_prune_reverts_unwired_entry_when_no_package_is_patched() { &mock.uri(), ]) .current_dir(root) - .env("SOCKET_TELEMETRY_DISABLED", "1") .env_remove("VIRTUAL_ENV") .env_remove("CONDA_PREFIX") .output() diff --git a/crates/socket-patch-cli/tests/spawn_env_hygiene.rs b/crates/socket-patch-cli/tests/spawn_env_hygiene.rs index 007776399..57416b473 100644 --- a/crates/socket-patch-cli/tests/spawn_env_hygiene.rs +++ b/crates/socket-patch-cli/tests/spawn_env_hygiene.rs @@ -118,26 +118,13 @@ const PENDING_RAW_SPAWNS: &[&str] = &[ "mode_migration_pypi.rs", "remove_rollback_api_overrides.rs", "rollback/rollback_multicopy_blob_gate.rs", - "scan_api_retry_e2e.rs", - "scan_pnpm_relocated_store_cwd_e2e.rs", - "scan_requirements_lock_only.rs", - "scan_rollout_e2e.rs", - "scan_vendor_e2e.rs", - "scan_vendor_requirements_unwired.rs", "vendor/e2e_golang_redirect.rs", "vendor/in_process_vendor_bun.rs", "vendor/vendor_gem_lockfile_only_e2e.rs", "vendor/vendor_rerun_no_network_e2e.rs", - "vendor_crash_safety_e2e.rs", - "vendor_eject.rs", - "vendor_eject_bun_lockb.rs", - "vendor_eject_fresh_checkout.rs", - "vendor_jvm_cli.rs", - "vendor_partial_staging_e2e.rs", "vex_e2e_common/uv.rs", "vex_pdm_hatch_common/mod.rs", "vex_pipenv_pip_common/mod.rs", - "vex_terminal_output.rs", "vlt_e2e_common/mod.rs", "vlt_hosted_common/mod.rs", "vlt_vendor_common/mod.rs", diff --git a/crates/socket-patch-cli/tests/vendor_crash_safety_e2e.rs b/crates/socket-patch-cli/tests/vendor_crash_safety_e2e.rs index 3e48b3025..b406e44a2 100644 --- a/crates/socket-patch-cli/tests/vendor_crash_safety_e2e.rs +++ b/crates/socket-patch-cli/tests/vendor_crash_safety_e2e.rs @@ -13,13 +13,12 @@ #[path = "common/mod.rs"] mod common; -use common::{binary, git_sha256}; +use common::{binary, git_sha256, hermetic_command}; #[path = "prebuilt_common/mod.rs"] mod prebuilt_common; use std::path::Path; -use std::process::Command; use serde_json::{json, Value}; use sha2::{Digest, Sha256}; @@ -91,14 +90,8 @@ fn npm_project() -> tempfile::TempDir { /// `vendor --json --offline` through the built binary, optionally crashing /// at `failpoint`. Returns the exit code and stdout. fn vendor(root: &Path, failpoint: Option<&str>) -> (i32, String) { - let mut cmd = Command::new(binary()); + let mut cmd = hermetic_command(&binary()); cmd.args(["vendor", "--json"]).current_dir(root); - 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"); if let Some(point) = failpoint { cmd.env("SOCKET_PATCH_FAILPOINT", point); } @@ -291,16 +284,10 @@ fn crash_before_the_barrier_repairs_a_copy_dir_artifact() { .unwrap(); let run = |failpoint: Option<&str>| { - let mut cmd = Command::new(binary()); + let mut cmd = hermetic_command(&binary()); cmd.args(["vendor", "--json"]) .current_dir(&root) - .env("CARGO_HOME", &cargo_home) - .env("SOCKET_TELEMETRY_DISABLED", "1"); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") && key != "SOCKET_NO_CONFIG" { - cmd.env_remove(key); - } - } + .env("CARGO_HOME", &cargo_home); if let Some(point) = failpoint { cmd.env("SOCKET_PATCH_FAILPOINT", point); } diff --git a/crates/socket-patch-cli/tests/vendor_eject.rs b/crates/socket-patch-cli/tests/vendor_eject.rs index aafb5dfb7..8218d4ef4 100644 --- a/crates/socket-patch-cli/tests/vendor_eject.rs +++ b/crates/socket-patch-cli/tests/vendor_eject.rs @@ -15,12 +15,13 @@ #[path = "common/envelope.rs"] mod envelope; +#[path = "common/hermetic.rs"] +mod hermetic; #[path = "prebuilt_common/mod.rs"] mod prebuilt_common; use envelope::find_event; use std::path::{Path, PathBuf}; -use std::process::Command; use base64::Engine as _; use serde_json::{json, Value}; @@ -72,22 +73,16 @@ impl Project { self.root().join(".socket/vendor/redirect-state.json") } - /// Run the binary against the mocks with every ambient `SOCKET_*` var - /// scrubbed. + /// Run the binary against the mocks in the hermetic `SOCKET_*` + /// environment. fn run(&self, args: &[&str]) -> (i32, String, String) { - let mut cmd = Command::new(env!("CARGO_BIN_EXE_socket-patch")); + let mut cmd = hermetic::binary_command(); cmd.args(args) .arg("--cwd") .arg(self.root()) .current_dir(self.root()); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") { - cmd.env_remove(key); - } - } let uri = self.server.uri(); - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_API_URL", &uri) + cmd.env("SOCKET_API_URL", &uri) .env("SOCKET_API_TOKEN", "fake-token") .env("SOCKET_ORG_SLUG", ORG) .env("SOCKET_NPM_REGISTRY", &uri) diff --git a/crates/socket-patch-cli/tests/vendor_eject_bun_lockb.rs b/crates/socket-patch-cli/tests/vendor_eject_bun_lockb.rs index 18be305fc..6214e7314 100644 --- a/crates/socket-patch-cli/tests/vendor_eject_bun_lockb.rs +++ b/crates/socket-patch-cli/tests/vendor_eject_bun_lockb.rs @@ -24,11 +24,12 @@ #[path = "common/rollback_json.rs"] mod rollback_json; +#[path = "common/hermetic.rs"] +mod hermetic; #[path = "prebuilt_common/mod.rs"] mod prebuilt_common; use std::path::Path; -use std::process::Command; use base64::Engine as _; use serde_json::{json, Value}; @@ -86,16 +87,11 @@ impl Project { } fn run_json(&self, args: &[&str]) -> (i32, Value) { - let mut cmd = Command::new(env!("CARGO_BIN_EXE_socket-patch")); + let mut cmd = hermetic::binary_command(); cmd.args(args) .args(["--json", "--cwd"]) .arg(self.root()) .current_dir(self.root()); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") { - cmd.env_remove(key); - } - } // A registry exported by npm (`npm_config_registry`) or Bun would // steer the takeover's restore off the fixtures' registry (#992). for key in [ @@ -106,8 +102,7 @@ impl Project { cmd.env_remove(key); } let uri = self.server.uri(); - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_API_URL", &uri) + cmd.env("SOCKET_API_URL", &uri) .env("SOCKET_API_TOKEN", "fake-token") .env("SOCKET_ORG_SLUG", ORG) .env("SOCKET_NPM_REGISTRY", &uri) diff --git a/crates/socket-patch-cli/tests/vendor_eject_fresh_checkout.rs b/crates/socket-patch-cli/tests/vendor_eject_fresh_checkout.rs index 00c32cb2b..dce1abede 100644 --- a/crates/socket-patch-cli/tests/vendor_eject_fresh_checkout.rs +++ b/crates/socket-patch-cli/tests/vendor_eject_fresh_checkout.rs @@ -7,6 +7,8 @@ //! from the registry (verified against that checksum) instead of requiring //! an installed tree. Every registry and API endpoint is a wiremock. +#[path = "common/hermetic.rs"] +mod hermetic; #[path = "prebuilt_common/mod.rs"] mod prebuilt_common; @@ -73,7 +75,7 @@ async fn mock_view( .await; } -/// Run the binary with every ambient `SOCKET_*` var scrubbed, an empty +/// Run the binary in the hermetic `SOCKET_*` environment, with an empty /// `CARGO_HOME`, and the API / registries / patch origin on `server`. fn run_json(root: &Path, server: &MockServer, args: &[&str]) -> (i32, Value) { run_json_with(root, server, args, &[]) @@ -103,22 +105,16 @@ fn run_json_with( (out.status.code().unwrap_or(-1), env) } -/// The binary in `root` (the caller adds the args, then `--cwd`), with -/// every ambient `SOCKET_*` var scrubbed, an empty `CARGO_HOME`, and the +/// The binary in `root` (the caller adds the args, then `--cwd`), in the +/// hermetic `SOCKET_*` environment, with an empty `CARGO_HOME` and the /// API / registries / patch origin on `server`. fn command(root: &Path, server: &MockServer, extra: &[(&str, String)]) -> Command { let cargo_home = root.join("../cargo-home"); std::fs::create_dir_all(&cargo_home).unwrap(); - let mut cmd = Command::new(env!("CARGO_BIN_EXE_socket-patch")); + let mut cmd = hermetic::binary_command(); cmd.current_dir(root); - for (key, _) in std::env::vars() { - if key.starts_with("SOCKET_") { - cmd.env_remove(key); - } - } let uri = server.uri(); - cmd.env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_API_URL", &uri) + cmd.env("SOCKET_API_URL", &uri) .env("SOCKET_API_TOKEN", "fake-token") .env("SOCKET_ORG_SLUG", ORG) .env("SOCKET_NPM_REGISTRY", &uri) diff --git a/crates/socket-patch-cli/tests/vendor_jvm_cli.rs b/crates/socket-patch-cli/tests/vendor_jvm_cli.rs index 90b36932e..bd75dd126 100644 --- a/crates/socket-patch-cli/tests/vendor_jvm_cli.rs +++ b/crates/socket-patch-cli/tests/vendor_jvm_cli.rs @@ -10,7 +10,7 @@ #[path = "common/mod.rs"] mod common; -use common::git_sha256; +use common::{git_sha256, hermetic}; #[path = "prebuilt_common/mod.rs"] mod prebuilt_common; @@ -18,7 +18,6 @@ mod prebuilt_common; use std::collections::BTreeMap; use std::io::Write as _; use std::path::Path; -use std::process::Command; use sha2::{Digest as _, Sha256}; @@ -151,12 +150,7 @@ fn fixture(root: &Path, shape: Shape, packages: &[(&str, &str)]) { /// `socket-patch --json --offline --cwd /proj`; `(exit, envelope)`. fn socket(root: &Path, args: &[&str]) -> (Option, serde_json::Value) { - let mut cmd = Command::new(env!("CARGO_BIN_EXE_socket-patch")); - for (k, _) in std::env::vars_os() { - if k.to_string_lossy().starts_with("SOCKET_") { - cmd.env_remove(&k); - } - } + let mut cmd = hermetic::binary_command(); let proj = root.join("proj"); let _fixture = prebuilt_common::prepare_command( &mut cmd, @@ -166,8 +160,6 @@ fn socket(root: &Path, args: &[&str]) -> (Option, serde_json::Value) { ); let out = cmd .args(["--json", "--cwd", proj.to_str().unwrap()]) - .env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") .env("MAVEN_REPO_LOCAL", root.join("m2")) .env_remove("M2_HOME") .output() @@ -751,12 +743,7 @@ const FOO_MAVEN_TREE: &str = "proj/.socket/vendor/maven2/org/example/foo/1.0-soc /// `socket(root, args)` run from `cwd` (relative to `root`) instead of /// the project root; the fixture service reads `cwd`'s manifest. fn socket_in(root: &Path, cwd: &str, args: &[&str]) -> (Option, serde_json::Value) { - let mut cmd = Command::new(env!("CARGO_BIN_EXE_socket-patch")); - for (k, _) in std::env::vars_os() { - if k.to_string_lossy().starts_with("SOCKET_") { - cmd.env_remove(&k); - } - } + let mut cmd = hermetic::binary_command(); let dir = root.join(cwd); let _fixture = prebuilt_common::prepare_command( &mut cmd, @@ -766,8 +753,6 @@ fn socket_in(root: &Path, cwd: &str, args: &[&str]) -> (Option, serde_json: ); let out = cmd .args(["--json", "--cwd", dir.to_str().unwrap()]) - .env("SOCKET_TELEMETRY_DISABLED", "1") - .env("SOCKET_NO_CONFIG", "1") .env("MAVEN_REPO_LOCAL", root.join("m2")) .env_remove("M2_HOME") .output() diff --git a/crates/socket-patch-cli/tests/vendor_partial_staging_e2e.rs b/crates/socket-patch-cli/tests/vendor_partial_staging_e2e.rs index cd61bc305..f2f12d00c 100644 --- a/crates/socket-patch-cli/tests/vendor_partial_staging_e2e.rs +++ b/crates/socket-patch-cli/tests/vendor_partial_staging_e2e.rs @@ -1,11 +1,12 @@ //! Vendoring consumes immutable service artifacts without downloading patch blobs. #[path = "common/envelope.rs"] mod envelope; +#[path = "common/hermetic.rs"] +mod hermetic; #[path = "prebuilt_common/mod.rs"] mod prebuilt_common; use envelope::events; use std::path::Path; -use std::process::Command; use serde_json::{json, Value}; use socket_patch_core::hash::git_sha256::compute_git_sha256_from_bytes; @@ -160,7 +161,7 @@ fn tgz_members(tgz: &Path) -> std::collections::BTreeMap> { } fn vendor_cli_with_source(root: &Path, api_url: &str, source: &str) -> (i32, Value, String) { - let mut cmd = Command::new(env!("CARGO_BIN_EXE_socket-patch")); + let mut cmd = hermetic::binary_command(); cmd.args([ "vendor", "--json", @@ -174,12 +175,6 @@ fn vendor_cli_with_source(root: &Path, api_url: &str, source: &str) -> (i32, Val ORG, ]) .current_dir(root); - 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"); let out = cmd.output().expect("spawn socket-patch"); let stdout = String::from_utf8_lossy(&out.stdout).into_owned(); let stderr = String::from_utf8_lossy(&out.stderr).into_owned(); @@ -240,7 +235,7 @@ async fn mount_discovery(server: &MockServer) { } fn scan_vendored_cli(root: &Path, api_url: &str) -> (i32, Value, String) { - let mut cmd = Command::new(env!("CARGO_BIN_EXE_socket-patch")); + let mut cmd = hermetic::binary_command(); cmd.args([ "scan", "--json", @@ -257,12 +252,6 @@ fn scan_vendored_cli(root: &Path, api_url: &str) -> (i32, Value, String) { ORG, ]) .current_dir(root); - 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"); let out = cmd.output().expect("spawn socket-patch"); let stdout = String::from_utf8_lossy(&out.stdout).into_owned(); let stderr = String::from_utf8_lossy(&out.stderr).into_owned(); @@ -394,7 +383,7 @@ async fn local_build_source_is_rejected() { let tmp = tempfile::tempdir().unwrap(); fixture(tmp.path()); let before = std::fs::read(tmp.path().join("package-lock.json")).unwrap(); - let out = Command::new(env!("CARGO_BIN_EXE_socket-patch")) + let out = hermetic::binary_command() .current_dir(tmp.path()) .args([ "vendor", diff --git a/crates/socket-patch-cli/tests/vex_terminal_output.rs b/crates/socket-patch-cli/tests/vex_terminal_output.rs index f9a6a0203..1f2ce0b50 100644 --- a/crates/socket-patch-cli/tests/vex_terminal_output.rs +++ b/crates/socket-patch-cli/tests/vex_terminal_output.rs @@ -1,9 +1,12 @@ //! Terminal-output contract for the standalone `vex` command: what a human //! (and a `--json` consumer) sees for dry runs, `-O -`, stale-document //! cleanup, omission reporting and flag typos. Fixtures are self-contained -//! tempdir projects driven through the built binary with a scrubbed +//! tempdir projects driven through the built binary with the hermetic //! `SOCKET_*` environment (the parent env is never mutated). +#[path = "common/hermetic.rs"] +mod hermetic; + use std::collections::HashMap; use std::path::Path; use std::process::{Command, Output}; @@ -16,14 +19,7 @@ use socket_patch_core::manifest::schema::{ const STALE_OPENVEX_DOC: &str = r#"{"@context":"https://openvex.dev/ns/v0.2.0","@id":"urn:uuid:stale","author":"Socket","timestamp":"2020-01-01T00:00:00Z","version":1,"statements":[]}"#; 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"); - cmd + hermetic::binary_command() } fn record(uuid: &str, ghsa: &str) -> PatchRecord {