Initial Commit
This commit is contained in:
89
packages/communication/emails/dmarc/src/parsers/forensic.md
Normal file
89
packages/communication/emails/dmarc/src/parsers/forensic.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# forensic
|
||||
|
||||
## Purpose
|
||||
|
||||
Implements `parseDmarcForensic`, the entry point for parsing DMARC forensic (RUF) reports. Forensic reports notify domain owners of individual authentication failures; they share the `<feedback>` / `<report_metadata>` / `<policy_published>` structure with aggregate reports but omit the `<record>` block and may include a raw MIME-formatted `auth_failure_report` element. This module provides the same auto-detect-decompress → parse → map → validate pipeline as the aggregate parser but targets the forensic schema.
|
||||
|
||||
## Features
|
||||
|
||||
- Auto-detects gzip input via magic-byte probe
|
||||
- Accepts raw XML strings or `Uint8Array` binary blobs
|
||||
- Maps snake_case XML fields to camelCase typed fields
|
||||
- Surfaces `authFailureReport` string when present in the source XML
|
||||
- Validates against `DmarcForensicReportSchema` (Zod)
|
||||
- Emits OTel counter and histogram for both success and error paths
|
||||
- Wraps all failures in typed `DmarcError` with stable codes
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { parseDmarcForensic } from "@suaveplan/dmarc/parsers/forensic";
|
||||
|
||||
const report = parseDmarcForensic(`
|
||||
<feedback>
|
||||
<version>1.0</version>
|
||||
<report_metadata>
|
||||
<org_name>Example Corp</org_name>
|
||||
<email>dmarc@example.com</email>
|
||||
<report_id>ruf-001</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><pct>100</pct>
|
||||
</policy_published>
|
||||
</feedback>
|
||||
`);
|
||||
|
||||
console.log(report.feedback.reportMetadata.orgName); // "Example Corp"
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { parseDmarcForensic } from "@suaveplan/dmarc/parsers/forensic";
|
||||
import { DmarcError, DMARC_CODES } from "@suaveplan/dmarc/errors";
|
||||
import { gzipSync, strToU8 } from "fflate";
|
||||
|
||||
const compressed = gzipSync(strToU8(xmlString));
|
||||
const report = parseDmarcForensic(compressed);
|
||||
|
||||
// Access optional auth failure report body
|
||||
if (report.authFailureReport) {
|
||||
console.log("Failure report MIME body:", report.authFailureReport);
|
||||
}
|
||||
|
||||
// Discriminated error handling
|
||||
try {
|
||||
parseDmarcForensic("<bad>xml</bad>");
|
||||
} catch (err) {
|
||||
if (err instanceof DmarcError && err.code === DMARC_CODES.INVALID_REPORT) {
|
||||
// Schema validation failed — missing required elements
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `parseDmarcForensic(input, options?): DmarcForensicReport`
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `input: string | Uint8Array` — raw XML string or binary data
|
||||
- `options.decompress?: boolean` — force gzip decompression
|
||||
|
||||
**Returns:** `DmarcForensicReport` — validated forensic report
|
||||
|
||||
**Throws:**
|
||||
|
||||
- `DmarcError(DMARC.PARSE_ERROR)` — XML parse failure
|
||||
- `DmarcError(DMARC.INVALID_REPORT)` — schema validation failure or missing required elements
|
||||
- `DmarcError(DMARC.UNSUPPORTED_ENCODING)` — gzip decompression failure
|
||||
|
||||
### `ParseDmarcForensicOptions`
|
||||
|
||||
`{ decompress?: boolean }`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The forensic parser is structurally identical to the aggregate parser except for the output schema. Both share the same `str`/`num` coercion helpers and the same two-phase (map → validate) approach. In practice, RFC 7489 forensic reports are far less common than aggregate reports and are often transmitted as raw MIME messages rather than standalone XML files; the `authFailureReport` field is therefore optional and surfaced as a plain string for callers to process further if needed.
|
||||
Reference in New Issue
Block a user