dmarc: implement DMARCbis DNS Tree Walk and new tag support
Replace RFC 7489 PSL-based org-domain lookup and RFC 9091 PSD DMARC fallback with the DMARCbis DNS Tree Walk algorithm (max 8 queries, 8-label shortcut, TLD records require psd=y). Add parsing for the new t= (test mode), psd= (y/n/u), and deprecated tag detection (pct, rf, ri). Update validateDMARC to accept p=-absent records with rua= per DMARCbis §4.7. Score t=y by downgrading effective policy one level. Surface user-facing advisories in DmarcRecordDisplay: deprecation warnings for pct=/rf=/ri=, test mode explanation with per-policy impact, and PSD/org-domain boundary notices.
This commit is contained in:
parent
1b8627ef86
commit
809bca02e4
4 changed files with 482 additions and 159 deletions
|
|
@ -11,6 +11,7 @@
|
|||
const isFallback = $derived(
|
||||
!!dmarcRecord?.domain && !!fromDomain && dmarcRecord.domain !== fromDomain,
|
||||
);
|
||||
// A single-label domain (no dot) is a TLD/PSD level fallback
|
||||
const isPsdFallback = $derived(isFallback && !dmarcRecord?.domain?.includes("."));
|
||||
|
||||
// Helper function to determine policy strength
|
||||
|
|
@ -18,6 +19,15 @@
|
|||
const strength: Record<string, number> = { none: 0, quarantine: 1, reject: 2 };
|
||||
return strength[policy || "none"] || 0;
|
||||
};
|
||||
|
||||
// Effective policy after applying DMARCbis t=y downgrade
|
||||
const effectivePolicy = $derived((): string => {
|
||||
const p = dmarcRecord?.policy ?? "none";
|
||||
if (!dmarcRecord?.test_mode) return p;
|
||||
if (p === "reject") return "quarantine";
|
||||
if (p === "quarantine") return "none";
|
||||
return p;
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if dmarcRecord}
|
||||
|
|
@ -68,9 +78,12 @@
|
|||
No DMARC record exists for <code>{fromDomain}</code>. The record above was
|
||||
inherited from
|
||||
{#if isPsdFallback}
|
||||
the Public Suffix Domain <code>{dmarcRecord.domain}</code> per RFC 9091.
|
||||
the Public Suffix Domain <code>{dmarcRecord.domain}</code> via the DMARCbis
|
||||
DNS Tree Walk (which obsoletes the RFC 9091 PSD DMARC experiment).
|
||||
{:else}
|
||||
the organizational domain <code>{dmarcRecord.domain}</code> per RFC 7489.
|
||||
the organizational domain <code>{dmarcRecord.domain}</code> via the
|
||||
DMARCbis DNS Tree Walk (compatible with RFC 7489 organizational domain
|
||||
fallback).
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -123,6 +136,53 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Test Mode (DMARCbis t= tag) -->
|
||||
{#if dmarcRecord.test_mode}
|
||||
<div class="mb-3">
|
||||
<strong>Test Mode:</strong>
|
||||
<span class="badge bg-warning">t=y (active)</span>
|
||||
<div class="alert alert-warning mt-2 mb-0 small">
|
||||
<i class="bi bi-flask me-1"></i>
|
||||
<strong>Test mode active</strong> — DMARCbis-compliant receivers will
|
||||
downgrade the effective policy one level:
|
||||
{#if dmarcRecord.policy === "reject"}
|
||||
<code>p=reject</code> is applied as <code>p=quarantine</code>.
|
||||
{:else if dmarcRecord.policy === "quarantine"}
|
||||
<code>p=quarantine</code> is applied as <code>p=none</code> (no action taken).
|
||||
{:else}
|
||||
<code>p=none</code> is unaffected by test mode.
|
||||
{/if}
|
||||
Aggregate reports are still generated normally.
|
||||
This tag replaces the deprecated <code>pct=</code> for gradual rollout.
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- PSD tag (DMARCbis psd=) -->
|
||||
{#if dmarcRecord.psd === "y"}
|
||||
<div class="mb-3">
|
||||
<strong>Public Suffix Domain:</strong>
|
||||
<span class="badge bg-info">psd=y</span>
|
||||
<div class="alert alert-info mt-2 mb-0 small">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
<strong>PSD declared</strong> — this domain is declared as a Public Suffix
|
||||
Domain. DMARCbis-compliant receivers will apply this policy to subdomains
|
||||
that have no DMARC record of their own when using the DNS Tree Walk algorithm.
|
||||
</div>
|
||||
</div>
|
||||
{:else if dmarcRecord.psd === "n"}
|
||||
<div class="mb-3">
|
||||
<strong>Organizational Domain Boundary:</strong>
|
||||
<span class="badge bg-info">psd=n</span>
|
||||
<div class="alert alert-info mt-2 mb-0 small">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
<strong>Org Domain declared</strong> — <code>psd=n</code> explicitly declares
|
||||
this as an Organizational Domain boundary. Subdomains with separate DNS
|
||||
delegation will use their own independent DMARCbis Tree Walk.
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Subdomain Policy -->
|
||||
{#if dmarcRecord.subdomain_policy}
|
||||
{@const mainStrength = policyStrength(dmarcRecord.policy)}
|
||||
|
|
@ -202,7 +262,7 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Percentage -->
|
||||
<!-- Percentage (pct=, deprecated in DMARCbis) -->
|
||||
{#if dmarcRecord.percentage !== undefined}
|
||||
<div class="mb-3">
|
||||
<strong>Enforcement Percentage:</strong>
|
||||
|
|
@ -215,25 +275,35 @@
|
|||
>
|
||||
{dmarcRecord.percentage}%
|
||||
</span>
|
||||
<div class="alert alert-warning mt-2 mb-0 small">
|
||||
<i class="bi bi-exclamation-triangle me-1"></i>
|
||||
<strong>Deprecated tag</strong> — the <code>pct=</code> tag is removed in
|
||||
DMARCbis. Many receivers already ignore it. For gradual rollout, replace it
|
||||
with <code>t=y</code> (test mode); for full enforcement, simply remove
|
||||
<code>pct=</code> from your record.
|
||||
{#if dmarcRecord.percentage === 0}
|
||||
<br /><strong>pct=0 is an anti-pattern</strong> — it was widely misused
|
||||
as a signal to bypass DMARC entirely, which is one reason the tag was
|
||||
removed. Use <code>t=y</code> instead.
|
||||
{/if}
|
||||
</div>
|
||||
{#if dmarcRecord.percentage === 100}
|
||||
<div class="alert alert-success mt-2 mb-0 small">
|
||||
<i class="bi bi-check-circle me-1"></i>
|
||||
<strong>Full enforcement</strong> — all messages are subject to DMARC policy.
|
||||
This provides maximum protection.
|
||||
</div>
|
||||
{:else if dmarcRecord.percentage >= 50}
|
||||
{:else if dmarcRecord.percentage > 0 && dmarcRecord.percentage >= 50}
|
||||
<div class="alert alert-warning mt-2 mb-0 small">
|
||||
<i class="bi bi-exclamation-triangle me-1"></i>
|
||||
<strong>Partial enforcement</strong> — only {dmarcRecord.percentage}% of
|
||||
messages are subject to DMARC policy. Consider increasing to
|
||||
<code>pct=100</code> once you've validated your configuration.
|
||||
messages are subject to DMARC policy. Receivers ignoring pct= will apply
|
||||
the full policy regardless.
|
||||
</div>
|
||||
{:else}
|
||||
{:else if dmarcRecord.percentage > 0}
|
||||
<div class="alert alert-danger mt-2 mb-0 small">
|
||||
<i class="bi bi-x-circle me-1"></i>
|
||||
<strong>Low enforcement</strong> — only {dmarcRecord.percentage}% of
|
||||
messages are protected. Gradually increase to <code>pct=100</code> for full
|
||||
protection.
|
||||
messages are protected. Receivers ignoring pct= will apply full policy.
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
@ -319,6 +389,30 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Deprecated rf=/ri= tags -->
|
||||
{#if dmarcRecord.deprecated_rf || dmarcRecord.deprecated_ri}
|
||||
<div class="alert alert-warning mt-2 mb-3 small">
|
||||
<i class="bi bi-exclamation-triangle me-1"></i>
|
||||
<strong>Deprecated tags detected</strong> — your record contains
|
||||
{#if dmarcRecord.deprecated_rf && dmarcRecord.deprecated_ri}
|
||||
<code>rf=</code> and <code>ri=</code> tags that are
|
||||
{:else if dmarcRecord.deprecated_rf}
|
||||
the <code>rf=</code> tag that is
|
||||
{:else}
|
||||
the <code>ri=</code> tag that is
|
||||
{/if}
|
||||
removed in DMARCbis. Modern receivers will ignore
|
||||
{dmarcRecord.deprecated_rf && dmarcRecord.deprecated_ri ? "them" : "it"}.
|
||||
{#if dmarcRecord.deprecated_ri}
|
||||
Aggregate reporting interval is now fixed at ≥ 24 hours regardless of
|
||||
<code>ri=</code>.
|
||||
{/if}
|
||||
You can safely remove
|
||||
{dmarcRecord.deprecated_rf && dmarcRecord.deprecated_ri ? "these tags" : "this tag"}
|
||||
from your DMARC record.
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Error -->
|
||||
{#if dmarcRecord.error}
|
||||
<div class="text-danger">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue