An RFC 7489-compliant DMARC aggregate report parser written in Rust. It can be used both as a library in your own Rust projects and as a standalone CLI tool for viewing reports in the terminal, as HTML, or as Markdown.
- Parses DMARC aggregate feedback XML as defined in RFC 7489 Appendix C
- Zero-copy deserialization into strongly-typed Rust structs
FromStr,TryFrom<&str>, andTryFrom<&[u8]>trait implementations for ergonomic usage- Optional CLI with colorized terminal output, HTML, and Markdown rendering
- CLI supports
.xml,.xml.gz,.gz, and.zipinput files - CLI accepts multiple files and renders them as a single aggregate view
Add the crate to your project:
cargo add dmarc-report-parserInstall with the cli feature enabled:
cargo install dmarc-report-parser --features cliThis installs the dmarc-report binary.
use dmarc_report_parser::{parse, Report};
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<feedback>
<report_metadata>
<org_name>Example Corp</org_name>
<email>dmarc@example.com</email>
<report_id>abc123</report_id>
<date_range>
<begin>1609459200</begin>
<end>1609545600</end>
</date_range>
</report_metadata>
<policy_published>
<domain>example.com</domain>
<adkim>r</adkim>
<aspf>r</aspf>
<p>none</p>
<sp>none</sp>
<pct>100</pct>
</policy_published>
<record>
<row>
<source_ip>192.0.2.1</source_ip>
<count>5</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>pass</spf>
</policy_evaluated>
</row>
<identifiers>
<envelope_from>example.com</envelope_from>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<spf>
<domain>example.com</domain>
<result>pass</result>
</spf>
</auth_results>
</record>
</feedback>"#;
let report: Report = parse(xml).unwrap();
assert_eq!(report.report_metadata.org_name, "Example Corp");
assert_eq!(report.records.len(), 1);let xml_bytes: &[u8] = b"<?xml version=\"1.0\"?>
<feedback>
<report_metadata>
<org_name>Test</org_name>
<email>test@example.com</email>
<report_id>1</report_id>
<date_range><begin>0</begin><end>0</end></date_range>
</report_metadata>
<policy_published>
<domain>example.com</domain><p>none</p><sp>none</sp><pct>100</pct>
</policy_published>
<record>
<row>
<source_ip>127.0.0.1</source_ip><count>1</count>
<policy_evaluated><disposition>none</disposition><dkim>pass</dkim><spf>pass</spf></policy_evaluated>
</row>
<identifiers><envelope_from>example.com</envelope_from><header_from>example.com</header_from></identifiers>
<auth_results><spf><domain>example.com</domain><result>pass</result></spf></auth_results>
</record>
</feedback>";
let report = dmarc_report_parser::parse_bytes(xml_bytes).unwrap();
assert_eq!(report.report_metadata.org_name, "Test");use std::str::FromStr;
use dmarc_report_parser::Report;
# let xml = r#"<?xml version="1.0"?>
# <feedback>
# <report_metadata>
# <org_name>Test</org_name><email>t@e.com</email><report_id>1</report_id>
# <date_range><begin>0</begin><end>0</end></date_range>
# </report_metadata>
# <policy_published><domain>e.com</domain><p>none</p><sp>none</sp><pct>100</pct></policy_published>
# <record>
# <row><source_ip>127.0.0.1</source_ip><count>1</count>
# <policy_evaluated><disposition>none</disposition><dkim>pass</dkim><spf>pass</spf></policy_evaluated>
# </row>
# <identifiers><envelope_from>e.com</envelope_from><header_from>e.com</header_from></identifiers>
# <auth_results><spf><domain>e.com</domain><result>pass</result></spf></auth_results>
# </record>
# </feedback>"#;
let report = Report::from_str(xml).unwrap();
let report: Report = xml.parse().unwrap();
let report = Report::try_from(xml).unwrap();The dmarc-report CLI reads one or more DMARC aggregate report files and
renders them in the chosen format. When multiple files are passed, the output
is an aggregate view containing an overview, a per-report summary, and a
combined records table.
Usage: dmarc-report [OPTIONS] <FILES>...
Arguments:
<FILES>... One or more DMARC report files (.xml, .xml.gz, .zip, or .gz)
Options:
-f, --format <FORMAT> Output format [default: terminal]
[possible values: terminal, html, markdown]
-o, --output <FILE> Write output to a file instead of stdout
-h, --help Print help
-V, --version Print version
# Display a report in the terminal with colors
dmarc-report report.xml
# Render as HTML and save to a file
dmarc-report report.xml --format html --output report.html
# Render as Markdown from a gzip-compressed report
dmarc-report report.xml.gz --format markdown
# Parse a report inside a zip archive
dmarc-report report.zip
# Aggregate multiple reports into a single Markdown view
dmarc-report *.xml.gz --format markdownThe library exposes the full RFC 7489 Appendix C schema as Rust types:
| Type | Description |
|---|---|
Report |
Top-level aggregate feedback report |
Aggregate |
Combined view across multiple reports |
ReportMetadata |
Report generator metadata |
DateRange |
UTC time range (Unix timestamps) |
PolicyPublished |
Published DMARC policy for the domain |
Record |
A single message record |
Row |
Per-message data row |
PolicyEvaluated |
DMARC evaluation results |
PolicyOverrideReason |
Reason for policy override |
Identifiers |
Message identifiers |
AuthResults |
Authentication results |
DkimAuthResult |
DKIM signature evaluation result |
SpfAuthResult |
SPF check result |
And enums: AlignmentMode, Disposition, DmarcResult, DkimResult, SpfResult,
SpfDomainScope, PolicyOverride.
MIT — see LICENSE for details.