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
6 changes: 6 additions & 0 deletions crates/socket-patch-cli/tests/common/hermetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ pub fn command(bin: &Path) -> Command {
cmd.env_remove(k);
}
cmd.env_remove("SOCKET_API_TOKEN");
// The `SOCKET_CLI_*` peer names too: the binary promotes each onto its
// canonical `SOCKET_*` name, so a peer token set mid-run would put the
// child on the authenticated path.
for (_, peer) in socket_patch_core::utils::env_compat::PEER_ENV_ALIASES {
cmd.env_remove(peer);
}
scrub_socket_vars(&mut cmd);
// Belt-and-braces on top of the `.cargo/config.toml` `[env]` defaults
// (see `FORCED_OPT_OUTS`): a developer's real `socket login` would flip
Expand Down
73 changes: 22 additions & 51 deletions crates/socket-patch-cli/tests/e2e_golang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,53 +50,37 @@ async fn mount_batch(server: &MockServer) {
.await;
}

/// The binary with the environment pinned hard: `hermetic::command` scrubs
/// every ambient `SOCKET_*` (so `SOCKET_OFFLINE`, `SOCKET_ECOSYSTEMS`,
/// `SOCKET_GLOBAL*` and the output-mode trio can't change what the crawler
/// discovers or whether it calls home), `VIRTUAL_ENV` is removed (the scans
/// are unscoped, so an activated venv would add its packages to the batch),
/// `GOPATH` is removed, and the token/url/org steer the API at the in-test
/// server. Callers pin the module cache.
fn golang_command(cwd: &Path, api_url: &str) -> std::process::Command {
let mut cmd = common::hermetic::command(&binary());
common::hermetic::scrub_extra(&mut cmd, &[common::hermetic::Extra::Venv]);
cmd.current_dir(cwd)
.env("SOCKET_API_URL", api_url)
.env("SOCKET_API_TOKEN", "sktsec_dummy_e2e_golang_token_api")
.env("SOCKET_ORG_SLUG", ORG)
.env_remove("GOPATH");
cmd
}

/// Run the binary as a blocking subprocess (off the async runtime so the
/// wiremock server can service the request concurrently).
///
/// The environment is pinned hard: `GOMODCACHE` fixes the crawl root, the
/// token/url/org steer the API at the in-test server, and every variable that
/// could redirect the API elsewhere or disable it (`GOPATH`, `SOCKET_OFFLINE`,
/// the proxy URLs) is scrubbed so an ambient value in the test environment
/// can't quietly change what the crawler discovers or whether it calls home.
/// wiremock server can service the request concurrently), with
/// [`golang_command`]'s env and `GOMODCACHE` fixing the crawl root.
async fn run(args: &[&str], cwd: &Path, gomodcache: &Path, api_url: &str) -> Output {
let args: Vec<String> = args.iter().map(|s| s.to_string()).collect();
let cwd = cwd.to_path_buf();
let gomodcache = gomodcache.to_path_buf();
let api_url = api_url.to_string();
tokio::task::spawn_blocking(move || {
let arg_refs: Vec<&str> = args.iter().map(String::as_str).collect();
std::process::Command::new(binary())
golang_command(&cwd, &api_url)
.args(&arg_refs)
.current_dir(&cwd)
.env("GOMODCACHE", &gomodcache)
.env("SOCKET_API_URL", &api_url)
.env("SOCKET_API_TOKEN", "sktsec_dummy_e2e_golang_token_api")
.env("SOCKET_ORG_SLUG", ORG)
// Ambient-pollution regression guard: seed a hostile value for
// each scan-affecting variable, then scrub it below. `env_remove`
// clears explicitly-set values too, so the child never sees the
// seeds — but if a scrub line is ever dropped, the seed (rather
// than a developer's ambient shell, which this suite can't rely
// on) turns the tests red immediately. Each seed was verified to
// break the suite when inherited: `SOCKET_ECOSYSTEMS` filters the
// go crawler out entirely, `SOCKET_GLOBAL` / `SOCKET_GLOBAL_PREFIX`
// redirect the crawl away from the pinned `GOMODCACHE`, and
// `SOCKET_JSON` / `SOCKET_SILENT` replace or suppress the human
// output the tests assert on.
.env("SOCKET_ECOSYSTEMS", "npm")
.env("SOCKET_GLOBAL", "true")
.env("SOCKET_GLOBAL_PREFIX", "/nonexistent")
.env("SOCKET_JSON", "true")
.env("SOCKET_SILENT", "true")
.env_remove("SOCKET_ECOSYSTEMS")
.env_remove("SOCKET_GLOBAL")
.env_remove("SOCKET_GLOBAL_PREFIX")
.env_remove("SOCKET_JSON")
.env_remove("SOCKET_SILENT")
.env_remove("GOPATH")
.env_remove("SOCKET_OFFLINE")
.env_remove("SOCKET_PROXY_URL")
.env_remove("SOCKET_BATCH_SIZE")
.output()
.expect("Failed to run socket-patch binary")
})
Expand Down Expand Up @@ -317,25 +301,12 @@ async fn scan_finds_the_cache_named_by_the_go_env_file() {

let (project2, api) = (project.clone(), api_url.clone());
let output = tokio::task::spawn_blocking(move || {
common::hermetic::command(&binary())
golang_command(&project2, &api)
.args(["scan", "--json", "--cwd", project2.to_str().unwrap()])
.current_dir(&project2)
.env("GOENV", &goenv)
.env("HOME", &home)
.env("USERPROFILE", &home)
.env("SOCKET_API_URL", &api)
.env("SOCKET_API_TOKEN", "sktsec_dummy_e2e_golang_token_api")
.env("SOCKET_ORG_SLUG", ORG)
.env_remove("GOMODCACHE")
.env_remove("GOPATH")
.env_remove("SOCKET_ECOSYSTEMS")
.env_remove("SOCKET_GLOBAL")
.env_remove("SOCKET_GLOBAL_PREFIX")
.env_remove("SOCKET_JSON")
.env_remove("SOCKET_SILENT")
.env_remove("SOCKET_OFFLINE")
.env_remove("SOCKET_PROXY_URL")
.env_remove("SOCKET_BATCH_SIZE")
.output()
.expect("Failed to run socket-patch binary")
})
Expand Down
34 changes: 8 additions & 26 deletions crates/socket-patch-cli/tests/e2e_golang_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,26 +60,10 @@ fn go_env<'a>(modcache: &'a str, proxy_url: &'a str) -> Vec<(&'a str, &'a str)>
]
}

/// Run socket-patch with ambient `SOCKET_*` scrubbed + the fixture GOMODCACHE
/// (the go crawler resolves installed modules through it). Every global flag
/// is env-backed (`SOCKET_DRY_RUN`, `SOCKET_GLOBAL`, `SOCKET_MANIFEST_PATH`,
/// …), so an unscrubbed ambient value would silently reconfigure `apply` /
/// `--check` out from under the assertions.
/// Run socket-patch through the shared hermetic runner with the fixture
/// GOMODCACHE (the go crawler resolves installed modules through it).
fn run_socket(cwd: &Path, args: &[&str], modcache: &Path) -> (i32, String, String) {
let mut cmd = Command::new(binary());
cmd.args(args).current_dir(cwd);
for (k, _) in std::env::vars_os() {
if k.to_string_lossy().starts_with("SOCKET_") && k.to_string_lossy() != "SOCKET_NO_CONFIG" {
cmd.env_remove(&k);
}
}
cmd.env("GOMODCACHE", modcache);
let out = cmd.output().expect("failed to run socket-patch binary");
(
out.status.code().unwrap_or(-1),
String::from_utf8_lossy(&out.stdout).into_owned(),
String::from_utf8_lossy(&out.stderr).into_owned(),
)
common::run_with_env(cwd, args, &[("GOMODCACHE", modcache.to_str().unwrap())])
}

/// Run `go` with its caches sandboxed, then the fixture's own env on top.
Expand Down Expand Up @@ -206,14 +190,12 @@ fn go_build_links_patch_via_replace_redirect() {
if !golang_e2e_matrix::toolchain_ready("e2e_golang_build") {
return;
}
// RED guards for the hermeticity pins: bake the hostile ambient values in
// so this suite fails deterministically if either leak returns.
// `GOTOOLCHAIN` must lose to go_env's `local` pin (or every `go` command
// chases a nonexistent toolchain through the file proxy); `SOCKET_DRY_RUN`
// must be scrubbed by `run_socket` (or every apply is a no-op that still
// exits 0 and the patched-symbol assert sees PRISTINE).
// RED guard for the toolchain pin: bake the hostile ambient value in so
// this suite fails deterministically if the leak returns. `GOTOOLCHAIN`
// must lose to go_env's `local` pin (or every `go` command chases a
// nonexistent toolchain through the file proxy). `SOCKET_DRY_RUN` needs
// no guard here: `hermetic::command` seeds and scrubs it itself.
std::env::set_var("GOTOOLCHAIN", "go1.99.99");
std::env::set_var("SOCKET_DRY_RUN", "true");
let tmp = tempfile::tempdir().unwrap();
let (consumer, modcache, proxy_url) = stage(tmp.path());
let cs = consumer.to_str().unwrap();
Expand Down
18 changes: 5 additions & 13 deletions crates/socket-patch-cli/tests/e2e_golang_workspace_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ mod golang_e2e_matrix;
mod vex_e2e_common;

use golang_e2e_matrix::{manifestless_vex, ManifestlessGo};
use vex_e2e_common::{binary, git_sha256, patch_view, Marker, VexVia};
use vex_e2e_common::{binary, git_sha256, hermetic, patch_view, Marker, VexVia};

const ORG: &str = "test-org";
const UMOD: &str = "example.com/upstream";
Expand Down Expand Up @@ -292,20 +292,12 @@ fn move_replace_to_go_work(root: &Path, sums_of: Option<&str>) -> String {
line
}

/// Run socket-patch (SOCKET_* scrubbed, hermetic config) in `cwd`.
/// Run socket-patch (hermetic `SOCKET_*` environment) in `cwd`.
fn socket(cwd: &Path, args: &[&str], modcache: &Path) -> (i32, serde_json::Value, String) {
let mut cmd = Command::new(binary());
let mut cmd = hermetic::command(&binary());
hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]);
cmd.current_dir(cwd);
for (k, _) in std::env::vars_os() {
if k.to_string_lossy().starts_with("SOCKET_") {
cmd.env_remove(&k);
}
}
cmd.env("SOCKET_NO_CONFIG", "1")
.env("SOCKET_TELEMETRY_DISABLED", "1")
.env("GOMODCACHE", modcache)
.env("GOFLAGS", "")
.env_remove("VIRTUAL_ENV");
cmd.env("GOMODCACHE", modcache).env("GOFLAGS", "");
let _fixture = prebuilt_common::prepare_command(
&mut cmd,
cwd,
Expand Down
13 changes: 9 additions & 4 deletions crates/socket-patch-cli/tests/spawn_env_hygiene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ const PENDING_RAW_SPAWNS: &[&str] = &[
"e2e_composer.rs",
"e2e_composer_version_identity.rs",
"e2e_gem.rs",
"e2e_golang.rs",
"e2e_golang_build.rs",
"e2e_golang_workspace_build.rs",
"e2e_hosted_production.rs",
"e2e_maven.rs",
"e2e_npm.rs",
Expand All @@ -51,7 +48,6 @@ const PENDING_RAW_SPAWNS: &[&str] = &[
"e2e_vendored_production.rs",
"e2e_vex_vendor.rs",
"rollback/rollback_multicopy_blob_gate.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",
Expand Down Expand Up @@ -305,6 +301,11 @@ fn command_drops_flag_bound_vars_set_after_it_is_built() {
("SOCKET_DEBUG", "1"),
("SOCKET_PROXY_URL", "http://ambient.invalid"),
("SOCKET_SKIP_ROLLBACK", "true"),
// Peer aliases the binary promotes onto `SOCKET_API_TOKEN` & co.
("SOCKET_CLI_API_TOKEN", "late-token"),
("SOCKET_CLI_ORG_SLUG", "late-org"),
("SOCKET_CLI_API_BASE_URL", "http://ambient.invalid"),
("SOCKET_CLI_NO_API_TOKEN", "1"),
],
|| {
let env = effective_env(&cmd);
Expand All @@ -313,6 +314,10 @@ fn command_drops_flag_bound_vars_set_after_it_is_built() {
"SOCKET_DEBUG",
"SOCKET_PROXY_URL",
"SOCKET_SKIP_ROLLBACK",
"SOCKET_CLI_API_TOKEN",
"SOCKET_CLI_ORG_SLUG",
"SOCKET_CLI_API_BASE_URL",
"SOCKET_CLI_NO_API_TOKEN",
] {
assert_eq!(get(&env, key), None, "{key} set after build leaked");
}
Expand Down
45 changes: 10 additions & 35 deletions crates/socket-patch-cli/tests/vendor/e2e_golang_redirect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,42 +64,17 @@ fn stage(root: &Path) -> (std::path::PathBuf, std::path::PathBuf) {
(gomodcache, cache_dir)
}

/// Run the CLI binary with the environment pinned hard (mirrors the
/// seed-then-scrub runner in `e2e_golang.rs`). Each variable below was
/// verified to break this suite when inherited from the ambient shell, so it
/// is seeded with a hostile value and then scrubbed — `env_remove` clears the
/// seed too, so the child never sees it, but if a scrub line is ever dropped
/// the seed (rather than a developer's ambient shell, which this suite can't
/// rely on) turns the tests red immediately. `SOCKET_GLOBAL` /
/// `SOCKET_GLOBAL_PREFIX` take the redirect backend out of local scope
/// (apply patches the fake module cache IN PLACE and `--check` exits 0
/// having checked nothing), `SOCKET_DRY_RUN` makes every apply a no-op, and
/// `SOCKET_MANIFEST_PATH` points apply/check at a manifest that isn't there.
/// `--offline`, `--ecosystems`, and `--cwd` are passed as flags, which
/// outrank env, so their env twins can't bite; `GOMODCACHE` pins the crawl
/// root (so `GOPATH` is never consulted).
/// Run the CLI binary through the shared hermetic runner. Its hostile seeds
/// cover the vars verified to break this suite when inherited:
/// `SOCKET_GLOBAL` / `SOCKET_GLOBAL_PREFIX` take the redirect backend out of
/// local scope (apply patches the fake module cache IN PLACE and `--check`
/// exits 0 having checked nothing), `SOCKET_DRY_RUN` makes every apply a
/// no-op, and `SOCKET_MANIFEST_PATH` points apply/check at a manifest that
/// isn't there. `--offline`, `--ecosystems`, and `--cwd` are passed as flags,
/// which outrank env, so their env twins can't bite; `GOMODCACHE` pins the
/// crawl root (so `GOPATH` is never consulted).
fn run_cli(root: &Path, gomodcache: &Path, args: &[&str]) -> (i32, String, String) {
let out = std::process::Command::new(binary())
.args(args)
.current_dir(root)
.env("GOMODCACHE", gomodcache)
.env("SOCKET_GLOBAL", "true")
.env("SOCKET_GLOBAL_PREFIX", "/nonexistent")
.env("SOCKET_DRY_RUN", "true")
.env("SOCKET_MANIFEST_PATH", "/nonexistent/manifest.json")
.env_remove("SOCKET_GLOBAL")
.env_remove("SOCKET_GLOBAL_PREFIX")
.env_remove("SOCKET_DRY_RUN")
.env_remove("SOCKET_MANIFEST_PATH")
.env_remove("SOCKET_API_TOKEN")
.env_remove("SOCKET_CLI_API_TOKEN")
.output()
.expect("failed to execute socket-patch binary");
(
out.status.code().unwrap_or(-1),
String::from_utf8_lossy(&out.stdout).to_string(),
String::from_utf8_lossy(&out.stderr).to_string(),
)
common::run_with_env(root, args, &[("GOMODCACHE", gomodcache.to_str().unwrap())])
}

fn apply(root: &Path, gomodcache: &Path) -> (i32, String, String) {
Expand Down
Loading