diff --git a/_primitives/_rust/kei-auth/src/main.rs b/_primitives/_rust/kei-auth/src/main.rs index f6f0c6b..796691b 100644 --- a/_primitives/_rust/kei-auth/src/main.rs +++ b/_primitives/_rust/kei-auth/src/main.rs @@ -1,4 +1,9 @@ //! kei-auth CLI — issue/verify/revoke. +//! +//! v0.14.1 security fix: the `--key` CLI flag was removed because it +//! leaked the HMAC signing secret through `/proc//cmdline` and +//! shell history. The only supported key source is the `KEI_AUTH_KEY` +//! env var (sourced from `~/.claude/secrets/.env` per RULE 0.8). use clap::{Parser, Subcommand}; use kei_auth::schema::open; @@ -12,8 +17,6 @@ use std::str::FromStr; #[command(name = "kei-auth", version)] struct Cli { #[arg(long)] db: Option, - /// HMAC signing key (env KEI_AUTH_KEY fallback). - #[arg(long)] key: Option, #[command(subcommand)] cmd: Cmd, } @@ -34,17 +37,24 @@ fn db_path(o: Option) -> PathBuf { PathBuf::from(home).join(".claude/auth/auth.sqlite") } -fn key(cli_key: Option) -> anyhow::Result> { - if let Some(k) = cli_key { return Ok(k.into_bytes()); } - let k = std::env::var("KEI_AUTH_KEY") - .map_err(|_| anyhow::anyhow!("provide --key or set KEI_AUTH_KEY"))?; +fn key() -> anyhow::Result> { + let k = std::env::var("KEI_AUTH_KEY").map_err(|_| { + anyhow::anyhow!( + "KEI_AUTH_KEY env var not set.\n \ + Set it before running kei-auth:\n \ + export KEI_AUTH_KEY=\"$(openssl rand -hex 32)\"\n \ + Or read from ~/.claude/secrets/.env (RULE 0.8 SSoT).\n \ + The previous --key CLI flag was removed in v0.14.1 because \ + it leaked the secret via /proc//cmdline." + ) + })?; Ok(k.into_bytes()) } fn run() -> anyhow::Result<()> { let cli = Cli::parse(); let conn = open(&db_path(cli.db))?; - let k = key(cli.key)?; + let k = key()?; match cli.cmd { Cmd::Issue { user, project, scope, ttl } => { let sc = Scope::from_str(&scope).map_err(|e| anyhow::anyhow!(e))?;