diff --git a/cmd/crates/soroban-test/tests/it/config.rs b/cmd/crates/soroban-test/tests/it/config.rs index 1c2457cc8..2b4a1edc4 100644 --- a/cmd/crates/soroban-test/tests/it/config.rs +++ b/cmd/crates/soroban-test/tests/it/config.rs @@ -194,6 +194,34 @@ fn seed_phrase() { .stdout(predicates::str::contains("test_seed\n")); } +#[test] +fn secure_store_rejects_env_secret_key() { + let sandbox = TestEnv::default(); + + // --secure-store with STELLAR_SECRET_KEY set should be rejected rather than + // silently writing the raw secret to a plaintext identity file. + sandbox + .new_assert_cmd("keys") + .env( + "STELLAR_SECRET_KEY", + "SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD", + ) + .arg("add") + .arg("alice") + .arg("--secure-store") + .assert() + .failure() + .stderr(predicate::str::contains( + "--secure-store only supports seed phrases", + )); + + // The identity file must not exist — no plaintext fallback. + assert!( + !sandbox.config_dir().join("identity/alice.toml").exists(), + "identity file should not be created when --secure-store is rejected" + ); +} + #[test] fn use_env() { let sandbox = TestEnv::default(); diff --git a/cmd/soroban-cli/src/commands/keys/add.rs b/cmd/soroban-cli/src/commands/keys/add.rs index 999884eec..64aa4271b 100644 --- a/cmd/soroban-cli/src/commands/keys/add.rs +++ b/cmd/soroban-cli/src/commands/keys/add.rs @@ -33,6 +33,12 @@ pub enum Error { #[error("An identity with the name '{0}' already exists")] IdentityAlreadyExists(String), + + #[error( + "--secure-store only supports seed phrases; \ + unset STELLAR_SECRET_KEY or provide a seed phrase instead" + )] + SecureStoreRequiresSeedPhrase, } #[derive(Debug, clap::Parser, Clone)] @@ -82,9 +88,15 @@ impl Cmd { } fn read_secret(&self, print: &Print) -> Result { - if let Ok(secret_key) = std::env::var("STELLAR_SECRET_KEY") { - Ok(Secret::SecretKey { secret_key }) - } else if self.secrets.secure_store { + if self.secrets.secure_store { + if std::env::var("STELLAR_SECRET_KEY").is_ok() { + return Err(Error::SecureStoreRequiresSeedPhrase); + } + } else if let Ok(secret_key) = std::env::var("STELLAR_SECRET_KEY") { + return Ok(Secret::SecretKey { secret_key }); + } + + if self.secrets.secure_store { let prompt = "Type a 12/24 word seed phrase:"; let secret_key = read_password(print, prompt)?; if secret_key.split_whitespace().count() < 24 {