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
3 changes: 2 additions & 1 deletion crates/socket-patch-cli/tests/common/hermetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ use std::process::Command;
/// never sees it, but if a scrub line is ever dropped the seed (rather than a
/// developer's ambient shell, which the suites can't rely on) turns the tests
/// red immediately.
const HOSTILE_SEEDS: &[(&str, &str)] = &[
pub const HOSTILE_SEEDS: &[(&str, &str)] = &[
("SOCKET_CWD", "/nonexistent"),
("SOCKET_GLOBAL", "true"),
("SOCKET_GLOBAL_PREFIX", "/nonexistent"),
("SOCKET_DRY_RUN", "true"),
("SOCKET_SAVE_ONLY", "true"),
("SOCKET_ECOSYSTEMS", "cargo"),
("SOCKET_MANIFEST_PATH", "/nonexistent/manifest.json"),
("SOCKET_JSON", "true"),
Expand Down
27 changes: 12 additions & 15 deletions crates/socket-patch-cli/tests/e2e_nuget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@

#[path = "common/mod.rs"]
mod common;
use common::binary;
use common::{binary, hermetic};

use std::path::Path;
use std::process::{Command, Output};
use std::process::Output;

use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
Expand Down Expand Up @@ -46,28 +46,25 @@ async fn start_proxy() -> MockServer {
/// Run the binary as a blocking subprocess (off the async runtime so the
/// in-test proxy can service its requests concurrently), pinned to `proxy_url`.
///
/// `SOCKET_API_TOKEN` is stripped so the binary deterministically takes the
/// public-proxy path (an ambient token would flip it onto the authenticated
/// API, bypassing `--proxy-url`), and every other variable that could
/// redirect the API elsewhere or disable it is scrubbed so an ambient value
/// can't quietly change what the scan reports.
/// The child gets the hermetic `SOCKET_*` environment (no token, so the
/// binary deterministically takes the public-proxy path `--proxy-url`
/// pins). The scans are unscoped, so an activated venv is scrubbed and
/// `HOME` points at a stand-in (`jvm_env::isolate_cli`): the developer's
/// venv, `~/.m2` or user site-packages would add packages to the counts
/// these tests assert.
async fn run(args: &[&str], cwd: &Path, nuget_packages: &Path, proxy_url: &str) -> Output {
let mut args: Vec<String> = args.iter().map(|s| s.to_string()).collect();
args.extend(["--proxy-url".to_string(), proxy_url.to_string()]);
let cwd = cwd.to_path_buf();
let nuget_packages = nuget_packages.to_path_buf();
tokio::task::spawn_blocking(move || {
let arg_refs: Vec<&str> = args.iter().map(String::as_str).collect();
Command::new(binary())
.args(&arg_refs)
let mut cmd = hermetic::command(&binary());
hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]);
common::jvm_env::isolate_cli(&mut cmd);
cmd.args(&arg_refs)
.current_dir(&cwd)
.env("NUGET_PACKAGES", &nuget_packages)
.env_remove("SOCKET_API_TOKEN")
.env_remove("SOCKET_CLI_API_TOKEN")
.env_remove("SOCKET_API_URL")
.env_remove("SOCKET_OFFLINE")
.env_remove("SOCKET_PROXY_URL")
.env_remove("SOCKET_BATCH_SIZE")
.output()
.expect("Failed to run socket-patch binary")
})
Expand Down
11 changes: 2 additions & 9 deletions crates/socket-patch-cli/tests/e2e_nuget_dotnet_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,19 +376,12 @@ fn b64(bytes: &[u8]) -> String {
/// Run socket-patch (`args`) with a scrubbed environment and the crawler's
/// global packages folder pinned to `store` → (exit, envelope, stderr).
fn socket_patch(cwd: &Path, store: &Path, args: &[&str]) -> (Option<i32>, 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 mut cmd = hermetic::command(&binary());
hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]);
let out = cmd
.args(args)
.current_dir(cwd)
.env("SOCKET_TELEMETRY_DISABLED", "1")
.env("SOCKET_NO_CONFIG", "1")
.env("NUGET_PACKAGES", store)
.env_remove("VIRTUAL_ENV")
.output()
.expect("spawn socket-patch");
let stderr = String::from_utf8_lossy(&out.stderr).into_owned();
Expand Down
31 changes: 10 additions & 21 deletions crates/socket-patch-cli/tests/e2e_pypi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@

#[path = "common/mod.rs"]
mod common;
use common::{binary, git_sha256};
use common::{binary, git_sha256, hermetic};

use std::path::{Path, PathBuf};
use std::process::{Command, Output};

use socket_patch_cli::args::{GLOBAL_ARG_ENV_VARS, LOCAL_ARG_ENV_VARS};

use common::cache_env;

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -52,30 +50,21 @@ fn git_sha256_file(path: &Path) -> String {

/// Run the CLI binary with the given args, setting `cwd` as the working dir.
///
/// The environment is pinned hard: every env var the CLI binds (the canonical
/// `GLOBAL_ARG_ENV_VARS` / `LOCAL_ARG_ENV_VARS` lists) is scrubbed so ambient
/// developer/CI configuration can't change the lifecycle under test. Scrubbing `SOCKET_API_TOKEN` also forces the
/// The child gets the hermetic `SOCKET_*` environment, which forces the
/// public-proxy (free-tier) path this suite relies on. The stakes are higher
/// here than in the offline suites: an inherited `SOCKET_GLOBAL=true` takes
/// `get`/`apply` out of the temp venv and patches the host's real
/// site-packages, while `SOCKET_MANIFEST_PATH` relocates `.socket/` (verified
/// to fail `get` outright) and `SOCKET_DRY_RUN` / `SOCKET_SAVE_ONLY` turn the
/// apply steps into silent no-ops. The hostile seeds below are cleared by the
/// scrub loop — the child never sees them — but if the scrub is ever dropped
/// the seeds (rather than a developer's ambient shell, which this suite can't
/// rely on) turn the tests red immediately.
/// apply steps into silent no-ops. An activated venv is scrubbed too, so the
/// crawler patches the suite's own `.venv` rather than the developer's, and
/// `HOME` points at a stand-in (`jvm_env::isolate_cli`) so the user site
/// under the real `~` stays out of the scans.
fn run(cwd: &Path, args: &[&str]) -> (i32, String, String) {
let mut cmd = Command::new(binary());
cmd.args(args)
.current_dir(cwd)
.env("SOCKET_GLOBAL", "true")
.env("SOCKET_GLOBAL_PREFIX", "/nonexistent")
.env("SOCKET_DRY_RUN", "true")
.env("SOCKET_SAVE_ONLY", "true")
.env("SOCKET_MANIFEST_PATH", "/nonexistent/manifest.json");
for var in GLOBAL_ARG_ENV_VARS.iter().chain(LOCAL_ARG_ENV_VARS) {
cmd.env_remove(var);
}
let mut cmd = hermetic::command(&binary());
hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]);
common::jvm_env::isolate_cli(&mut cmd);
cmd.args(args).current_dir(cwd);
let out: Output = cmd.output().expect("failed to execute socket-patch binary");

let code = out.status.code().unwrap_or(-1);
Expand Down
11 changes: 1 addition & 10 deletions crates/socket-patch-cli/tests/e2e_pypi_multi_copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
mod common;

use std::path::{Path, PathBuf};
use std::process::Command;

use serde_json::{json, Value};

Expand Down Expand Up @@ -48,19 +47,11 @@ const DISCOVERY_VARS: &[&str] = &[
/// Run the binary in `cwd` with the `SOCKET_*` and discovery env scrubbed
/// and exactly `env` added.
fn run(cwd: &Path, args: &[&str], env: &[(&str, &str)]) -> (i32, String, String) {
let mut cmd = Command::new(common::binary());
let mut cmd = common::hermetic::command(&common::binary());
cmd.args(args).current_dir(cwd);
for (key, _) in std::env::vars_os() {
let name = key.to_string_lossy();
if name.starts_with("SOCKET_") && !name.contains("TELEMETRY") {
cmd.env_remove(&key);
}
}
for name in DISCOVERY_VARS {
cmd.env_remove(name);
}
cmd.env("SOCKET_NO_CONFIG", "1")
.env("SOCKET_NO_UPDATE_CHECK", "1");
for (k, v) in env {
cmd.env(k, v);
}
Expand Down
18 changes: 1 addition & 17 deletions crates/socket-patch-cli/tests/spawn_env_hygiene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ const PENDING_RAW_SPAWNS: &[&str] = &[
"e2e_hosted_production.rs",
"e2e_maven.rs",
"e2e_npm.rs",
"e2e_nuget.rs",
"e2e_nuget_dotnet_build.rs",
"e2e_pypi.rs",
"e2e_pypi_multi_copy.rs",
"e2e_redirect_bun_build.rs",
"e2e_redirect_cargo_shapes.rs",
"e2e_scan.rs",
Expand Down Expand Up @@ -275,19 +271,7 @@ fn workflow_env_copies_carry_every_opt_out() {
#[serial]
fn command_never_leaks_its_hostile_seeds() {
let env = effective_env(&hermetic::command(Path::new("socket-patch")));
for key in [
"SOCKET_CWD",
"SOCKET_GLOBAL",
"SOCKET_GLOBAL_PREFIX",
"SOCKET_DRY_RUN",
"SOCKET_ECOSYSTEMS",
"SOCKET_MANIFEST_PATH",
"SOCKET_JSON",
"SOCKET_SILENT",
"SOCKET_VERBOSE",
"SOCKET_UPDATE_BASE_URL",
"SOCKET_UPDATE_STATE_DIR",
] {
for (key, _) in hermetic::HOSTILE_SEEDS {
assert_eq!(get(&env, key), None, "{key} seed must be scrubbed");
}
}
Expand Down
Loading