From e12806c8fa72bd7cb28694f9dfbc820b46be74e0 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Tue, 10 Mar 2026 10:47:43 -0700 Subject: [PATCH 1/5] Display env vars with concealed value by default. --- cmd/crates/soroban-test/tests/it/config.rs | 26 +++++++++++++++++-- cmd/soroban-cli/src/cli.rs | 6 +---- cmd/soroban-cli/src/commands/env/mod.rs | 15 ++++++++--- cmd/soroban-cli/src/env_vars.rs | 30 ++++++++++++++++++++++ 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/config.rs b/cmd/crates/soroban-test/tests/it/config.rs index debbed7400..c8fb301456 100644 --- a/cmd/crates/soroban-test/tests/it/config.rs +++ b/cmd/crates/soroban-test/tests/it/config.rs @@ -590,6 +590,18 @@ fn cannot_create_key_with_alias() { .failure(); } +#[test] +fn env_does_not_display_rpc_headers() { + let sandbox = TestEnv::default(); + sandbox + .new_assert_cmd("env") + .env("STELLAR_RPC_HEADERS", "a:1") + .assert() + .stdout(predicate::str::contains("STELLAR_RPC_HEADERS=")) + .stdout(predicate::str::contains("a:1").not()) + .success(); +} + #[test] fn env_does_not_display_secret_key() { let sandbox = TestEnv::default(); @@ -600,7 +612,11 @@ fn env_does_not_display_secret_key() { "SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD", ) .assert() - .stdout(predicate::str::contains("SECRET_KEY").not()) + .stdout(predicate::str::contains("STELLAR_SECRET_KEY=")) + .stdout( + predicate::str::contains("SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD") + .not(), + ) .success(); } @@ -614,6 +630,12 @@ fn env_does_not_display_sign_with_key() { "SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD", ) .assert() - .stdout(predicate::str::contains("SIGN_WITH_KEY").not()) + .stdout(predicate::str::contains( + "STELLAR_SIGN_WITH_KEY=", + )) + .stdout( + predicate::str::contains("SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD") + .not(), + ) .success(); } diff --git a/cmd/soroban-cli/src/cli.rs b/cmd/soroban-cli/src/cli.rs index 5a9d0de0f6..352d99d8dc 100644 --- a/cmd/soroban-cli/src/cli.rs +++ b/cmd/soroban-cli/src/cli.rs @@ -20,11 +20,7 @@ pub async fn main() { // Map SOROBAN_ env vars to STELLAR_ env vars for backwards compatibility // with the soroban-cli prior to when the stellar-cli was released. // - let mut vars = env_vars::unprefixed(); - - // Manually add SECRET_KEY so it doesn't leak on `stellar env`. - vars.push("SECRET_KEY"); - vars.push("SIGN_WITH_KEY"); + let vars = env_vars::unprefixed(); for var in vars { let soroban_key = format!("SOROBAN_{var}"); diff --git a/cmd/soroban-cli/src/commands/env/mod.rs b/cmd/soroban-cli/src/commands/env/mod.rs index 03b3105dd5..82cfc46523 100644 --- a/cmd/soroban-cli/src/commands/env/mod.rs +++ b/cmd/soroban-cli/src/commands/env/mod.rs @@ -39,9 +39,12 @@ impl Cmd { // If a specific name is given, just print that one value if let Some(name) = &self.name { - if let Some(v) = vars.iter().find(|v| &v.key == name) { - println!("{}", v.value); + if env_vars::is_visible(name) { + if let Some(v) = vars.iter().find(|v| &v.key == name) { + println!("{}", v.value); + } } + return Ok(()); } @@ -88,6 +91,12 @@ impl EnvVar { } fn str(&self) -> String { - format!("{}={}", self.key, self.value) + let value = if env_vars::is_visible(&self.key) { + &self.value + } else { + "" + }; + + format!("{}={}", self.key, value) } } diff --git a/cmd/soroban-cli/src/env_vars.rs b/cmd/soroban-cli/src/env_vars.rs index d05c9d4281..4e1d218f3e 100644 --- a/cmd/soroban-cli/src/env_vars.rs +++ b/cmd/soroban-cli/src/env_vars.rs @@ -1,5 +1,6 @@ // List of environment variables used by the CLI. // Most values come from `clap` env var aliases, but some are used directly. +// This list must include everything, even env vars that are secrets. pub fn unprefixed() -> Vec<&'static str> { vec![ "ACCOUNT", @@ -17,12 +18,41 @@ pub fn unprefixed() -> Vec<&'static str> { "OPERATION_SOURCE_ACCOUNT", "RPC_HEADERS", "RPC_URL", + "SECRET_KEY", "SEND", + "SIGN_WITH_KEY", "SIGN_WITH_LAB", "SIGN_WITH_LEDGER", ] } +/// Returns true if the key is one of the supported env vars that should be shown in `stellar env`. +/// Uses an allow list approach to avoid showing any env vars that are not explicitly supported, +/// even if they start with the expected prefix. +pub fn is_visible(key: &str) -> bool { + let visible = vec![ + "ACCOUNT", + "ARCHIVE_URL", + "CONFIG_HOME", + "CONTRACT_ID", + "DATA_HOME", + "FEE", + "INCLUSION_FEE", + "INVOKE_VIEW", + "NETWORK", + "NETWORK_PASSPHRASE", + "NO_CACHE", + "NO_UPDATE_CHECK", + "OPERATION_SOURCE_ACCOUNT", + "RPC_URL", + "SEND", + "SIGN_WITH_LAB", + "SIGN_WITH_LEDGER", + ]; + + visible.iter().any(|suffix| key.ends_with(suffix)) +} + pub fn prefixed(key: &str) -> Vec { unprefixed() .iter() From 39f3abb2464993253bcbfd69e901cb78e91055e7 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Tue, 10 Mar 2026 12:56:58 -0700 Subject: [PATCH 2/5] Address pr feedback. --- cmd/crates/soroban-test/tests/it/config.rs | 15 +++++++ cmd/soroban-cli/src/env_vars.rs | 47 ++++++++++++---------- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/config.rs b/cmd/crates/soroban-test/tests/it/config.rs index c8fb301456..f4d06c7edc 100644 --- a/cmd/crates/soroban-test/tests/it/config.rs +++ b/cmd/crates/soroban-test/tests/it/config.rs @@ -620,6 +620,21 @@ fn env_does_not_display_secret_key() { .success(); } +#[test] +fn env_single_concealed_key_returns_empty() { + let sandbox = TestEnv::default(); + sandbox + .new_assert_cmd("env") + .args(["STELLAR_SECRET_KEY"]) + .env( + "STELLAR_SECRET_KEY", + "SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD", + ) + .assert() + .stdout("") + .success(); +} + #[test] fn env_does_not_display_sign_with_key() { let sandbox = TestEnv::default(); diff --git a/cmd/soroban-cli/src/env_vars.rs b/cmd/soroban-cli/src/env_vars.rs index 4e1d218f3e..ce6d1939c4 100644 --- a/cmd/soroban-cli/src/env_vars.rs +++ b/cmd/soroban-cli/src/env_vars.rs @@ -26,31 +26,36 @@ pub fn unprefixed() -> Vec<&'static str> { ] } +/// Unprefixed names of env vars that are safe to display in plain text. +const VISIBLE: &[&str] = &[ + "ACCOUNT", + "ARCHIVE_URL", + "CONFIG_HOME", + "CONTRACT_ID", + "DATA_HOME", + "FEE", + "INCLUSION_FEE", + "INVOKE_VIEW", + "NETWORK", + "NETWORK_PASSPHRASE", + "NO_CACHE", + "NO_UPDATE_CHECK", + "OPERATION_SOURCE_ACCOUNT", + "RPC_URL", + "SEND", + "SIGN_WITH_LAB", + "SIGN_WITH_LEDGER", +]; + /// Returns true if the key is one of the supported env vars that should be shown in `stellar env`. /// Uses an allow list approach to avoid showing any env vars that are not explicitly supported, /// even if they start with the expected prefix. pub fn is_visible(key: &str) -> bool { - let visible = vec![ - "ACCOUNT", - "ARCHIVE_URL", - "CONFIG_HOME", - "CONTRACT_ID", - "DATA_HOME", - "FEE", - "INCLUSION_FEE", - "INVOKE_VIEW", - "NETWORK", - "NETWORK_PASSPHRASE", - "NO_CACHE", - "NO_UPDATE_CHECK", - "OPERATION_SOURCE_ACCOUNT", - "RPC_URL", - "SEND", - "SIGN_WITH_LAB", - "SIGN_WITH_LEDGER", - ]; - - visible.iter().any(|suffix| key.ends_with(suffix)) + let name = key + .strip_prefix("STELLAR_") + .or_else(|| key.strip_prefix("SOROBAN_")) + .unwrap_or(key); + VISIBLE.iter().any(|allowed| *allowed == name) } pub fn prefixed(key: &str) -> Vec { From ea4c8a0d799e4155205d5ae4f49ef2fdc578b505 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Tue, 10 Mar 2026 14:02:18 -0700 Subject: [PATCH 3/5] Output concealed env vars as comments. --- cmd/crates/soroban-test/tests/it/config.rs | 8 +++++--- cmd/soroban-cli/src/commands/env/mod.rs | 10 ++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/config.rs b/cmd/crates/soroban-test/tests/it/config.rs index f4d06c7edc..cd3e4dd903 100644 --- a/cmd/crates/soroban-test/tests/it/config.rs +++ b/cmd/crates/soroban-test/tests/it/config.rs @@ -597,7 +597,9 @@ fn env_does_not_display_rpc_headers() { .new_assert_cmd("env") .env("STELLAR_RPC_HEADERS", "a:1") .assert() - .stdout(predicate::str::contains("STELLAR_RPC_HEADERS=")) + .stdout(predicate::str::contains( + "# STELLAR_RPC_HEADERS=", + )) .stdout(predicate::str::contains("a:1").not()) .success(); } @@ -612,7 +614,7 @@ fn env_does_not_display_secret_key() { "SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD", ) .assert() - .stdout(predicate::str::contains("STELLAR_SECRET_KEY=")) + .stdout(predicate::str::contains("# STELLAR_SECRET_KEY=")) .stdout( predicate::str::contains("SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD") .not(), @@ -646,7 +648,7 @@ fn env_does_not_display_sign_with_key() { ) .assert() .stdout(predicate::str::contains( - "STELLAR_SIGN_WITH_KEY=", + "# STELLAR_SIGN_WITH_KEY=", )) .stdout( predicate::str::contains("SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD") diff --git a/cmd/soroban-cli/src/commands/env/mod.rs b/cmd/soroban-cli/src/commands/env/mod.rs index 82cfc46523..711e8d446a 100644 --- a/cmd/soroban-cli/src/commands/env/mod.rs +++ b/cmd/soroban-cli/src/commands/env/mod.rs @@ -91,12 +91,10 @@ impl EnvVar { } fn str(&self) -> String { - let value = if env_vars::is_visible(&self.key) { - &self.value + if env_vars::is_visible(&self.key) { + format!("{}={}", self.key, self.value) } else { - "" - }; - - format!("{}={}", self.key, value) + format!("# {}=", self.key) + } } } From 60945c098851e47ac9568da509eeeb355671b85f Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Tue, 10 Mar 2026 15:48:55 -0700 Subject: [PATCH 4/5] Fix cargo check warning. --- cmd/soroban-cli/src/env_vars.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/env_vars.rs b/cmd/soroban-cli/src/env_vars.rs index ce6d1939c4..eabe07a2ae 100644 --- a/cmd/soroban-cli/src/env_vars.rs +++ b/cmd/soroban-cli/src/env_vars.rs @@ -55,7 +55,7 @@ pub fn is_visible(key: &str) -> bool { .strip_prefix("STELLAR_") .or_else(|| key.strip_prefix("SOROBAN_")) .unwrap_or(key); - VISIBLE.iter().any(|allowed| *allowed == name) + VISIBLE.contains(&name) } pub fn prefixed(key: &str) -> Vec { From f73d06a11a98168d70afff790c0d8acad6f0becd Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Tue, 10 Mar 2026 16:30:59 -0700 Subject: [PATCH 5/5] Rename function. --- cmd/soroban-cli/src/commands/env/mod.rs | 4 ++-- cmd/soroban-cli/src/env_vars.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-cli/src/commands/env/mod.rs b/cmd/soroban-cli/src/commands/env/mod.rs index 711e8d446a..2d94e4f56e 100644 --- a/cmd/soroban-cli/src/commands/env/mod.rs +++ b/cmd/soroban-cli/src/commands/env/mod.rs @@ -39,7 +39,7 @@ impl Cmd { // If a specific name is given, just print that one value if let Some(name) = &self.name { - if env_vars::is_visible(name) { + if env_vars::is_concealed(name) { if let Some(v) = vars.iter().find(|v| &v.key == name) { println!("{}", v.value); } @@ -91,7 +91,7 @@ impl EnvVar { } fn str(&self) -> String { - if env_vars::is_visible(&self.key) { + if env_vars::is_concealed(&self.key) { format!("{}={}", self.key, self.value) } else { format!("# {}=", self.key) diff --git a/cmd/soroban-cli/src/env_vars.rs b/cmd/soroban-cli/src/env_vars.rs index eabe07a2ae..ec434c40f9 100644 --- a/cmd/soroban-cli/src/env_vars.rs +++ b/cmd/soroban-cli/src/env_vars.rs @@ -50,7 +50,7 @@ const VISIBLE: &[&str] = &[ /// Returns true if the key is one of the supported env vars that should be shown in `stellar env`. /// Uses an allow list approach to avoid showing any env vars that are not explicitly supported, /// even if they start with the expected prefix. -pub fn is_visible(key: &str) -> bool { +pub fn is_concealed(key: &str) -> bool { let name = key .strip_prefix("STELLAR_") .or_else(|| key.strip_prefix("SOROBAN_"))