Skip to content

Commit

Permalink
Merge pull request #4 from MarioKart8-Integrity/feat/result
Browse files Browse the repository at this point in the history
Feat: Generating Report
  • Loading branch information
Simon9991 authored May 4, 2024
2 parents 89fba35 + 718093d commit 7538c0e
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 22 deletions.
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: Rust CI

on:
push:
pull_request:

env:
CARGO_TERM_COLOR: always
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ Cargo.lock
# MacOS
.DS_Store
*.dSYM

# App related
results/
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ edition = "2021"

[dependencies]
anyhow = "1.0.82"
bincode = "1.3.3"
hex = "0.4.3"
serde = { version = "1.0.198", features = ["derive"] }
sha2 = "0.10.8"
thiserror = "1.0.59"
time = "0.3.36"
toml = "0.8.12"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
urlencoding = "2.1.3"
70 changes: 52 additions & 18 deletions src/file_integrity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ use std::{
};
use thiserror::Error;

use crate::config::Config;
use crate::{config::Config, report::ChecksumReport};

/// TODO: how to retrieve the actual pure checksum (reference value)
/// Fictional struct for the moment / prob use an external library
#[derive(Clone, Debug, PartialEq, PartialOrd, Hash)]
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Checksum {
value: Vec<u8>,
}

impl Checksum {
/// A checksum with no stored data.
pub const UNIT_CHECKSUM: Checksum = Checksum { value: vec![] };
}

impl std::fmt::Display for Checksum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex::encode(&self.value))
Expand All @@ -27,35 +32,57 @@ impl std::fmt::Display for Checksum {
struct GameFile {
path: PathBuf,
expected_checksum: Checksum,
computed_checksum: Checksum,
}

impl GameFile {
pub fn compute_real_checksum(&self) -> Checksum {
pub fn compute_real_checksum(&mut self) -> Checksum {
let mut hasher = Sha256::new();
let bytes = match fs::read(&self.path) {
Ok(bytes) => bytes,
Err(_) => return Checksum { value: vec![] },
Err(_) => return Checksum::UNIT_CHECKSUM,
};

hasher.update(&bytes);

let res = hasher.finalize();
// the checksum we get from the file
let checksum_bytes = hasher.finalize();

// dbg variables
let hex_to_str: String = res.to_vec().iter().fold(String::new(), |mut out, b| {
write!(out, "{:02x}", b).expect("Checksum hex string");
out
});
let hex_to_str: String =
checksum_bytes
.to_vec()
.iter()
.fold(String::new(), |mut out, b| {
write!(out, "{:02x}", b).expect("Checksum hex string");
out
});
dbg!(&self.path, hex_to_str);
// end of dbg

self.computed_checksum = Checksum {
value: checksum_bytes.to_vec(),
};

Checksum {
value: res.to_vec(),
value: checksum_bytes.to_vec(),
}
}

pub fn checksums_match(&self) -> bool {
self.expected_checksum == self.compute_real_checksum()
/// Makes a `ChecksumReport` for this GameFile.
pub fn get_report(&mut self) -> ChecksumReport {
// make the report
ChecksumReport {
file_path: self.path.clone(),
is_matching: self.checksums_match(),
got: self.computed_checksum.clone(),
}
}

/// Checks if the checksums are equal + compute the checksum of the file
pub fn checksums_match(&mut self) -> bool {
let real_checksum = self.compute_real_checksum();
self.expected_checksum == real_checksum
}
}

Expand Down Expand Up @@ -93,7 +120,8 @@ impl FileIntegrity {
} else {
let g_file: GameFile = GameFile {
path: p.to_path_buf(),
expected_checksum: Checksum { value: vec![] }, // Placeholder
expected_checksum: Checksum::UNIT_CHECKSUM, // Placeholder
computed_checksum: Checksum::UNIT_CHECKSUM, // Placeholder
};
game_files.push(g_file);
}
Expand All @@ -106,15 +134,21 @@ impl FileIntegrity {
}

/// Checking if EVERY file is actully matching the expected checksum
pub fn check(&self) -> bool {
let mut res = true;
pub fn check(&mut self) -> Result<(), Vec<ChecksumReport>> {
let mut failed_files = vec![];

for f in self.game_files.iter() {
// TODO: remove the [..10] slice
for f in self.game_files[..10].iter_mut() {
if !f.checksums_match() {
res = false;
failed_files.push(f.get_report());
}
}
res

if failed_files.is_empty() {
Ok(()) // no failures :3
} else {
Err(failed_files)
}
}
}

Expand Down
17 changes: 14 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
use config::Config;
use file_integrity::FileIntegrity;
use report::Report;
mod config;
mod file_integrity;
mod report;

fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();

match FileIntegrity::new(&Config::new()?) {
Ok(app) => {
let res = app.check();
println!("File check result: {}", res);
Ok(mut app) => {
let mut report = Report::new();

match app.check() {
Ok(_) => {
// we will not do anything, just continue program
}
Err(checksum_reports) => {
report.set_incorrect_file_checksums(checksum_reports);
}
}
report.generate_report()?;
}
Err(e) => {
tracing::error!("Failed to initialize the application. Error: {:?}", e);
Expand Down
86 changes: 86 additions & 0 deletions src/report.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use std::{collections::HashSet, io::Write, path::PathBuf};

use anyhow::Result;
use thiserror::Error;
use time::OffsetDateTime;

use crate::file_integrity::Checksum;

/// A specific report of a file's checksum results.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ChecksumReport {
pub file_path: PathBuf,
/// Whether or not the checksums are matching. If false, assume the user is a cheating loser.
pub is_matching: bool,
pub got: Checksum,
}

/// A report of all the tool's analysis results.
/// TODO: Add PC specs, connected hardware, PID of opened programs, etc.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Report {
incorrect_file_checksums: HashSet<ChecksumReport>,
}

impl Report {
/// Creates an empty report.
pub fn new() -> Self {
Self {
incorrect_file_checksums: HashSet::new(),
}
}

/// Sets the `incorrect_file_checksums` field from a Vec<ChecksumReport>.
pub fn set_incorrect_file_checksums(&mut self, incorrect_file_checksums: Vec<ChecksumReport>) {
self.incorrect_file_checksums = incorrect_file_checksums.into_iter().collect();
}

/// Writes the report to disk.
/// TODO: choose the generatted style (markdown, json, etc)
pub fn generate_report(&self) -> Result<(), ReportError> {
const REPORT_FOLDER: &str = "./results/";

let utc_date = OffsetDateTime::now_utc();
let utc_string = {
let mut s = String::new();

// date
s.push_str(&utc_date.date().to_string());
s.push('_');

// time
let time = utc_date.time();
s.push_str(&time.hour().to_string());
s.push(':');
s.push_str(&time.minute().to_string());

s
};

let file_path = format!("{}{}.report", REPORT_FOLDER, utc_string);

std::fs::create_dir_all(REPORT_FOLDER)?;

let mut file = std::fs::File::create(file_path)?;

writeln!(file, "Report generated on: {}\n\n## Checksums", utc_date)?;

for checksum_report in &self.incorrect_file_checksums {
writeln!(
file,
"[incorrect] --> {}: {}",
checksum_report.file_path.to_string_lossy(),
checksum_report.got
)?;
}

Ok(())
}
}

/// An error that can occur when generating a report.
#[derive(Debug, Error)]
pub enum ReportError {
#[error("Failed to build report due to IO error: {0}")]
IoError(#[from] std::io::Error),
}

0 comments on commit 7538c0e

Please sign in to comment.