Handle relaxed domain match
This commit is contained in:
parent
84a504d668
commit
3d03bfc4fa
5 changed files with 137 additions and 35 deletions
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type { DNSResults, ReceivedHop } from "$lib/api/types.gen";
|
||||
import type { DomainAlignment, DNSResults, ReceivedHop } from "$lib/api/types.gen";
|
||||
import { getScoreColorClass } from "$lib/score";
|
||||
import GradeDisplay from "./GradeDisplay.svelte";
|
||||
import MxRecordsDisplay from "./MxRecordsDisplay.svelte";
|
||||
|
|
@ -11,13 +11,14 @@
|
|||
import PtrForwardRecordsDisplay from "./PtrForwardRecordsDisplay.svelte";
|
||||
|
||||
interface Props {
|
||||
domainAlignment?: DomainAlignment;
|
||||
dnsResults?: DNSResults;
|
||||
dnsGrade?: string;
|
||||
dnsScore?: number;
|
||||
receivedChain?: ReceivedHop[];
|
||||
}
|
||||
|
||||
let { dnsResults, dnsGrade, dnsScore, receivedChain }: Props = $props();
|
||||
let { domainAlignment, dnsResults, dnsGrade, dnsScore, receivedChain }: Props = $props();
|
||||
|
||||
// Extract sender IP from first hop
|
||||
const senderIp = $derived(
|
||||
|
|
@ -81,19 +82,21 @@
|
|||
<hr class="my-4" />
|
||||
|
||||
<!-- Return-Path Domain Section -->
|
||||
<div class="mb-3 d-flex align-items-center gap-2">
|
||||
<h4 class="mb-0">
|
||||
Return-Path Domain: <code>{dnsResults.rp_domain || dnsResults.from_domain}</code>
|
||||
</h4>
|
||||
{#if dnsResults.rp_domain && dnsResults.rp_domain !== dnsResults.from_domain}
|
||||
<span class="badge bg-danger ms-2"><i class="bi bi-exclamation-triangle-fill"></i> Different from From domain</span>
|
||||
<small>
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
<a href="#domain-alignment">See domain alignment</a>
|
||||
</small>
|
||||
{:else}
|
||||
<span class="badge bg-success ms-2">Same as From domain</span>
|
||||
{/if}
|
||||
<div class="mb-3">
|
||||
<div class="d-flex align-items-center gap-2 flex-wrap">
|
||||
<h4 class="mb-0">
|
||||
Return-Path Domain: <code>{dnsResults.rp_domain || dnsResults.from_domain}</code>
|
||||
</h4>
|
||||
{#if (domainAlignment && !domainAlignment.aligned && !domainAlignment.relaxed_aligned) || (domainAlignment && !domainAlignment.aligned && domainAlignment.relaxed_aligned && dnsResults.dmarc_record && dnsResults.dmarc_record.spf_alignment === "strict") || (!domainAlignment && dnsResults.rp_domain && dnsResults.rp_domain !== dnsResults.from_domain)}
|
||||
<span class="badge bg-danger ms-2"><i class="bi bi-exclamation-triangle-fill"></i> Differs from From domain</span>
|
||||
<small>
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
<a href="#domain-alignment">See domain alignment</a>
|
||||
</small>
|
||||
{:else}
|
||||
<span class="badge bg-success ms-2">Same as From domain</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MX Records for Return-Path Domain -->
|
||||
|
|
@ -117,7 +120,7 @@
|
|||
From Domain: <code>{dnsResults.from_domain}</code>
|
||||
</h4>
|
||||
{#if dnsResults.rp_domain && dnsResults.rp_domain !== dnsResults.from_domain}
|
||||
<span class="badge bg-danger ms-2"><i class="bi bi-exclamation-triangle-fill"></i> Different from Return-Path domain</span>
|
||||
<span class="badge bg-danger ms-2"><i class="bi bi-exclamation-triangle-fill"></i> Differs from Return-Path domain</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
<script lang="ts">
|
||||
import type { HeaderAnalysis } from "$lib/api/types.gen";
|
||||
import type { DMARCRecord, HeaderAnalysis } from "$lib/api/types.gen";
|
||||
import { getScoreColorClass } from "$lib/score";
|
||||
import GradeDisplay from "./GradeDisplay.svelte";
|
||||
|
||||
interface Props {
|
||||
dmarcRecord: DMARCRecord;
|
||||
headerAnalysis: HeaderAnalysis;
|
||||
headerGrade?: string;
|
||||
headerScore?: number;
|
||||
}
|
||||
|
||||
let { headerAnalysis, headerGrade, headerScore }: Props = $props();
|
||||
let { dmarcRecord, headerAnalysis, headerGrade, headerScore }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
|
|
@ -59,7 +60,7 @@
|
|||
<div class="card mb-3" id="domain-alignment">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi {headerAnalysis.domain_alignment.aligned ? 'bi-check-circle-fill text-success' : 'bi-x-circle-fill text-danger'}"></i>
|
||||
<i class="bi {headerAnalysis.domain_alignment.aligned ? 'bi-check-circle-fill text-success' : headerAnalysis.domain_alignment.relaxed_aligned ? 'bi-check-circle text-info' : 'bi-x-circle-fill text-danger'}"></i>
|
||||
Domain Alignment
|
||||
</h5>
|
||||
</div>
|
||||
|
|
@ -68,34 +69,64 @@
|
|||
Domain alignment ensures that the visible "From" domain matches the domain used for authentication (Return-Path). Proper alignment is crucial for DMARC compliance and helps prevent email spoofing by verifying that the sender domain is consistent across all authentication layers.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<small class="text-muted">Aligned</small>
|
||||
<div class="col-md-3">
|
||||
<small class="text-muted">Strict Alignment</small>
|
||||
<div>
|
||||
<span class="badge" class:bg-success={headerAnalysis.domain_alignment.aligned} class:bg-danger={!headerAnalysis.domain_alignment.aligned}>
|
||||
<i class="bi {headerAnalysis.domain_alignment.aligned ? 'bi-check-circle-fill' : 'bi-x-circle-fill'} me-1"></i>
|
||||
<strong>{headerAnalysis.domain_alignment.aligned ? 'Yes' : 'No'}</strong>
|
||||
<strong>{headerAnalysis.domain_alignment.aligned ? 'Pass' : 'Fail'}</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div class="small text-muted mt-1">Exact domain match</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="col-md-3">
|
||||
<small class="text-muted">Relaxed Alignment</small>
|
||||
<div>
|
||||
<span class="badge" class:bg-success={headerAnalysis.domain_alignment.relaxed_aligned} class:bg-danger={!headerAnalysis.domain_alignment.relaxed_aligned}>
|
||||
<i class="bi {headerAnalysis.domain_alignment.relaxed_aligned ? 'bi-check-circle-fill' : 'bi-x-circle-fill'} me-1"></i>
|
||||
<strong>{headerAnalysis.domain_alignment.relaxed_aligned ? 'Pass' : 'Fail'}</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div class="small text-muted mt-1">Organizational domain match</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<small class="text-muted">From Domain</small>
|
||||
<div><code>{headerAnalysis.domain_alignment.from_domain || '-'}</code></div>
|
||||
{#if headerAnalysis.domain_alignment.from_org_domain && headerAnalysis.domain_alignment.from_org_domain !== headerAnalysis.domain_alignment.from_domain}
|
||||
<div class="small text-muted mt-1">Org: <code>{headerAnalysis.domain_alignment.from_org_domain}</code></div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="col-md-3">
|
||||
<small class="text-muted">Return-Path Domain</small>
|
||||
<div><code>{headerAnalysis.domain_alignment.return_path_domain || '-'}</code></div>
|
||||
{#if headerAnalysis.domain_alignment.return_path_org_domain && headerAnalysis.domain_alignment.return_path_org_domain !== headerAnalysis.domain_alignment.return_path_domain}
|
||||
<div class="small text-muted mt-1">Org: <code>{headerAnalysis.domain_alignment.return_path_org_domain}</code></div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if headerAnalysis.domain_alignment.issues && headerAnalysis.domain_alignment.issues.length > 0}
|
||||
<div class="mt-2">
|
||||
<div class="mt-3">
|
||||
{#each headerAnalysis.domain_alignment.issues as issue}
|
||||
<div class="text-warning small">
|
||||
<i class="bi bi-exclamation-triangle me-1"></i>
|
||||
<div class="alert alert-{headerAnalysis.domain_alignment.relaxed_aligned ? 'info' : 'warning'} py-2 small mb-2">
|
||||
<i class="bi bi-{headerAnalysis.domain_alignment.relaxed_aligned ? 'info-circle' : 'exclamation-triangle'} me-1"></i>
|
||||
{issue}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Alignment Information based on DMARC policy -->
|
||||
{#if dmarcRecord && headerAnalysis.domain_alignment.return_path_domain && headerAnalysis.domain_alignment.return_path_domain !== headerAnalysis.domain_alignment.from_domain}
|
||||
<div class="alert mt-2 mb-0 small py-2 {dmarcRecord.spf_alignment === 'strict' ? 'alert-warning' : 'alert-info'}">
|
||||
{#if dmarcRecord.spf_alignment === 'strict'}
|
||||
<i class="bi bi-exclamation-triangle me-1"></i>
|
||||
<strong>Strict SPF alignment required</strong> — Your DMARC policy requires exact domain match. The Return-Path domain must exactly match the From domain for SPF to pass DMARC alignment.
|
||||
{:else}
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
<strong>Relaxed SPF alignment allowed</strong> — Your DMARC policy allows organizational domain matching. As long as both domains share the same organizational domain (e.g., mail.example.com and example.com), SPF alignment can pass.
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@
|
|||
<div class="row mb-4" id="dns">
|
||||
<div class="col-12">
|
||||
<DnsRecordsCard
|
||||
domainAlignment={report.header_analysis?.domain_alignment}
|
||||
dnsResults={report.dns_results}
|
||||
dnsGrade={report.summary?.dns_grade}
|
||||
dnsScore={report.summary?.dns_score}
|
||||
|
|
@ -185,6 +186,7 @@
|
|||
<div class="row mb-4" id="header">
|
||||
<div class="col-12">
|
||||
<HeaderAnalysisCard
|
||||
dmarcRecord={report.dns_results.dmarc_record}
|
||||
headerAnalysis={report.header_analysis}
|
||||
headerGrade={report.summary?.header_grade}
|
||||
headerScore={report.summary?.header_score}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue