diff --git a/CHANGELOG.md b/CHANGELOG.md index 61eb4b8b..eaac4693 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ Notable changes between releases. Detailed migration notes for storage transitio ## [Unreleased] +### Fixed + +- **CLI help no longer prints credential-bearing environment values.** `awa --help` hides the resolved `DATABASE_URL`, while `awa serve --help` and `awa callbacks serve --help` hide `AWA_CALLBACK_HMAC_SECRET`. The CLI still accepts those environment variables and help still identifies their names, but their current values are never rendered. + ## [0.6.6] — 2026-08-14 Patch release: storage-transition convergence and recovery fixes. No migrations, schema changes, or public API changes. diff --git a/awa-cli/src/main.rs b/awa-cli/src/main.rs index f317d906..97f00edd 100644 --- a/awa-cli/src/main.rs +++ b/awa-cli/src/main.rs @@ -14,7 +14,7 @@ mod storage_wait; )] struct Cli { /// Database URL (not required for migrate --sql without --pending) - #[arg(long, env = "DATABASE_URL")] + #[arg(long, env = "DATABASE_URL", hide_env_values = true)] database_url: Option, #[command(subcommand)] @@ -101,7 +101,7 @@ enum Commands { #[arg(long, default_value = "5", env = "AWA_CACHE_TTL")] cache_ttl: u64, /// Hex-encoded 32-byte key used to verify callback signatures. - #[arg(long, env = "AWA_CALLBACK_HMAC_SECRET")] + #[arg(long, env = "AWA_CALLBACK_HMAC_SECRET", hide_env_values = true)] callback_hmac_secret: Option, /// Force the server into read-only mode regardless of DB privilege. /// @@ -151,7 +151,7 @@ enum CallbackCommands { pool_acquire_timeout: u64, /// Hex-encoded 32-byte key used to verify callback signatures. /// Required unless `--allow-unsigned` is set. - #[arg(long, env = "AWA_CALLBACK_HMAC_SECRET")] + #[arg(long, env = "AWA_CALLBACK_HMAC_SECRET", hide_env_values = true)] callback_hmac_secret: Option, /// Path prefix the callback routes are mounted under. Defaults to /// `/api/callbacks`, matching the built-in `awa serve` layout. diff --git a/awa-cli/tests/help_cli_test.rs b/awa-cli/tests/help_cli_test.rs new file mode 100644 index 00000000..1b15397e --- /dev/null +++ b/awa-cli/tests/help_cli_test.rs @@ -0,0 +1,46 @@ +use assert_cmd::Command; + +fn rendered_help(args: &[&str], env_name: &str, secret: &str) -> String { + let output = Command::cargo_bin("awa") + .expect("awa binary") + .env(env_name, secret) + .args(args) + .output() + .expect("run awa help command"); + + assert!(output.status.success()); + format!( + "{}{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ) +} + +#[test] +fn help_does_not_disclose_environment_database_url() { + let secret_url = "postgres://secret-user:secret-password@db.example/awa"; + let rendered = rendered_help(&["--help"], "DATABASE_URL", secret_url); + assert!(rendered.contains("DATABASE_URL")); + assert!(!rendered.contains(secret_url)); + assert!(!rendered.contains("secret-password")); +} + +#[test] +fn serve_help_does_not_disclose_callback_secret() { + let secret = "serve-callback-secret-sentinel"; + let rendered = rendered_help(&["serve", "--help"], "AWA_CALLBACK_HMAC_SECRET", secret); + assert!(rendered.contains("AWA_CALLBACK_HMAC_SECRET")); + assert!(!rendered.contains(secret)); +} + +#[test] +fn callback_receiver_help_does_not_disclose_callback_secret() { + let secret = "receiver-callback-secret-sentinel"; + let rendered = rendered_help( + &["callbacks", "serve", "--help"], + "AWA_CALLBACK_HMAC_SECRET", + secret, + ); + assert!(rendered.contains("AWA_CALLBACK_HMAC_SECRET")); + assert!(!rendered.contains(secret)); +}