Sanitize input of qalculate

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/2/head
trivernis 3 years ago
parent 9d4ed2dfb5
commit 8f98399ea9
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -1,9 +1,12 @@
use crate::providers::qalc;
use regex::Regex;
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::{Args, CommandResult};
use serenity::model::channel::Message;
static QALC_HELP: &[&str] = &["help", "--help", "-h", "h"];
#[command]
#[description("Calculates an expression")]
#[min_args(1)]
@ -11,8 +14,32 @@ use serenity::model::channel::Message;
#[example("1 * 1 + 1 / sqrt(2)")]
async fn qalc(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let expression = args.message();
let result = qalc::qalc(expression).await?;
msg.reply(ctx, format!("`{}`", result)).await?;
lazy_static::lazy_static! {
static ref ERROR_REGEX: Regex = Regex::new(r"^error:.*").unwrap();
}
if QALC_HELP.contains(&expression) {
msg.channel_id.send_message(ctx, |f| {
f.embed(|e| e.title("Help").description("Read the [Qalculate! Manual](https://qalculate.github.io/manual/index.html)"))
}).await?;
} else {
let result = qalc::qalc(expression).await?;
let mut description = format!("`{}`", result);
if ERROR_REGEX.is_match(&result) {
description +=
"\nRead the [Qalculate! Manual](https://qalculate.github.io/manual/index.html)";
}
msg.channel_id
.send_message(ctx, |f| {
f.embed(|e| {
e.description(description)
.footer(|f| f.text("Powered by qalculate!"))
})
})
.await?;
}
Ok(())
}

@ -1,7 +1,7 @@
use crate::providers::music::queue::Song;
use crate::providers::music::responses::{PlaylistEntry, VideoInformation};
use crate::utils::error::BotResult;
use crate::utils::run_command_async;
use crate::utils::process::run_command_async;
use futures::future::BoxFuture;
use futures::FutureExt;
use std::sync::atomic::{AtomicU8, Ordering};

@ -1,8 +1,9 @@
use crate::utils::error::BotResult;
use crate::utils::run_command_async;
use crate::utils::process::{run_command_async, sanitize_argument};
/// Runs the qalc command with the given expression
pub async fn qalc(expression: &str) -> BotResult<String> {
let result = run_command_async("qalc", &[expression]).await?;
let expression = sanitize_argument(expression, true)?;
let result = run_command_async("qalc", &[&*expression]).await?;
Ok(result)
}

@ -28,6 +28,9 @@ pub enum BotError {
#[error("Reqwest Error: {0}")]
Reqwest(#[from] reqwest::Error),
#[error("Detected CLI injection attempt")]
CliInject,
#[error("{0}")]
Msg(String),
}

@ -1,15 +1,11 @@
use std::collections::VecDeque;
use rand::Rng;
use std::io;
use std::process::Stdio;
use tokio::io::AsyncReadExt;
use tokio::process::Command;
use std::collections::VecDeque;
pub(crate) mod context_data;
pub(crate) mod error;
pub(crate) mod logging;
pub(crate) mod messages;
pub(crate) mod process;
/// Fisher-Yates shuffle for VecDeque
pub fn shuffle_vec_deque<T>(deque: &mut VecDeque<T>) {
@ -20,23 +16,3 @@ pub fn shuffle_vec_deque<T>(deque: &mut VecDeque<T>) {
deque.swap(i, rng.gen_range(0..i + 1))
}
}
/// Asynchronously runs a given command and returns the output
pub async fn run_command_async(command: &str, args: &[&str]) -> io::Result<String> {
log::trace!("Running command '{}' with args {:?}", command, args);
let cmd = Command::new(command)
.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let mut stderr = String::new();
let mut output = String::new();
cmd.stderr.unwrap().read_to_string(&mut stderr).await?;
if stderr.len() != 0 {
log::debug!("STDERR of command {}: {}", command, stderr);
}
cmd.stdout.unwrap().read_to_string(&mut output).await?;
log::trace!("Command output is {}", output);
Ok(output)
}

@ -0,0 +1,51 @@
use crate::utils::error::{BotError, BotResult};
use regex::Regex;
use std::io;
use std::process::Stdio;
use tokio::io::AsyncReadExt;
use tokio::process::Command;
/// Asynchronously runs a given command and returns the output
pub async fn run_command_async(command: &str, args: &[&str]) -> io::Result<String> {
log::trace!("Running command '{}' with args {:?}", command, args);
let cmd = Command::new(command)
.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let mut stderr = String::new();
let mut output = String::new();
cmd.stderr.unwrap().read_to_string(&mut stderr).await?;
if stderr.len() != 0 {
log::debug!("STDERR of command {}: {}", command, stderr);
}
cmd.stdout.unwrap().read_to_string(&mut output).await?;
log::trace!("Command output is {}", output);
Ok(output)
}
/// Sanitizes a command line argument and throws an error
/// on a possible injection attempt
pub fn sanitize_argument(arg: &str, detect_help: bool) -> BotResult<String> {
log::debug!("Sanitizing argument '{}'", arg);
lazy_static::lazy_static! {
static ref HELP_FLAG: Regex = Regex::new(r"^\s*(-*)h(elp)?\s*$").unwrap();
static ref FLAG_REGEX: Regex = Regex::new(r"^\s*-\w*\s*$").unwrap();
}
if FLAG_REGEX.is_match(arg) {
log::debug!("Detected STDIN injection");
return Err(BotError::CliInject);
}
if detect_help && HELP_FLAG.is_match(arg) {
log::debug!("Detected help injection");
return Err(BotError::CliInject);
}
let arg = arg.replace("--", "\\-\\-");
if arg.is_empty() {
return Err(BotError::CliInject);
}
log::debug!("Sanitized argument is '{}'", arg);
Ok(arg)
}
Loading…
Cancel
Save