Skip to content

Commit 6b2e3ea

Browse files
authored
Display env vars with concealed value by default. (#2440)
1 parent aac2016 commit 6b2e3ea

4 files changed

Lines changed: 87 additions & 10 deletions

File tree

cmd/crates/soroban-test/tests/it/config.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,20 @@ fn cannot_create_key_with_alias() {
590590
.failure();
591591
}
592592

593+
#[test]
594+
fn env_does_not_display_rpc_headers() {
595+
let sandbox = TestEnv::default();
596+
sandbox
597+
.new_assert_cmd("env")
598+
.env("STELLAR_RPC_HEADERS", "a:1")
599+
.assert()
600+
.stdout(predicate::str::contains(
601+
"# STELLAR_RPC_HEADERS=<concealed>",
602+
))
603+
.stdout(predicate::str::contains("a:1").not())
604+
.success();
605+
}
606+
593607
#[test]
594608
fn env_does_not_display_secret_key() {
595609
let sandbox = TestEnv::default();
@@ -600,7 +614,26 @@ fn env_does_not_display_secret_key() {
600614
"SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD",
601615
)
602616
.assert()
603-
.stdout(predicate::str::contains("SECRET_KEY").not())
617+
.stdout(predicate::str::contains("# STELLAR_SECRET_KEY=<concealed>"))
618+
.stdout(
619+
predicate::str::contains("SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD")
620+
.not(),
621+
)
622+
.success();
623+
}
624+
625+
#[test]
626+
fn env_single_concealed_key_returns_empty() {
627+
let sandbox = TestEnv::default();
628+
sandbox
629+
.new_assert_cmd("env")
630+
.args(["STELLAR_SECRET_KEY"])
631+
.env(
632+
"STELLAR_SECRET_KEY",
633+
"SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD",
634+
)
635+
.assert()
636+
.stdout("")
604637
.success();
605638
}
606639

@@ -614,7 +647,13 @@ fn env_does_not_display_sign_with_key() {
614647
"SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD",
615648
)
616649
.assert()
617-
.stdout(predicate::str::contains("SIGN_WITH_KEY").not())
650+
.stdout(predicate::str::contains(
651+
"# STELLAR_SIGN_WITH_KEY=<concealed>",
652+
))
653+
.stdout(
654+
predicate::str::contains("SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD")
655+
.not(),
656+
)
618657
.success();
619658
}
620659

cmd/soroban-cli/src/cli.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,7 @@ pub async fn main() {
2020
// Map SOROBAN_ env vars to STELLAR_ env vars for backwards compatibility
2121
// with the soroban-cli prior to when the stellar-cli was released.
2222
//
23-
let mut vars = env_vars::unprefixed();
24-
25-
// Manually add SECRET_KEY so it doesn't leak on `stellar env`.
26-
vars.push("SECRET_KEY");
27-
vars.push("SIGN_WITH_KEY");
23+
let vars = env_vars::unprefixed();
2824

2925
for var in vars {
3026
let soroban_key = format!("SOROBAN_{var}");

cmd/soroban-cli/src/commands/env/mod.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,12 @@ impl Cmd {
3939

4040
// If a specific name is given, just print that one value
4141
if let Some(name) = &self.name {
42-
if let Some(v) = vars.iter().find(|v| &v.key == name) {
43-
println!("{}", v.value);
42+
if env_vars::is_concealed(name) {
43+
if let Some(v) = vars.iter().find(|v| &v.key == name) {
44+
println!("{}", v.value);
45+
}
4446
}
47+
4548
return Ok(());
4649
}
4750

@@ -88,6 +91,10 @@ impl EnvVar {
8891
}
8992

9093
fn str(&self) -> String {
91-
format!("{}={}", self.key, self.value)
94+
if env_vars::is_concealed(&self.key) {
95+
format!("{}={}", self.key, self.value)
96+
} else {
97+
format!("# {}=<concealed>", self.key)
98+
}
9299
}
93100
}

cmd/soroban-cli/src/env_vars.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// List of environment variables used by the CLI.
22
// Most values come from `clap` env var aliases, but some are used directly.
3+
// This list must include everything, even env vars that are secrets.
34
pub fn unprefixed() -> Vec<&'static str> {
45
vec![
56
"ACCOUNT",
@@ -17,12 +18,46 @@ pub fn unprefixed() -> Vec<&'static str> {
1718
"OPERATION_SOURCE_ACCOUNT",
1819
"RPC_HEADERS",
1920
"RPC_URL",
21+
"SECRET_KEY",
2022
"SEND",
23+
"SIGN_WITH_KEY",
2124
"SIGN_WITH_LAB",
2225
"SIGN_WITH_LEDGER",
2326
]
2427
}
2528

29+
/// Unprefixed names of env vars that are safe to display in plain text.
30+
const VISIBLE: &[&str] = &[
31+
"ACCOUNT",
32+
"ARCHIVE_URL",
33+
"CONFIG_HOME",
34+
"CONTRACT_ID",
35+
"DATA_HOME",
36+
"FEE",
37+
"INCLUSION_FEE",
38+
"INVOKE_VIEW",
39+
"NETWORK",
40+
"NETWORK_PASSPHRASE",
41+
"NO_CACHE",
42+
"NO_UPDATE_CHECK",
43+
"OPERATION_SOURCE_ACCOUNT",
44+
"RPC_URL",
45+
"SEND",
46+
"SIGN_WITH_LAB",
47+
"SIGN_WITH_LEDGER",
48+
];
49+
50+
/// Returns true if the key is one of the supported env vars that should be shown in `stellar env`.
51+
/// Uses an allow list approach to avoid showing any env vars that are not explicitly supported,
52+
/// even if they start with the expected prefix.
53+
pub fn is_concealed(key: &str) -> bool {
54+
let name = key
55+
.strip_prefix("STELLAR_")
56+
.or_else(|| key.strip_prefix("SOROBAN_"))
57+
.unwrap_or(key);
58+
VISIBLE.contains(&name)
59+
}
60+
2661
pub fn prefixed(key: &str) -> Vec<String> {
2762
unprefixed()
2863
.iter()

0 commit comments

Comments
 (0)