From 9f45c2e5daf73109aba1823c014f67029505e75a Mon Sep 17 00:00:00 2001 From: h7x4 Date: Tue, 23 Dec 2025 17:40:07 +0900 Subject: [PATCH] client: error out on non-tty interactivity --- src/client/commands/create_user.rs | 11 +++++++++++ src/client/commands/drop_db.rs | 9 ++++++++- src/client/commands/drop_user.rs | 8 ++++++++ src/client/commands/edit_privs.rs | 13 +++++++++++-- src/client/commands/passwd_user.rs | 7 ++++++- 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/client/commands/create_user.rs b/src/client/commands/create_user.rs index 5032aae..5393639 100644 --- a/src/client/commands/create_user.rs +++ b/src/client/commands/create_user.rs @@ -1,3 +1,5 @@ +use std::io::IsTerminal; + use clap::Parser; use clap_complete::ArgValueCompleter; use dialoguer::Confirm; @@ -78,6 +80,15 @@ pub async fn create_users( .filter_map(|(username, result)| result.as_ref().ok().map(|()| username)) .collect::>(); + if !std::io::stdin().is_terminal() + && !args.no_password + && !successfully_created_users.is_empty() + { + anyhow::bail!( + "Cannot prompt for passwords in non-interactive mode. Use --no-password to skip setting passwords." + ); + } + for username in successfully_created_users { if !args.no_password && Confirm::new() diff --git a/src/client/commands/drop_db.rs b/src/client/commands/drop_db.rs index 992cb81..278bff9 100644 --- a/src/client/commands/drop_db.rs +++ b/src/client/commands/drop_db.rs @@ -1,3 +1,5 @@ +use std::io::IsTerminal; + use clap::Parser; use clap_complete::ArgValueCompleter; use dialoguer::Confirm; @@ -41,6 +43,12 @@ pub async fn drop_databases( anyhow::bail!("No database names provided"); } + if !std::io::stdin().is_terminal() && !args.yes { + anyhow::bail!( + "Cannot prompt for confirmation in non-interactive mode. Use --yes to automatically confirm." + ); + } + if !args.yes { let confirmation = Confirm::new() .with_prompt(format!( @@ -53,7 +61,6 @@ pub async fn drop_databases( )) .interact()?; - // if !confirmation { // TODO: should we return with an error code here? println!("Aborting drop operation."); diff --git a/src/client/commands/drop_user.rs b/src/client/commands/drop_user.rs index 790e54a..b26ec8c 100644 --- a/src/client/commands/drop_user.rs +++ b/src/client/commands/drop_user.rs @@ -1,3 +1,5 @@ +use std::io::IsTerminal; + use clap::Parser; use clap_complete::ArgValueCompleter; use dialoguer::Confirm; @@ -41,6 +43,12 @@ pub async fn drop_users( anyhow::bail!("No usernames provided"); } + if !std::io::stdin().is_terminal() && !args.yes { + anyhow::bail!( + "Cannot prompt for confirmation in non-interactive mode. Use --yes to automatically confirm." + ); + } + if !args.yes { let confirmation = Confirm::new() .with_prompt(format!( diff --git a/src/client/commands/edit_privs.rs b/src/client/commands/edit_privs.rs index 9cc6d79..ee689aa 100644 --- a/src/client/commands/edit_privs.rs +++ b/src/client/commands/edit_privs.rs @@ -1,4 +1,7 @@ -use std::collections::{BTreeMap, BTreeSet}; +use std::{ + collections::{BTreeMap, BTreeSet}, + io::IsTerminal, +}; use anyhow::Context; use clap::{Args, Parser}; @@ -213,6 +216,11 @@ pub async fn edit_database_privileges( }; let diffs: BTreeSet = if privs.is_empty() { + if !std::io::stdin().is_terminal() { + anyhow::bail!( + "Cannot launch editor in non-interactive mode. Please provide privileges via command line arguments." + ); + } let privileges_to_change = edit_privileges_with_editor(&existing_privilege_rows, use_database.as_ref())?; diff_privileges(&existing_privilege_rows, &privileges_to_change) @@ -275,7 +283,8 @@ pub async fn edit_database_privileges( println!("The following changes will be made:\n"); println!("{}", display_privilege_diffs(&diffs)); - if !args.yes + if std::io::stdin().is_terminal() + && !args.yes && !Confirm::new() .with_prompt("Do you want to apply these changes?") .default(false) diff --git a/src/client/commands/passwd_user.rs b/src/client/commands/passwd_user.rs index 7ef7727..101a934 100644 --- a/src/client/commands/passwd_user.rs +++ b/src/client/commands/passwd_user.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{io::IsTerminal, path::PathBuf}; use anyhow::Context; use clap::Parser; @@ -88,6 +88,11 @@ pub async fn passwd_user( .context("Failed to read password from stdin")?; buffer.trim().to_string() } else { + if !std::io::stdin().is_terminal() { + anyhow::bail!( + "Cannot prompt for password in non-interactive mode. Use --stdin or --password-file to provide the password." + ); + } read_password_from_stdin_with_double_check(&args.username)? };