Skip to content

Commit

Permalink
Merge pull request #48 from frack113/feat/printing-capabilities
Browse files Browse the repository at this point in the history
Better ouput
  • Loading branch information
frack113 authored Feb 2, 2025
2 parents 2568d38 + fd0af1b commit b5450eb
Show file tree
Hide file tree
Showing 15 changed files with 464 additions and 194 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
---
# SPDX-FileCopyrightText: 2023 The MalwareTracesGenerator development team
# yamllint disable-line
# SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. <https://fsfe.org>
#
# SPDX-License-Identifier: CC0-1.0
name: Lint the project
Expand Down
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
name = "malware-traces-generator"
version = "1.0.0"
edition = "2021"
rust-version = "1.74.1"
rust-version = "1.83.0"
description = "Generate malware traces for detection tests"
documentation = "https://frack113.github.io/MalwareTracesGenerator/"
repository = "https://github.com/frack113/MalwareTracesGenerator/"
Expand Down Expand Up @@ -44,6 +44,8 @@ rand = "0.8.5"
regex_generate = "0.2.3"
toml = "0.8.19"
serde = { version = "1.0.214", features = ["derive"] }
indicatif = { version = "0.17.11" }
console = { version = "0.15.10" }

[build-dependencies]
embed-resource = "3.0.1"
Expand Down
2 changes: 1 addition & 1 deletion deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ all-features = true

[licenses]
confidence-threshold = 1.0
allow = ["Apache-2.0", "MIT", "Unicode-DFS-2016"]
allow = ["Apache-2.0", "MIT", "Unicode-3.0"]

[bans]
multiple-versions = "deny"
Expand Down
32 changes: 19 additions & 13 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,43 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later

pub mod generate;
pub mod traces;

use crate::commands::{generate::Generate, traces::Traces};
use crate::commands::{
generate::Generate,
traces::{Traces, Traversable as TracesTraversable},
};
use clap::{Parser, Subcommand};
use std::error::Error;
use std::process::ExitCode;

mod generate;
mod traces;

#[derive(Parser)]
#[clap(author, version)]
#[clap(arg_required_else_help = true)]
pub struct Arguments {
#[clap(subcommand)]
pub command: Command,
pub command: Commands,
}

#[derive(Subcommand)]
pub enum Command {
pub enum Commands {
Traces(Traces),
Generate(Generate),
}

pub trait Traversable {
fn traverse(&self) -> &dyn Runnable;
}

pub trait Runnable {
fn run(&self) -> Result<(), Box<dyn Error>>;
fn run(&self) -> ExitCode;
}

impl Runnable for Arguments {
fn run(&self) -> Result<(), Box<dyn Error>> {
impl Traversable for Arguments {
fn traverse(&self) -> &dyn Runnable {
match &self.command {
Command::Traces(traces) => traces as &dyn Runnable,
Command::Generate(generate) => generate,
Commands::Traces(traces) => traces.traverse().as_runnable(),
Commands::Generate(generate) => generate,
}
.run()
}
}
135 changes: 121 additions & 14 deletions src/commands/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,29 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later

use crate::commands::{traces::Traces, Runnable};
use crate::{
commands::{
traces::{Traces, Traversable},
Runnable,
},
displayer::Displayer,
};
use clap::Parser;
use console::{style, Emoji};
use serde::Deserialize;
use std::{error::Error, fs::read_to_string, path::PathBuf};
use std::{
fmt::{Display, Formatter, Result as FormatResult},
fs::read_to_string,
path::PathBuf,
process::ExitCode,
};
use toml::from_str;

#[derive(Deserialize)]
struct Configuration {
struct Generation {
metadata: Metadata,
traces: Vec<Traces>,
#[serde(rename(deserialize = "traces"))]
commands: Vec<Traces>,
}

#[derive(Deserialize)]
Expand All @@ -30,22 +43,116 @@ struct Author {

#[derive(Parser)]
pub struct Generate {
#[clap(required = true, help = "Path to the configuration file")]
path: PathBuf,
#[clap(required = true, help = "Path to the generation file")]
generation: PathBuf,
}

impl Runnable for Generate {
fn run(&self) -> Result<(), Box<dyn Error>> {
if !self.path.try_exists()? || !self.path.is_file() {
return Ok(());
impl Display for Metadata {
fn fmt(&self, formatter: &mut Formatter) -> FormatResult {
let mut authors_lines = String::new();

if let Some(authors) = &self.authors {
for (index, author) in authors.iter().enumerate() {
authors_lines.push_str(
format!(
"{}Author n°{}\n\
{author}",
Emoji("🧑 ", ""),
index + 1,
)
.as_str(),
);

if index != authors.len() - 1 {
authors_lines.push_str("\n\n");
}
}
}

let configuration: Configuration = from_str(read_to_string(self.path.clone())?.as_str())?;
write!(
formatter,
"{}Name: {}\n\
{}Version: {}\n\
{}References: {:#?}\n\n\
{authors_lines}",
Emoji("📝 ", ""),
self.name,
Emoji("🤖 ", ""),
self.version,
Emoji("🔗 ", ""),
self.references,
)
}
}

impl Display for Author {
fn fmt(&self, formatter: &mut Formatter) -> FormatResult {
let mut email_line = String::new();

if let Some(email) = &self.email {
email_line = format!("\n{}Email: {email}", Emoji("📧 ", ""));
}

write!(
formatter,
"{}Name: {}\
{email_line}",
Emoji("💬 ", ""),
self.name,
)
}
}

impl Runnable for Generate {
fn run(&self) -> ExitCode {
let mut displayer = Displayer::new();
displayer.loading("Reading the generation file");

let file_content = match read_to_string(self.generation.clone()) {
Ok(content) => content,
Err(error) => {
displayer.failure(format!("Failed to read the generation file: {error}").as_str());

return ExitCode::FAILURE;
}
};

displayer.success("The generation file is read");
displayer.loading("Parsing the content");

let generation = match from_str::<Generation>(file_content.as_str()) {
Ok(generation) => generation,
Err(error) => {
displayer.failure(format!("Failed to parse the content: {error}").as_str());

return ExitCode::FAILURE;
}
};

displayer.success("The content is parsed");

println!(
"\nGeneration metadata\n\
{}",
generation.metadata
);

for (index, command) in generation.commands.iter().enumerate() {
let trace = command.traverse();

println!(
"\n{} Generating {} trace",
style(format!("[{}/{}]", index + 1, generation.commands.len()))
.blue()
.bold(),
trace.name(),
);

for trace in configuration.traces {
trace.run()?;
if trace.run() == ExitCode::FAILURE {
return ExitCode::FAILURE;
}
}

Ok(())
ExitCode::SUCCESS
}
}
28 changes: 18 additions & 10 deletions src/commands/traces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,39 @@ use crate::commands::{
};
use clap::{Args, Subcommand};
use serde::Deserialize;
use std::error::Error;

pub mod drivers;
pub mod processes;
mod drivers;
mod processes;

#[derive(Args, Deserialize)]
pub struct Traces {
#[clap(subcommand)]
#[serde(flatten)]
pub command: Command,
pub command: Commands,
}

#[derive(Subcommand, Deserialize)]
#[serde(rename_all = "snake_case", untagged)]
pub enum Command {
pub enum Commands {
Drivers(Drivers),
Processes(Processes),
}

impl Runnable for Traces {
fn run(&self) -> Result<(), Box<dyn Error>> {
pub trait Trace: Runnable {
fn name(&self) -> &str;
fn as_runnable(&self) -> &dyn Runnable;
}

pub trait Traversable {
fn traverse(&self) -> &dyn Trace;
}

impl Traversable for Traces {
fn traverse(&self) -> &dyn Trace {
match &self.command {
Command::Drivers(drivers) => drivers as &dyn Runnable,
Command::Processes(processes) => processes,
Commands::Drivers(drivers) => drivers as &dyn Traversable,
Commands::Processes(processes) => processes,
}
.run()
.traverse()
}
}
16 changes: 7 additions & 9 deletions src/commands/traces/drivers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,29 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later

use crate::commands::{traces::drivers::byovd::Byovd, Runnable};
use crate::commands::traces::{drivers::byovd::Byovd, Trace, Traversable};
use clap::{Args, Subcommand};
use serde::Deserialize;
use std::error::Error;

pub mod byovd;
mod byovd;

#[derive(Args, Deserialize)]
pub struct Drivers {
#[clap(subcommand)]
#[serde(flatten)]
pub command: Command,
pub command: Commands,
}

#[derive(Subcommand, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Command {
pub enum Commands {
Byovd(Byovd),
}

impl Runnable for Drivers {
fn run(&self) -> Result<(), Box<dyn Error>> {
impl Traversable for Drivers {
fn traverse(&self) -> &dyn Trace {
match &self.command {
Command::Byovd(byovd) => byovd as &dyn Runnable,
Commands::Byovd(byovd) => byovd,
}
.run()
}
}
Loading

0 comments on commit b5450eb

Please sign in to comment.