A DMARC aggregate report groups observations into records. Reading only a source IP or a red status discards the policy, identity and reporting context needed to diagnose the row. Work from the outer report inward and preserve the original XML alongside your interpretation.
Establish the report context
Under feedback, report_metadata identifies the reporting organization, report ID and date range. policy_published describes the policy configuration the receiver observed. RFC 9990 requires one policy domain per report, but that report can contain multiple author domains using the policy. Do not assume that policy_published/domain equals every record's header_from.
Inspect the declared namespace and format. The current RFC 9990 schema and older RFC 7489-era reports differ, so record which validation profile you use. If a document does not match the expected format, quarantine it or explicitly mark partial interpretation rather than silently treating malformed data as a clean report. Parse untrusted XML without resolving arbitrary external entities, and bound compressed input and expansion size.
Follow a record through three layers
The row contains source_ip, count and policy_evaluated. The IP is the connecting system seen by that receiver, not automatically your originating application. The count gives the weight of that combination of observations; counting XML records is not counting messages.
The identifiers section supplies header_from and optional envelope information. auth_results contains raw SPF and DKIM outcomes before interpretation for DMARC. policy_evaluated/dkim and policy_evaluated/spf describe the corresponding DMARC alignment evaluation, while disposition describes handling under policy. Retain override reasons rather than reducing the entire record to a single pass/fail label.
Annotate a synthetic row
Suppose header_from is brand.example, count is 25, the MAIL FROM domain is vendor.example and raw SPF passes. DKIM passes with d=brand.example. The SPF identity is unrelated to the author domain, so it does not provide aligned SPF; the exact-domain DKIM signature does provide aligned DKIM. A policy result of spf=fail and dkim=pass is consistent with those facts. DMARC can pass through DKIM despite the SPF-policy failure.
This example is a field-level teaching fixture, not a complete schema-valid XML document or a measured customer result. It illustrates why adding the connecting IP to brand.example's SPF may be irrelevant: SPF was evaluated for vendor.example, and DMARC already had an aligned DKIM path.
Turn the row into an owned investigation
Correlate the IP and identities with authorized sender records, provider evidence and controlled message tests. Assign an owner before declaring an unknown source malicious or changing DNS. Preserve reporting gaps and time boundaries in the incident note.
A useful annotation includes provenance, period, policy domain, author domain, raw results, aligned results, disposition, count and next action. That structure lets another investigator reproduce your reasoning without depending on a dashboard color.
Retain the field map and expected result
A partial synthetic report map uses reporter Fictional Receiver A, report ID field-map-25 and UTC epoch interval 1704067200–1704153600 (1 January 2024 00:00 to 2 January 2024 00:00 UTC). Its policy domain is brand.example with p=reject, aspf=r and adkim=r. The record has source_ip=192.0.2.10, count=25, header_from=brand.example and envelope_from=vendor.example. Raw SPF passes for vendor.example; raw DKIM passes for brand.example with selector billing1. For this current RFC 9990 reporting-profile example, reported policy results are spf=fail, dkim=pass and disposition=pass. Appendix A defines pass for passing DMARC under an enforcing policy; Appendix B illustrates it.
The field comparison describes 25 observations with a passing aligned DKIM path and no aligned SPF path. Disposition is a supplied receiver field, not computed inbox placement. Metadata establishes provenance and period; policy_published supplies historical configuration; identifiers and auth_results supply the comparison inputs. This fixture is a partial field map, not complete XML, and no schema-valid report or live receiver test is claimed.
Complete a row annotation
- Reporter and interval: preserve the original report ID and UTC start/end times.
- Weight: use count, not the number of XML rows.
- Context: record policy domain, header_from and applicable policy.
- Authentication: record raw SPF domain/result and each DKIM domain/result; compare passing domains for alignment.
- Outcome: keep alignment results, disposition and override reasons in separate fields.
- Next action: name the suspected route, evidence still missing and responsible owner before changing DNS.
Distinguish current and legacy dispositions
RFC 9990 Appendix A defines disposition pass as no action for passing DMARC with an enforcing policy. It retains none as no action taken. Older RFC 7489-format reports lack the pass disposition and can report none for a passing message. Check the namespace, reporting profile and aligned results together: none is not universally invalid, but it should not silently stand in for the current-profile pass example. Neither value proves inbox placement.