Skip to content

Commit 1b8d77b

Browse files
authored
Conceal stellar env output unless --reveal is used (#2597)
1 parent 5b3b098 commit 1b8d77b

5 files changed

Lines changed: 76 additions & 14 deletions

File tree

FULL_HELP_DOCS.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1179,7 +1179,9 @@ Prints the environment variables
11791179

11801180
Prints to stdout in a format that can be used as .env file. Environment variables have precedence over defaults.
11811181

1182-
Pass a name to get the value of a single environment variable.
1182+
By default, secret values are concealed. To display them, use `--reveal`.
1183+
1184+
Pass a name to get the value of a single environment variable. Its value is printed without shell quoting (control characters are neutralized), suitable for command substitution. Concealed variables print nothing unless `--reveal` is passed.
11831185

11841186
If there are no environment variables in use, prints the defaults.
11851187

@@ -1195,6 +1197,10 @@ If there are no environment variables in use, prints the defaults.
11951197

11961198
- `--config-dir <CONFIG_DIR>` — Location of config directory. By default, it uses `$XDG_CONFIG_HOME/stellar` if set, falling back to `~/.config/stellar` otherwise. Contains configuration files, aliases, and other persistent settings
11971199

1200+
###### **Options:**
1201+
1202+
- `--reveal` — Whether to reveal the value of concealed env vars. By default, concealed env vars are hidden behind a placeholder value
1203+
11981204
## `stellar keys`
11991205

12001206
Create and manage identities including keys and addresses

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,23 @@ fn env_does_not_display_secret_key() {
775775
.success();
776776
}
777777

778+
#[test]
779+
fn env_does_display_secret_key_when_reveal_is_provided() {
780+
let sandbox = TestEnv::default();
781+
sandbox
782+
.new_assert_cmd("env")
783+
.arg("--reveal")
784+
.env(
785+
"STELLAR_SECRET_KEY",
786+
"SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD",
787+
)
788+
.assert()
789+
.stdout(predicate::str::contains(
790+
"STELLAR_SECRET_KEY=SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD",
791+
))
792+
.success();
793+
}
794+
778795
#[test]
779796
fn env_single_concealed_key_returns_empty() {
780797
let sandbox = TestEnv::default();
@@ -790,6 +807,21 @@ fn env_single_concealed_key_returns_empty() {
790807
.success();
791808
}
792809

810+
#[test]
811+
fn env_single_is_returned_when_using_reveal() {
812+
let sandbox = TestEnv::default();
813+
sandbox
814+
.new_assert_cmd("env")
815+
.args(["STELLAR_SECRET_KEY", "--reveal"])
816+
.env(
817+
"STELLAR_SECRET_KEY",
818+
"SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD",
819+
)
820+
.assert()
821+
.stdout("SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD\n")
822+
.success();
823+
}
824+
793825
#[test]
794826
fn env_does_not_display_sign_with_key() {
795827
let sandbox = TestEnv::default();

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

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ use crate::{
33
config::locator::{self},
44
env_vars,
55
print::Print,
6+
utils::escape_control_characters,
67
};
78
use clap::Parser;
9+
use shell_escape::escape;
810

911
#[allow(clippy::doc_markdown)]
1012
#[derive(Debug, Parser)]
@@ -15,6 +17,11 @@ pub struct Cmd {
1517
#[arg()]
1618
pub name: Option<String>,
1719

20+
/// Whether to reveal the value of concealed env vars. By default, concealed env vars are
21+
/// hidden behind a placeholder value.
22+
#[arg(long)]
23+
pub reveal: bool,
24+
1825
#[command(flatten)]
1926
pub config_locator: locator::Args,
2027
}
@@ -32,16 +39,20 @@ impl Cmd {
3239
let supported = env_vars::prefixed("STELLAR");
3340

3441
for key in supported {
35-
if let Some(v) = EnvVar::get(&key) {
42+
if let Some(mut v) = EnvVar::get(&key) {
43+
if self.reveal {
44+
v.reveal();
45+
}
46+
3647
vars.push(v);
3748
}
3849
}
3950

4051
// If a specific name is given, just print that one value
4152
if let Some(name) = &self.name {
42-
if env_vars::is_concealed(name) {
43-
if let Some(v) = vars.iter().find(|v| &v.key == name) {
44-
println!("{}", v.value);
53+
if let Some(v) = vars.iter().find(|v| &v.key == name) {
54+
if v.is_revealed() {
55+
println!("{}", escape_control_characters(&v.value));
4556
}
4657
}
4758

@@ -70,6 +81,7 @@ struct EnvVar {
7081
key: String,
7182
value: String,
7283
source: String,
84+
reveal: bool,
7385
}
7486

7587
impl EnvVar {
@@ -84,15 +96,25 @@ impl EnvVar {
8496
key: key.to_string(),
8597
value,
8698
source,
99+
reveal: false,
87100
});
88101
}
89102

90103
None
91104
}
92105

106+
fn reveal(&mut self) {
107+
self.reveal = true;
108+
}
109+
110+
fn is_revealed(&self) -> bool {
111+
self.reveal || !env_vars::is_concealed(&self.key)
112+
}
113+
93114
fn str(&self) -> String {
94-
if env_vars::is_concealed(&self.key) {
95-
format!("{}={}", self.key, self.value)
115+
if self.is_revealed() {
116+
let value = escape(escape_control_characters(&self.value).into());
117+
format!("{}={value}", self.key)
96118
} else {
97119
format!("# {}=<concealed>", self.key)
98120
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,11 @@ pub enum Cmd {
157157
/// Prints to stdout in a format that can be used as .env file. Environment
158158
/// variables have precedence over defaults.
159159
///
160-
/// Pass a name to get the value of a single environment variable.
160+
/// By default, secret values are concealed. To display them, use `--reveal`.
161+
///
162+
/// Pass a name to get the value of a single environment variable. Its value is printed without
163+
/// shell quoting (control characters are neutralized), suitable for command substitution.
164+
/// Concealed variables print nothing unless `--reveal` is passed.
161165
///
162166
/// If there are no environment variables in use, prints the defaults.
163167
Env(env::Cmd),

cmd/soroban-cli/src/env_vars.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ pub fn unprefixed() -> Vec<&'static str> {
2929
/// Unprefixed names of env vars that are safe to display in plain text.
3030
const VISIBLE: &[&str] = &[
3131
"ACCOUNT",
32-
"ARCHIVE_URL",
3332
"CONFIG_HOME",
3433
"CONTRACT_ID",
3534
"DATA_HOME",
@@ -41,21 +40,20 @@ const VISIBLE: &[&str] = &[
4140
"NO_CACHE",
4241
"NO_UPDATE_CHECK",
4342
"OPERATION_SOURCE_ACCOUNT",
44-
"RPC_URL",
4543
"SEND",
4644
"SIGN_WITH_LAB",
4745
"SIGN_WITH_LEDGER",
4846
];
4947

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.
48+
/// Returns true if the key should be concealed in `stellar env` output, i.e. it is not in the
49+
/// allow list of vars that are safe to display. Using an allow list ensures unknown vars are
50+
/// concealed by default, even if they start with the expected prefix.
5351
pub fn is_concealed(key: &str) -> bool {
5452
let name = key
5553
.strip_prefix("STELLAR_")
5654
.or_else(|| key.strip_prefix("SOROBAN_"))
5755
.unwrap_or(key);
58-
VISIBLE.contains(&name)
56+
!VISIBLE.contains(&name)
5957
}
6058

6159
pub fn prefixed(key: &str) -> Vec<String> {

0 commit comments

Comments
 (0)