From 3bffae34eb3196fbe9a2cbc33c5c52e59461a5f4 Mon Sep 17 00:00:00 2001 From: shukuru Date: Sun, 18 Feb 2024 22:11:16 +0100 Subject: Update XML parsing feature - Implements DMARC raw deserialization - Add test - Add a fixture --- src/data_structures.rs | 125 ++++++++++++++++++++++++++++++++++++++------- src/main.rs | 3 ++ src/parser/deserializer.rs | 86 +++++++++++++++++++++++++++++++ src/parser/mod.rs | 1 + 4 files changed, 196 insertions(+), 19 deletions(-) create mode 100644 src/parser/deserializer.rs create mode 100644 src/parser/mod.rs (limited to 'src') diff --git a/src/data_structures.rs b/src/data_structures.rs index a90b7b0..ef1bcc2 100644 --- a/src/data_structures.rs +++ b/src/data_structures.rs @@ -1,35 +1,122 @@ -enum DMARCPolicy { +use serde::Deserialize; + +// Raw DS to deserialize XML report + +#[derive(Debug, Deserialize)] +pub struct Feedback { + pub version: String, + pub report_metadata: ReportMetadata, + pub policy_published: PolicyPublished, + pub record: Record, +} + +#[derive(Debug, Deserialize)] +pub struct ReportMetadata { + pub org_name: String, + pub email: String, + pub report_id: String, + pub date_range: DateRange, +} + +#[derive(Debug, Deserialize)] +pub struct DateRange { + pub begin: String, + pub end: String, +} + +#[derive(Debug, Deserialize)] +pub struct PolicyPublished { + pub domain: String, + pub adkim: String, + pub aspf: String, + pub p: String, + pub sp: String, + pub pct: String, + pub fo: String, +} + +#[derive(Debug, Deserialize)] +pub struct Record { + pub row: Row, + pub identifiers: Identifiers, + pub auth_results: AuthResults, +} + +#[derive(Debug, Deserialize)] +pub struct Row { + pub source_ip: String, + pub count: String, + pub policy_evaluated: PolicyEvaluated, +} + +#[derive(Debug, Deserialize)] +pub struct PolicyEvaluated { + pub disposition: String, + pub dkim: String, + pub spf: String, +} + +#[derive(Debug, Deserialize)] +pub struct Identifiers { + pub envelope_to: String, + pub envelope_from: String, + pub header_from: String, +} + +#[derive(Debug, Deserialize)] +pub struct AuthResults { + pub dkim: DKIM, + pub spf: SPF, +} + +#[derive(Debug, Deserialize)] +pub struct DKIM { + pub domain: String, + pub selector: String, + pub result: String, +} + +#[derive(Debug, Deserialize)] +pub struct SPF { + pub domain: String, + pub scope: String, + pub result: String, +} + +// Rust interface DS for HTML generation + +pub enum DMARCPolicy { None, Quarantine, - Reject + Reject, } -enum DMARCAlignment { +pub enum DMARCAlignment { Relaxed, - Strict + Strict, } -enum DMARCFailureOption { +pub enum DMARCFailureOption { AlignedPassFailure, OtherAlignedPassFailure, SignatureAlignmentFailure, - SPFFailure + SPFFailure, } -struct DMARCRecord { - domain: String, - adkim: DMARCAlignment, - aspf: DMARCAlignment, - policy: DMARCPolicy, - subdomain_policy: DMARCPolicy, - percentage: u64, - failure_opt: DMARCFailureOption +pub struct DMARCRecord { + pub domain: String, + pub adkim: DMARCAlignment, + pub aspf: DMARCAlignment, + pub policy: DMARCPolicy, + pub subdomain_policy: DMARCPolicy, + pub percentage: u64, + pub failure_opt: DMARCFailureOption, } pub struct DMARCReport { - id: String, - org: String, - record: DMARCRecord, - dkim_pass: bool, - spf_pass: bool + pub id: String, + pub org: String, + pub record: DMARCRecord, + pub dkim_pass: bool, + pub spf_pass: bool, } diff --git a/src/main.rs b/src/main.rs index e7a11a9..2125cf4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ +pub mod data_structures; +pub mod parser; + fn main() { println!("Hello, world!"); } diff --git a/src/parser/deserializer.rs b/src/parser/deserializer.rs new file mode 100644 index 0000000..75d3c6e --- /dev/null +++ b/src/parser/deserializer.rs @@ -0,0 +1,86 @@ +use std::io::Read; + +use crate::data_structures; + +fn deserialize_dmarc_report(reader: R) -> +Result { + serde_xml_rs::from_reader(reader) +} + +#[cfg(test)] +mod test { + use super::*; + + use std::fs::File; + use std::io::BufReader; + + #[test] + fn test_deserialize_dmarc_report() { + let dmarc_report_file = File::open("fixtures/dmarc_report_fixture.xml").unwrap(); + + let reader = BufReader::new(dmarc_report_file); + + let should_be = data_structures::Feedback { + version: String::from("1.0"), + report_metadata: data_structures::ReportMetadata { + org_name: String::from("Enterprise Outlook"), + email: String::from("dmarcreport@microsoft.com"), + report_id: String::from("3374fb5148ba40c1a5cf8e3d36f34a34"), + date_range: data_structures::DateRange { + begin: String::from("1707436800"), + end: String::from("1707523200"), + }, + }, + policy_published: data_structures::PolicyPublished { + domain: String::from("boitalett.re"), + adkim: String::from("s"), + aspf: String::from("s"), + p: String::from("reject"), + sp: String::from("reject"), + pct: String::from("100"), + fo: String::from("0"), + }, + record: data_structures::Record { + row: data_structures::Row { + source_ip: String::from("195.154.102.43"), + count: String::from("1"), + policy_evaluated: data_structures::PolicyEvaluated { + disposition: String::from("none"), + dkim: String::from("pass"), + spf: String::from("pass"), + }, + }, + identifiers: data_structures::Identifiers { + envelope_to: String::from("babilou.com"), + envelope_from: String::from("boitalett.re"), + header_from: String::from("boitalett.re"), + }, + auth_results: data_structures::AuthResults { + dkim: data_structures::DKIM { + domain: String::from("boitalett.re"), + selector: String::from("2023101501"), + result: String::from("pass"), + }, + spf: data_structures::SPF { + domain: String::from("boitalett.re"), + scope: String::from("mfrom"), + result: String::from("pass"), + }, + }, + }, + }; + + match deserialize_dmarc_report(reader) { + Ok(report) => { + assert_eq!( + report.report_metadata.report_id, + should_be.report_metadata.report_id + ); + println!("{:?}", report); + }, + Err(err) => { + panic!("{:?}", err); + } + } + } +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs new file mode 100644 index 0000000..218d426 --- /dev/null +++ b/src/parser/mod.rs @@ -0,0 +1 @@ +pub mod deserializer; -- cgit v1.2.3