///|
pub fn analyze_suite(
domain : String,
spf_text : String,
dmarc_text : String,
dkim_text : String,
mtasts_text : String,
tlsrpt_text : String,
bimi_text : String,
) -> MailSecuritySuiteReport {
let clean_domain = normalize_domain(domain)
let spf = parse_spf(spf_text)
let dmarc = parse_dmarc(dmarc_text)
let dkim = parse_dkim(dkim_text)
let mtasts = parse_mtasts(mtasts_text)
let tlsrpt = parse_tlsrpt(tlsrpt_text)
let bimi = parse_bimi(bimi_text)
let findings = Array::new(capacity=64)
append_domain_findings(findings, clean_domain)
append_findings(findings, spf.findings)
append_findings(findings, dmarc.findings)
append_findings(findings, dkim.findings)
append_findings(findings, mtasts.findings)
append_findings(findings, tlsrpt.findings)
append_findings(findings, bimi.findings)
append_cross_record_findings(findings, spf, dmarc, dkim, mtasts, tlsrpt, bimi)
let score = score_findings(findings)
let summary = summarize_findings(findings)
let actions = action_items(findings)
{
domain: clean_domain,
spf,
dmarc,
dkim,
mtasts,
tlsrpt,
bimi,
score,
verdict: suite_verdict(summary, score),
summary,
actions,
findings,
}
}
///|
pub fn suite_report_markdown(
domain : String,
spf_text : String,
dmarc_text : String,
dkim_text : String,
mtasts_text : String,
tlsrpt_text : String,
bimi_text : String,
) -> String {
analyze_suite(
domain, spf_text, dmarc_text, dkim_text, mtasts_text, tlsrpt_text, bimi_text,
).to_markdown()
}
///|
pub fn suite_report_json(
domain : String,
spf_text : String,
dmarc_text : String,
dkim_text : String,
mtasts_text : String,
tlsrpt_text : String,
bimi_text : String,
) -> String {
analyze_suite(
domain, spf_text, dmarc_text, dkim_text, mtasts_text, tlsrpt_text, bimi_text,
).to_json()
}
///|
fn append_domain_findings(
findings : Array[PolicyFinding],
domain : String,
) -> Unit {
if domain.is_empty() {
findings.push(
fail(
"domain.name", "Domain name is missing", "Suite reports should identify the domain being assessed.",
"", "Pass a stable domain name to analyze_suite.",
),
)
} else if domain.contains(" ") {
findings.push(
fail(
"domain.space", "Domain name contains spaces", "A DNS domain label cannot contain whitespace.",
domain, "Pass a normalized DNS domain such as example.com.",
),
)
} else if domain.contains("_") {
findings.push(
warn(
"domain.underscore",
"Domain contains an underscore",
"Underscores are normal for DNS service labels but unusual for an organizational domain.",
domain,
"Pass the organizational domain, not a service label, when building an overall report.",
penalty=3,
),
)
} else {
findings.push(pass("domain.name", "Domain name is present", domain))
}
}
///|
fn append_findings(
target : Array[PolicyFinding],
source : Array[PolicyFinding],
) -> Unit {
for item in source {
target.push(item)
}
}
///|
fn append_cross_record_findings(
findings : Array[PolicyFinding],
spf : SpfRecord,
dmarc : DmarcRecord,
dkim : DkimRecord,
mtasts : MtastsRecord,
tlsrpt : TlsRptRecord,
bimi : BimiRecord,
) -> Unit {
if spf.valid_version && dmarc.valid_version {
findings.push(
pass("suite.auth-pair", "SPF and DMARC are both present", "spf+dmarc"),
)
} else {
findings.push(
fail(
"suite.auth-pair-missing", "SPF and DMARC are not both valid", "A production sending domain should have both SPF and DMARC policy records.",
"", "Fix SPF and DMARC before onboarding the domain.",
),
)
}
if dkim.is_configured() && dmarc.valid_version {
findings.push(
pass(
"suite.dkim-dmarc",
"DKIM and DMARC can work together",
dkim.key_strength_label(),
),
)
} else if dmarc.policy == "reject" && !dkim.is_configured() {
findings.push(
warn(
"suite.dkim-needed",
"DMARC enforcement has no DKIM selector input",
"SPF-only DMARC alignment is fragile when forwarding occurs.",
"p=" + dmarc.policy,
"Add at least one production DKIM selector record.",
),
)
}
if bimi.is_configured() &&
dmarc.policy != "reject" &&
dmarc.policy != "quarantine" {
findings.push(
fail(
"suite.bimi-dmarc",
"BIMI is configured without DMARC enforcement",
"BIMI should be used after DMARC is at quarantine or reject.",
"p=" + dmarc.policy,
"Move DMARC to quarantine or reject before advertising BIMI.",
),
)
} else if bimi.is_configured() {
findings.push(
pass(
"suite.bimi-ready",
"BIMI has a compatible DMARC policy",
dmarc.policy,
),
)
}
if mtasts.is_enforcing() && tlsrpt.is_configured() {
findings.push(
pass(
"suite.transport-observable", "MTA-STS enforcement has TLS reporting", "mtasts+tlsrpt",
),
)
} else if mtasts.is_enforcing() && !tlsrpt.is_configured() {
findings.push(
warn(
"suite.transport-reporting", "MTA-STS is enforced without TLS reporting",
"TLS-RPT helps detect delivery failures after strict TLS rollout.", "mode=enforce",
"Publish a TLS-RPT record before or with enforcement.",
),
)
} else if tlsrpt.is_configured() && !mtasts.is_configured() {
findings.push(
pass(
"suite.transport-reporting-early", "TLS-RPT is available before MTA-STS enforcement",
"tlsrpt",
),
)
}
if spf.dns_lookup_count >= 8 && dkim.key_strength_label() == "missing" {
findings.push(
warn(
"suite.sender-fragility",
"Sender authentication is concentrated in SPF",
"High SPF lookup usage plus missing DKIM makes forwarding and provider changes riskier.",
spf.dns_lookup_count.to_string() + " SPF lookups",
"Add DKIM and reduce unnecessary SPF mechanisms.",
),
)
}
if dmarc.policy == "reject" &&
spf.all_qualifier == "-" &&
dkim.is_configured() &&
mtasts.is_enforcing() &&
tlsrpt.is_configured() {
findings.push(
pass(
"suite.production-ready", "Core mail-domain controls look production ready",
"spf+dmarc+dkim+mtasts+tlsrpt",
),
)
}
}
///|
pub fn summarize_findings(findings : Array[PolicyFinding]) -> ReportSummary {
let mut pass_count = 0
let mut warn_count = 0
let mut fail_count = 0
for finding in findings {
if finding.severity == Pass {
pass_count += 1
} else if finding.severity == Warn {
warn_count += 1
} else {
fail_count += 1
}
}
{
pass_count,
warn_count,
fail_count,
total_count: pass_count + warn_count + fail_count,
}
}
///|
fn suite_verdict(summary : ReportSummary, score : Int) -> String {
if summary.fail_count > 0 {
"risky"
} else if summary.warn_count > 0 || score < 90 {
"monitor"
} else {
"protected"
}
}
///|
pub fn action_items(findings : Array[PolicyFinding]) -> Array[ActionItem] {
let actions = Array::new()
for finding in findings {
if finding.severity != Pass {
actions.push(action_from_finding(finding))
}
}
actions
}
///|
fn action_from_finding(finding : PolicyFinding) -> ActionItem {
{
id: finding.id,
module_name: finding_module_name(finding.id),
title: finding.title,
priority: action_priority(finding),
detail: finding.detail,
next_step: finding.repair,
}
}
///|
fn finding_module_name(id : String) -> String {
let parts = id.split(".").to_array()
if parts.length() == 0 {
"general"
} else {
parts[0].to_owned()
}
}
///|
fn action_priority(finding : PolicyFinding) -> Int {
if finding.severity == Fail {
100 + finding.penalty
} else if finding.severity == Warn {
40 + finding.penalty
} else {
0
}
}
///|
pub fn MailSecuritySuiteReport::has_failures(
self : MailSecuritySuiteReport,
) -> Bool {
self.summary.fail_count > 0
}
///|
pub fn MailSecuritySuiteReport::has_warnings(
self : MailSecuritySuiteReport,
) -> Bool {
self.summary.warn_count > 0
}
///|
pub fn MailSecuritySuiteReport::action_count(
self : MailSecuritySuiteReport,
) -> Int {
self.actions.length()
}
///|
pub fn MailSecuritySuiteReport::coverage_label(
self : MailSecuritySuiteReport,
) -> String {
let mut configured = 0
if self.spf.valid_version {
configured += 1
}
if self.dmarc.valid_version {
configured += 1
}
if self.dkim.is_configured() {
configured += 1
}
if self.mtasts.is_configured() {
configured += 1
}
if self.tlsrpt.is_configured() {
configured += 1
}
if self.bimi.is_configured() {
configured += 1
}
if configured >= 6 {
"complete"
} else if configured >= 4 {
"broad"
} else if configured >= 2 {
"basic"
} else {
"thin"
}
}
///|
pub fn MailSecuritySuiteReport::to_markdown(
self : MailSecuritySuiteReport,
) -> String {
let out = StringBuilder()
out.write_string("# MailShield Suite Report\n\n")
out.write_string("- Domain: " + self.domain + "\n")
out.write_string("- Verdict: " + self.verdict + "\n")
out.write_string("- Score: " + self.score.to_string() + "/100\n")
out.write_string("- Coverage: " + self.coverage_label() + "\n")
out.write_string("- Summary: " + self.summary.to_short_text() + "\n")
out.write_string(
"- Action items: " + self.actions.length().to_string() + "\n\n",
)
out.write_string("## Records\n\n")
out.write_string("- SPF: ")
out.write_string(if self.spf.valid_version { "valid" } else { "invalid" })
out.write_string(", lookups=")
out.write_string(self.spf.dns_lookup_count.to_string())
out.write_string(", all=")
out.write_string(self.spf.all_qualifier)
out.write_string("\n")
out.write_string("- DMARC: ")
out.write_string(self.dmarc.policy)
out.write_string(", pct=")
out.write_string(self.dmarc.pct.to_string())
out.write_string(", rua=")
out.write_string(self.dmarc.rua_count.to_string())
out.write_string("\n")
out.write_string("- DKIM: ")
out.write_string(self.dkim.key_type)
out.write_string(", strength=")
out.write_string(self.dkim.key_strength_label())
out.write_string(", testing=")
out.write_string(bool_text(self.dkim.testing_mode))
out.write_string("\n")
out.write_string("- MTA-STS: ")
out.write_string(self.mtasts.mode)
out.write_string(", max_age=")
out.write_string(self.mtasts.max_age.to_string())
out.write_string(", mx=")
out.write_string(self.mtasts.mx_count.to_string())
out.write_string("\n")
out.write_string("- TLS-RPT: rua=")
out.write_string(self.tlsrpt.rua_count.to_string())
out.write_string(", mailto=")
out.write_string(self.tlsrpt.mailto_count.to_string())
out.write_string(", https=")
out.write_string(self.tlsrpt.https_count.to_string())
out.write_string("\n")
out.write_string("- BIMI: logo=")
out.write_string(
if self.bimi.logo_url.is_empty() {
"missing"
} else {
"present"
},
)
out.write_string(", authority=")
out.write_string(
if self.bimi.has_authority() {
"present"
} else {
"missing"
},
)
out.write_string("\n\n")
out.write_string("## Action Items\n\n")
if self.actions.length() == 0 {
out.write_string("- No action items.\n")
} else {
for action in self.actions {
out.write_string(action.to_markdown())
out.write_string("\n")
}
}
out.write_string("\n## Findings\n\n")
for finding in self.findings {
out.write_string("- [")
out.write_string(severity_text(finding.severity))
out.write_string("] ")
out.write_string(finding.id)
out.write_string(": ")
out.write_string(finding.title)
if !finding.evidence.is_empty() {
out.write_string(" -- ")
out.write_string(finding.evidence)
}
if finding.severity != Pass && !finding.repair.is_empty() {
out.write_string(" | repair: ")
out.write_string(finding.repair)
}
out.write_string("\n")
}
out.to_string()
}
///|
pub fn MailSecuritySuiteReport::to_json(
self : MailSecuritySuiteReport,
) -> String {
let out = StringBuilder()
out.write_string("{")
append_json_string(out, "domain", self.domain)
out.write_string(",")
append_json_string(out, "verdict", self.verdict)
out.write_string(",")
append_json_int(out, "score", self.score)
out.write_string(",")
append_json_string(out, "coverage", self.coverage_label())
out.write_string(",\"summary\":{")
append_json_int(out, "pass", self.summary.pass_count)
out.write_string(",")
append_json_int(out, "warn", self.summary.warn_count)
out.write_string(",")
append_json_int(out, "fail", self.summary.fail_count)
out.write_string(",")
append_json_int(out, "total", self.summary.total_count)
out.write_string("},\"records\":{")
out.write_string("\"spf\":{")
append_json_bool(out, "valid", self.spf.valid_version)
out.write_string(",")
append_json_int(out, "lookups", self.spf.dns_lookup_count)
out.write_string("},\"dmarc\":{")
append_json_string(out, "policy", self.dmarc.policy)
out.write_string(",")
append_json_int(out, "pct", self.dmarc.pct)
out.write_string("},\"dkim\":{")
append_json_string(out, "key_type", self.dkim.key_type)
out.write_string(",")
append_json_string(out, "strength", self.dkim.key_strength_label())
out.write_string("},\"mtasts\":{")
append_json_string(out, "mode", self.mtasts.mode)
out.write_string(",")
append_json_int(out, "max_age", self.mtasts.max_age)
out.write_string("},\"tlsrpt\":{")
append_json_int(out, "rua", self.tlsrpt.rua_count)
out.write_string("},\"bimi\":{")
append_json_bool(out, "configured", self.bimi.is_configured())
out.write_string("}},\"actions\":[")
for i in 0.. 0 {
out.write_string(",")
}
let action = self.actions[i]
out.write_string("{")
append_json_string(out, "id", action.id)
out.write_string(",")
append_json_string(out, "module", action.module_name)
out.write_string(",")
append_json_string(out, "title", action.title)
out.write_string(",")
append_json_int(out, "priority", action.priority)
out.write_string("}")
}
out.write_string("]}")
out.to_string()
}