From c5b440311c459b539fcbf45214bdb11aa2097ab9 Mon Sep 17 00:00:00 2001 From: kageru Date: Thu, 2 Jan 2020 17:51:02 +0100 Subject: [PATCH] Initial commit --- .gitignore | 3 +++ Cargo.toml | 12 ++++++++++++ README.md | 21 +++++++++++++++++++++ config.toml | 4 ++++ src/commands.rs | 41 +++++++++++++++++++++++++++++++++++++++++ src/file.rs | 9 +++++++++ src/main.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 138 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 config.toml create mode 100644 src/commands.rs create mode 100644 src/file.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a0bd527 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +secret diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f00aec3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "rceaadb" +version = "0.1.0" +authors = ["kageru "] +edition = "2018" + +[dependencies] +serenity = "0.7" +serde = "1.0.104" +toml = "0.5.5" +lazy_static = "1.4.0" +itertools = "0.8.2" diff --git a/README.md b/README.md new file mode 100644 index 0000000..1beba97 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# RCEAADB + +## I’m sorry? +RCEAABD. Remote code execution as a discord bot. +Almost as good as ncmpcpp. + +## But why? Do you hate security that much? +Evidently. + +## Seriously, why? +I noticed a use for this. +More than once did people ask me (or someone else) +to restart a game server, a long-running script, a discord bot, etc. +because something was broken, and restarting it was the easiest way to ‘fix’ it. + +The idea here is to give people you trust a way to selectively run certain commands on a server. +The bot will only ever execute the commands from the config. +The only user input are the triggers in discord messages that are then checked against the config. +No message content is ever executed, neither directly nor indirectly. +Every command is limited to one or multiple users. +See the example config for more information. diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..7c6504f --- /dev/null +++ b/config.toml @@ -0,0 +1,4 @@ +[[command]] +trigger = ">restart_example" +command = "/usr/bin/restart_something" +users = [12345, 54321] diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..ba550be --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,41 @@ +use itertools::Itertools; +use lazy_static; +use serde::Deserialize; +use serenity::model::channel::Message; + +#[derive(Deserialize, Debug)] +pub struct Command<'a> { + trigger: &'a str, + command: &'a str, + users: Vec, +} + +#[derive(Deserialize, Debug)] +struct Config { + #[serde(rename = "command")] + commands: Vec>, +} + +lazy_static! { + static ref RAW_CONFIG: String = super::file::read_file("config.toml").join("\n"); + static ref COMMANDS: Vec> = { + toml::from_str::(&RAW_CONFIG).expect("Error in config").commands + }; +} + +pub fn print_commands() { + COMMANDS.iter().for_each(|c| println!("{:?}", c)); +} + +pub fn find_matching(message: &str) -> Option<&Command> { + COMMANDS.iter().find(|&c| message.starts_with(&c.trigger)) +} + +impl<'a> Command<'a> { + pub fn execute(&self, msg: &Message) -> Result<(), &'static str> { + if !self.users.contains(&msg.author.id.0) { + return Err("You don’t have the permissions to execute this command."); + } + unimplemented!() + } +} diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..dda620e --- /dev/null +++ b/src/file.rs @@ -0,0 +1,9 @@ +use std::fs::File; +use std::io::{prelude::*, BufReader}; + +pub fn read_file(path: &'static str) -> impl Iterator { + let reader = BufReader::new(File::open(path).expect("File not found")); + reader + .lines() + .map(|l| l.expect(&format!("Could not read from file"))) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..52e65a4 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,48 @@ +#[macro_use] +extern crate lazy_static; +use commands::*; +use serenity::model::channel::Message; +use serenity::model::id::ChannelId; +use serenity::prelude::*; +mod commands; +mod file; + +pub fn main() { + /* + Client::new( + file::read_file("secret") + .next() + .expect("The file containing the login secret is present but empty"), + Handler, + ) + .expect("Error creating client") + .start() + .expect("Could not connect to discord"); + */ + print_commands(); +} + +const PREFIX: char = '$'; + +pub struct Handler; +impl EventHandler for Handler { + fn message(&self, ctx: Context, msg: Message) { + if !msg.content.starts_with(PREFIX) { + return; + } + if let Some(command) = find_matching(&msg.content) { + let response = match command.execute(&msg) { + Err(e) => e, + // TODO: maybe check the return code here? + Ok(()) => "Done", + }; + send(msg.channel_id, response, &ctx); + } + } +} + +pub fn send(target: ChannelId, message: &str, ctx: &Context) { + if let Err(cause) = target.say(&ctx.http, message) { + println!("Could not send message: {}", cause); + } +}