Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions awa-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,

#[command(subcommand)]
Expand Down Expand Up @@ -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<String>,
/// Force the server into read-only mode regardless of DB privilege.
///
Expand Down Expand Up @@ -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<String>,
/// Path prefix the callback routes are mounted under. Defaults to
/// `/api/callbacks`, matching the built-in `awa serve` layout.
Expand Down
46 changes: 46 additions & 0 deletions awa-cli/tests/help_cli_test.rs
Original file line number Diff line number Diff line change
@@ -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));
}