use secrecy::ExposeSecret;
use crate::{
error_mod::Error,
public_api_mod::{RED, RESET, YELLOW},
Result,
};
#[derive(Debug)]
pub struct ShellOutput {
pub status: i32,
pub stdout: String,
pub stderr: String,
}
pub fn run_shell_command_static(shell_command: &'static str) -> Result<()> {
if !shell_command.starts_with("echo ") && !shell_command.starts_with("printf ") {
println!(" {YELLOW}$ {shell_command}{RESET}");
}
let status = std::process::Command::new("sh").arg("-c").arg(shell_command).spawn()?.wait()?;
let exit_code = status
.code()
.ok_or_else(|| Error::ErrorFromString(format!("{RED}Error. {RESET}")))?;
if exit_code != 0 {
return Err(Error::ErrorFromString(format!(
"{RED}Error: run_shell_command {}. {RESET}",
exit_code
)));
}
Ok(())
}
pub struct ShellCommandLimitedDoubleQuotesSanitizer {
template: String,
string_to_echo: String,
string_to_execute: String,
}
impl crate::ShellCommandLimitedDoubleQuotesSanitizerTrait for ShellCommandLimitedDoubleQuotesSanitizer {
fn new(template: &str) -> Result<Self> {
if !template.contains("\"") {
return Err(Error::ErrorFromString(format!(
"{RED}The template must contain double quotes around placeholders because otherwise it is susceptible to command injection in shell command.{RESET}"
)));
}
Ok(ShellCommandLimitedDoubleQuotesSanitizer {
template: template.to_string(),
string_to_echo: template.to_string(),
string_to_execute: template.to_string(),
})
}
fn arg(&mut self, placeholder: &str, value: &str) -> Result<&mut Self> {
if value.contains("\"") {
return Err(Error::ErrorFromString(format!(
"{RED}The {placeholder} must not contain a double quote because it could create a command injection in shell command.{RESET}"
)));
}
if value.ends_with("\\") {
return Err(Error::ErrorFromString(format!(
"{RED}The {placeholder} must not end with a backslash \\ because it could create a command injection in shell command.{RESET}"
)));
}
self.template = self.template.replace(placeholder, value);
if placeholder.contains("secret") {
return Err(Error::ErrorFromString(format!(
"{RED}The {placeholder} looks like it contains a secret, but the argument is added with arg() and not arg_secret().{RESET}"
)));
}
self.string_to_echo = self.string_to_echo.replace(placeholder, value);
self.string_to_execute = self.string_to_execute.replace(placeholder, value);
Ok(self)
}
fn arg_secret(&mut self, placeholder: &str, value: &secrecy::SecretString) -> Result<&mut Self> {
if value.expose_secret().contains("\"") {
return Err(Error::ErrorFromString(format!(
"{RED}The {placeholder} must not contain a double quote because it could create a command injection in shell command.{RESET}"
)));
}
if value.expose_secret().ends_with("\\") {
return Err(Error::ErrorFromString(format!(
"{RED}The {placeholder} must not end with a backslash \\ because it could create a command injection in shell command.{RESET}"
)));
}
self.string_to_echo = self.string_to_echo.replace(placeholder, "[REDACTED]");
self.string_to_execute = self.string_to_execute.replace(placeholder, value.expose_secret());
Ok(self)
}
fn run(&self) -> Result<()> {
println!(" {YELLOW}$ {} {RESET}", self.string_to_echo);
let status = std::process::Command::new("sh")
.arg("-c")
.arg(&self.string_to_execute)
.spawn()?
.wait()?;
let exit_code = status
.code()
.ok_or_else(|| Error::ErrorFromString(format!("{RED}Error. {RESET}")))?;
if exit_code != 0 {
return Err(Error::ErrorFromString(format!(
"{RED}Error: run_shell_command {}. {RESET}",
exit_code
)));
}
Ok(())
}
}
pub fn run_shell_command(shell_command: &str) -> Result<()> {
if !shell_command.starts_with("echo ") && !shell_command.starts_with("printf ") {
println!(" {YELLOW}$ {shell_command}{RESET}");
}
let status = std::process::Command::new("sh").arg("-c").arg(shell_command).spawn()?.wait()?;
let exit_code = status
.code()
.ok_or_else(|| Error::ErrorFromString(format!("{RED}Error. {RESET}")))?;
if exit_code != 0 {
return Err(Error::ErrorFromString(format!(
"{RED}Error: run_shell_command {}. {RESET}",
exit_code
)));
}
Ok(())
}
pub fn run_shell_command_output(shell_command: &str) -> Result<ShellOutput> {
if !shell_command.starts_with("echo ") && !shell_command.starts_with("printf ") {
println!(" {YELLOW} $ {shell_command}{RESET}");
}
let output = std::process::Command::new("sh").arg("-c").arg(shell_command).output()?;
Ok(ShellOutput {
status: output.status.code().ok_or_else(|| Error::ErrorFromStr("code is None"))?,
stdout: String::from_utf8(output.stdout)?,
stderr: String::from_utf8(output.stderr)?,
})
}
pub fn run_shell_command_success(shell_command: &str) -> Result<bool> {
if !shell_command.starts_with("echo ") && !shell_command.starts_with("printf ") {
println!(" {YELLOW}$ {shell_command}{RESET}");
}
let status = std::process::Command::new("sh").arg("-c").arg(shell_command).status()?;
Ok(status.success())
}