Implement BIMI checks
This commit is contained in:
parent
35ff54b2e1
commit
74866d210c
6 changed files with 440 additions and 4 deletions
|
|
@ -104,6 +104,13 @@ func (a *AuthenticationAnalyzer) parseAuthenticationResultsHeader(header string,
|
|||
results.Dmarc = a.parseDMARCResult(part)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse BIMI
|
||||
if strings.HasPrefix(part, "bimi=") {
|
||||
if results.Bimi == nil {
|
||||
results.Bimi = a.parseBIMIResult(part)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -214,6 +221,44 @@ func (a *AuthenticationAnalyzer) parseDMARCResult(part string) *api.AuthResult {
|
|||
return result
|
||||
}
|
||||
|
||||
// parseBIMIResult parses BIMI result from Authentication-Results
|
||||
// Example: bimi=pass header.d=example.com header.selector=default
|
||||
func (a *AuthenticationAnalyzer) parseBIMIResult(part string) *api.AuthResult {
|
||||
result := &api.AuthResult{}
|
||||
|
||||
// Extract result (pass, fail, etc.)
|
||||
re := regexp.MustCompile(`bimi=(\w+)`)
|
||||
if matches := re.FindStringSubmatch(part); len(matches) > 1 {
|
||||
resultStr := strings.ToLower(matches[1])
|
||||
result.Result = api.AuthResultResult(resultStr)
|
||||
}
|
||||
|
||||
// Extract domain (header.d or d)
|
||||
domainRe := regexp.MustCompile(`(?:header\.)?d=([^\s;]+)`)
|
||||
if matches := domainRe.FindStringSubmatch(part); len(matches) > 1 {
|
||||
domain := matches[1]
|
||||
result.Domain = &domain
|
||||
}
|
||||
|
||||
// Extract selector (header.selector or selector)
|
||||
selectorRe := regexp.MustCompile(`(?:header\.)?selector=([^\s;]+)`)
|
||||
if matches := selectorRe.FindStringSubmatch(part); len(matches) > 1 {
|
||||
selector := matches[1]
|
||||
result.Selector = &selector
|
||||
}
|
||||
|
||||
// Extract details
|
||||
if idx := strings.Index(part, "("); idx != -1 {
|
||||
endIdx := strings.Index(part[idx:], ")")
|
||||
if endIdx != -1 {
|
||||
details := strings.TrimSpace(part[idx+1 : idx+endIdx])
|
||||
result.Details = &details
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// parseLegacySPF attempts to parse SPF from Received-SPF header
|
||||
func (a *AuthenticationAnalyzer) parseLegacySPF(email *EmailMessage) *api.AuthResult {
|
||||
receivedSPF := email.Header.Get("Received-SPF")
|
||||
|
|
@ -383,6 +428,12 @@ func (a *AuthenticationAnalyzer) GenerateAuthenticationChecks(results *api.Authe
|
|||
})
|
||||
}
|
||||
|
||||
// BIMI check (optional, informational only)
|
||||
if results.Bimi != nil {
|
||||
check := a.generateBIMICheck(results.Bimi)
|
||||
checks = append(checks, check)
|
||||
}
|
||||
|
||||
return checks
|
||||
}
|
||||
|
||||
|
|
@ -509,3 +560,38 @@ func (a *AuthenticationAnalyzer) generateDMARCCheck(dmarc *api.AuthResult) api.C
|
|||
|
||||
return check
|
||||
}
|
||||
|
||||
func (a *AuthenticationAnalyzer) generateBIMICheck(bimi *api.AuthResult) api.Check {
|
||||
check := api.Check{
|
||||
Category: api.Authentication,
|
||||
Name: "BIMI (Brand Indicators)",
|
||||
}
|
||||
|
||||
switch bimi.Result {
|
||||
case api.AuthResultResultPass:
|
||||
check.Status = api.CheckStatusPass
|
||||
check.Score = 0.0 // BIMI doesn't contribute to score (branding feature)
|
||||
check.Message = "BIMI validation passed"
|
||||
check.Severity = api.PtrTo(api.Info)
|
||||
check.Advice = api.PtrTo("Your brand logo is properly configured via BIMI")
|
||||
case api.AuthResultResultFail:
|
||||
check.Status = api.CheckStatusInfo
|
||||
check.Score = 0.0
|
||||
check.Message = "BIMI validation failed"
|
||||
check.Severity = api.PtrTo(api.Low)
|
||||
check.Advice = api.PtrTo("BIMI is optional but can improve brand recognition. Ensure DMARC is enforced (p=quarantine or p=reject) and configure a valid BIMI record")
|
||||
default:
|
||||
check.Status = api.CheckStatusInfo
|
||||
check.Score = 0.0
|
||||
check.Message = fmt.Sprintf("BIMI validation result: %s", bimi.Result)
|
||||
check.Severity = api.PtrTo(api.Low)
|
||||
check.Advice = api.PtrTo("BIMI is optional. Consider implementing it to display your brand logo in supported email clients")
|
||||
}
|
||||
|
||||
if bimi.Domain != nil {
|
||||
details := fmt.Sprintf("Domain: %s", *bimi.Domain)
|
||||
check.Details = &details
|
||||
}
|
||||
|
||||
return check
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue