diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ef11b8e16d..96c611b011c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,10 @@ versions. Files using the old names will continue to work. ([Louis Pilfold](https://github.com/lpil)) +- The password used for encrypting new local Hex API keys must now be at least + 8 characters in length. + ([Louis Pilfold](https://github.com/lpil)) + ### Language server - The language server now allows extracting the start of a pipeline into a diff --git a/compiler-cli/src/hex/auth.rs b/compiler-cli/src/hex/auth.rs index b35a4e069f2..330dc2f26bd 100644 --- a/compiler-cli/src/hex/auth.rs +++ b/compiler-cli/src/hex/auth.rs @@ -61,28 +61,52 @@ impl<'runtime> HexAuthentication<'runtime> { let future = hex::create_api_key(&name, &username, &password, &self.hex_config, &self.http); let api_key = self.runtime.block_on(future)?; - if self.local_password.is_none() { - println!( - " -Please enter a new unique password. This will be used to locally -encrypt your Hex API key. -" - ); - } - let password = self.ask_local_password()?; + // We are creating a new API key, so we need a new local password + // to encrypt it with. + let password = self.ask_for_new_local_password()?; + let encrypted = encryption::encrypt_with_passphrase(api_key.as_bytes(), &password) .map_err(|e| Error::FailedToEncryptLocalHexApiKey { detail: e.to_string(), })?; crate::fs::write(&path, &format!("{name}\n{encrypted}"))?; - println!("Encrypted Hex API key written to {path}"); + println!("\nEncrypted Hex API key written to {path}"); Ok(UnencryptedApiKey { unencrypted: api_key, }) } + /// Create a new local password. + /// + /// The password must be long enough. + /// + /// The old password will be discarded, and the new one will be both + /// returned and stored in `self.local_password` + /// + fn ask_for_new_local_password(&mut self) -> Result { + let required_length = 8; + self.local_password = None; + println!( + " +Please enter a new unique password. This will be used to locally +encrypt your Hex API key. +It should be at least {required_length} characters long. +" + ); + + loop { + let password = cli::ask_password(LOCAL_PASS_PROMPT)?; + if password.chars().count() < required_length { + println!("\nPlease use a password at least {required_length} characters long.\n") + } else { + self.local_password = Some(password.clone()); + return Ok(password); + } + } + } + fn ask_local_password(&mut self) -> Result { if let Some(pw) = self.local_password.as_ref() { return Ok(pw.clone()); diff --git a/compiler-cli/src/publish.rs b/compiler-cli/src/publish.rs index c377d37150d..b8884832b42 100644 --- a/compiler-cli/src/publish.rs +++ b/compiler-cli/src/publish.rs @@ -326,8 +326,12 @@ fn check_for_gleam_prefix(config: &PackageConfig) -> Result { println!( "You are about to publish a package with a name that starts with -the prefix `gleam_`, which is for packages maintained by the Gleam -core team.\n", +the prefix `gleam_`, which is preferred for packages maintained by the +Gleam core team. + +Security: do not assume the owner of a package from the name, always check +the maintainers listed on https://hex.pm/. +\n", ); let password = cli::ask_password("Please enter the core team password to continue")?; println!();