diff --git a/api/openapi.yaml b/api/openapi.yaml index c0c3c70..5c628fd 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -350,19 +350,6 @@ components: listed: false - rbl: "bl.spamcop.net" listed: false - whitelists: - type: object - additionalProperties: - type: array - items: - $ref: '#/components/schemas/BlacklistCheck' - description: Map of IP addresses to their DNS whitelist check results (informational only) - example: - "192.0.2.1": - - rbl: "list.dnswl.org" - listed: false - - rbl: "swl.spamhaus.org" - listed: false content_analysis: $ref: '#/components/schemas/ContentAnalysis' header_analysis: @@ -789,7 +776,7 @@ components: properties: result: type: string - enum: [pass, fail, invalid, missing, none, neutral, softfail, temperror, permerror, declined, domain_pass, orgdomain_pass, skipped] + enum: [pass, fail, invalid, missing, none, neutral, softfail, temperror, permerror, declined, domain_pass, orgdomain_pass] description: Authentication result example: "pass" domain: diff --git a/internal/config/config.go b/internal/config/config.go index 468a2aa..4a335c9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -65,7 +65,6 @@ type AnalysisConfig struct { DNSTimeout time.Duration HTTPTimeout time.Duration RBLs []string - DNSWLs []string CheckAllIPs bool // Check all IPs found in headers, not just the first one } @@ -89,7 +88,6 @@ func DefaultConfig() *Config { DNSTimeout: 5 * time.Second, HTTPTimeout: 10 * time.Second, RBLs: []string{}, - DNSWLs: []string{}, CheckAllIPs: false, // By default, only check the first IP }, } diff --git a/pkg/analyzer/analyzer.go b/pkg/analyzer/analyzer.go index 83eafe6..e7ae561 100644 --- a/pkg/analyzer/analyzer.go +++ b/pkg/analyzer/analyzer.go @@ -44,7 +44,6 @@ func NewEmailAnalyzer(cfg *config.Config) *EmailAnalyzer { cfg.Analysis.DNSTimeout, cfg.Analysis.HTTPTimeout, cfg.Analysis.RBLs, - cfg.Analysis.DNSWLs, cfg.Analysis.CheckAllIPs, ) @@ -131,12 +130,12 @@ func (a *APIAdapter) CheckBlacklistIP(ip string) ([]api.BlacklistCheck, int, int // Calculate score using the existing function // Create a minimal RBLResults structure for scoring - results := &DNSListResults{ + results := &RBLResults{ Checks: map[string][]api.BlacklistCheck{ip: checks}, IPsChecked: []string{ip}, ListedCount: listedCount, } - score, grade := a.analyzer.generator.rblChecker.CalculateScore(results) + score, grade := a.analyzer.generator.rblChecker.CalculateRBLScore(results) return checks, listedCount, score, grade, nil } diff --git a/pkg/analyzer/rbl.go b/pkg/analyzer/rbl.go index 44c6e99..5fcb939 100644 --- a/pkg/analyzer/rbl.go +++ b/pkg/analyzer/rbl.go @@ -32,15 +32,12 @@ import ( "git.happydns.org/happyDeliver/internal/api" ) -// DNSListChecker checks IP addresses against DNS-based block/allow lists. -// It handles both RBL (blacklist) and DNSWL (whitelist) semantics via flags. -type DNSListChecker struct { - Timeout time.Duration - Lists []string - CheckAllIPs bool // Check all IPs found in headers, not just the first one - filterErrorCodes bool // When true (RBL mode), treat 127.255.255.253/254/255 as operational errors - resolver *net.Resolver - informationalSet map[string]bool // Lists whose hits don't count toward the score +// RBLChecker checks IP addresses against DNS-based blacklists +type RBLChecker struct { + Timeout time.Duration + RBLs []string + CheckAllIPs bool // Check all IPs found in headers, not just the first one + resolver *net.Resolver } // DefaultRBLs is a list of commonly used RBL providers @@ -51,85 +48,40 @@ var DefaultRBLs = []string{ "b.barracudacentral.org", // Barracuda "cbl.abuseat.org", // CBL (Composite Blocking List) "dnsbl-1.uceprotect.net", // UCEPROTECT Level 1 - "dnsbl-2.uceprotect.net", // UCEPROTECT Level 2 (informational) - "dnsbl-3.uceprotect.net", // UCEPROTECT Level 3 (informational) - "spam.spamrats.com", // SpamRats SPAM - "dyna.spamrats.com", // SpamRats dynamic IPs - "psbl.surriel.com", // PSBL - "dnsbl.dronebl.org", // DroneBL - "bl.mailspike.net", // Mailspike BL - "z.mailspike.net", // Mailspike Z - "bl.rbl-dns.com", // RBL-DNS - "bl.nszones.com", // NSZones -} - -// DefaultInformationalRBLs lists RBLs that are checked but not counted in the score. -// These are typically broader lists where being listed is less definitive. -var DefaultInformationalRBLs = []string{ - "dnsbl-2.uceprotect.net", // UCEPROTECT Level 2: entire netblocks, may cause false positives - "dnsbl-3.uceprotect.net", // UCEPROTECT Level 3: entire ASes, too broad for scoring -} - -// DefaultDNSWLs is a list of commonly used DNSWL providers -var DefaultDNSWLs = []string{ - "list.dnswl.org", // DNSWL.org — the main DNS whitelist - "swl.spamhaus.org", // Spamhaus Safe Whitelist } // NewRBLChecker creates a new RBL checker with configurable timeout and RBL list -func NewRBLChecker(timeout time.Duration, rbls []string, checkAllIPs bool) *DNSListChecker { +func NewRBLChecker(timeout time.Duration, rbls []string, checkAllIPs bool) *RBLChecker { if timeout == 0 { - timeout = 5 * time.Second + timeout = 5 * time.Second // Default timeout } if len(rbls) == 0 { rbls = DefaultRBLs } - informationalSet := make(map[string]bool, len(DefaultInformationalRBLs)) - for _, rbl := range DefaultInformationalRBLs { - informationalSet[rbl] = true - } - return &DNSListChecker{ - Timeout: timeout, - Lists: rbls, - CheckAllIPs: checkAllIPs, - filterErrorCodes: true, - resolver: &net.Resolver{PreferGo: true}, - informationalSet: informationalSet, + return &RBLChecker{ + Timeout: timeout, + RBLs: rbls, + CheckAllIPs: checkAllIPs, + resolver: &net.Resolver{ + PreferGo: true, + }, } } -// NewDNSWLChecker creates a new DNSWL checker with configurable timeout and DNSWL list -func NewDNSWLChecker(timeout time.Duration, dnswls []string, checkAllIPs bool) *DNSListChecker { - if timeout == 0 { - timeout = 5 * time.Second - } - if len(dnswls) == 0 { - dnswls = DefaultDNSWLs - } - return &DNSListChecker{ - Timeout: timeout, - Lists: dnswls, - CheckAllIPs: checkAllIPs, - filterErrorCodes: false, - resolver: &net.Resolver{PreferGo: true}, - informationalSet: make(map[string]bool), - } +// RBLResults represents the results of RBL checks +type RBLResults struct { + Checks map[string][]api.BlacklistCheck // Map of IP -> list of RBL checks for that IP + IPsChecked []string + ListedCount int } -// DNSListResults represents the results of DNS list checks -type DNSListResults struct { - Checks map[string][]api.BlacklistCheck // Map of IP -> list of checks for that IP - IPsChecked []string - ListedCount int // Total listings including informational entries - RelevantListedCount int // Listings on scoring (non-informational) lists only -} - -// CheckEmail checks all IPs found in the email headers against the configured lists -func (r *DNSListChecker) CheckEmail(email *EmailMessage) *DNSListResults { - results := &DNSListResults{ +// CheckEmail checks all IPs found in the email headers against RBLs +func (r *RBLChecker) CheckEmail(email *EmailMessage) *RBLResults { + results := &RBLResults{ Checks: make(map[string][]api.BlacklistCheck), } + // Extract IPs from Received headers ips := r.extractIPs(email) if len(ips) == 0 { return results @@ -137,18 +89,17 @@ func (r *DNSListChecker) CheckEmail(email *EmailMessage) *DNSListResults { results.IPsChecked = ips + // Check each IP against all RBLs for _, ip := range ips { - for _, list := range r.Lists { - check := r.checkIP(ip, list) + for _, rbl := range r.RBLs { + check := r.checkIP(ip, rbl) results.Checks[ip] = append(results.Checks[ip], check) if check.Listed { results.ListedCount++ - if !r.informationalSet[list] { - results.RelevantListedCount++ - } } } + // Only check the first IP unless CheckAllIPs is enabled if !r.CheckAllIPs { break } @@ -157,8 +108,9 @@ func (r *DNSListChecker) CheckEmail(email *EmailMessage) *DNSListResults { return results } -// CheckIP checks a single IP address against all configured lists -func (r *DNSListChecker) CheckIP(ip string) ([]api.BlacklistCheck, int, error) { +// CheckIP checks a single IP address against all configured RBLs +func (r *RBLChecker) CheckIP(ip string) ([]api.BlacklistCheck, int, error) { + // Validate that it's a valid IP address if !r.isPublicIP(ip) { return nil, 0, fmt.Errorf("invalid or non-public IP address: %s", ip) } @@ -166,8 +118,9 @@ func (r *DNSListChecker) CheckIP(ip string) ([]api.BlacklistCheck, int, error) { var checks []api.BlacklistCheck listedCount := 0 - for _, list := range r.Lists { - check := r.checkIP(ip, list) + // Check the IP against all RBLs + for _, rbl := range r.RBLs { + check := r.checkIP(ip, rbl) checks = append(checks, check) if check.Listed { listedCount++ @@ -178,19 +131,27 @@ func (r *DNSListChecker) CheckIP(ip string) ([]api.BlacklistCheck, int, error) { } // extractIPs extracts IP addresses from Received headers -func (r *DNSListChecker) extractIPs(email *EmailMessage) []string { +func (r *RBLChecker) extractIPs(email *EmailMessage) []string { var ips []string seenIPs := make(map[string]bool) + // Get all Received headers receivedHeaders := email.Header["Received"] + + // Regex patterns for IP addresses + // Match IPv4: xxx.xxx.xxx.xxx ipv4Pattern := regexp.MustCompile(`\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b`) + // Look for IPs in Received headers for _, received := range receivedHeaders { + // Find all IPv4 addresses matches := ipv4Pattern.FindAllString(received, -1) for _, match := range matches { + // Skip private/reserved IPs if !r.isPublicIP(match) { continue } + // Avoid duplicates if !seenIPs[match] { ips = append(ips, match) seenIPs[match] = true @@ -198,10 +159,13 @@ func (r *DNSListChecker) extractIPs(email *EmailMessage) []string { } } + // If no IPs found in Received headers, try X-Originating-IP if len(ips) == 0 { originatingIP := email.Header.Get("X-Originating-IP") if originatingIP != "" { + // Extract IP from formats like "[192.0.2.1]" or "192.0.2.1" cleanIP := strings.TrimSuffix(strings.TrimPrefix(originatingIP, "["), "]") + // Remove any whitespace cleanIP = strings.TrimSpace(cleanIP) matches := ipv4Pattern.FindString(cleanIP) if matches != "" && r.isPublicIP(matches) { @@ -214,16 +178,19 @@ func (r *DNSListChecker) extractIPs(email *EmailMessage) []string { } // isPublicIP checks if an IP address is public (not private, loopback, or reserved) -func (r *DNSListChecker) isPublicIP(ipStr string) bool { +func (r *RBLChecker) isPublicIP(ipStr string) bool { ip := net.ParseIP(ipStr) if ip == nil { return false } + // Check if it's a private network if ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { return false } + // Additional checks for reserved ranges + // 0.0.0.0/8, 192.0.0.0/24, 192.0.2.0/24 (TEST-NET-1), 198.51.100.0/24 (TEST-NET-2), 203.0.113.0/24 (TEST-NET-3) if ip.IsUnspecified() { return false } @@ -231,43 +198,51 @@ func (r *DNSListChecker) isPublicIP(ipStr string) bool { return true } -// checkIP checks a single IP against a single DNS list -func (r *DNSListChecker) checkIP(ip, list string) api.BlacklistCheck { +// checkIP checks a single IP against a single RBL +func (r *RBLChecker) checkIP(ip, rbl string) api.BlacklistCheck { check := api.BlacklistCheck{ - Rbl: list, + Rbl: rbl, } + // Reverse the IP for DNSBL query reversedIP := r.reverseIP(ip) if reversedIP == "" { check.Error = api.PtrTo("Failed to reverse IP address") return check } - query := fmt.Sprintf("%s.%s", reversedIP, list) + // Construct DNSBL query: reversed-ip.rbl-domain + query := fmt.Sprintf("%s.%s", reversedIP, rbl) + // Perform DNS lookup with timeout ctx, cancel := context.WithTimeout(context.Background(), r.Timeout) defer cancel() addrs, err := r.resolver.LookupHost(ctx, query) if err != nil { + // Most likely not listed (NXDOMAIN) if dnsErr, ok := err.(*net.DNSError); ok { if dnsErr.IsNotFound { check.Listed = false return check } } + // Other DNS errors check.Error = api.PtrTo(fmt.Sprintf("DNS lookup failed: %v", err)) return check } + // If we got a response, check the return code if len(addrs) > 0 { - check.Response = api.PtrTo(addrs[0]) + check.Response = api.PtrTo(addrs[0]) // Return code (e.g., 127.0.0.2) - // In RBL mode, 127.255.255.253/254/255 indicate operational errors, not real listings. - if r.filterErrorCodes && (addrs[0] == "127.255.255.253" || addrs[0] == "127.255.255.254" || addrs[0] == "127.255.255.255") { + // Check for RBL error codes: 127.255.255.253, 127.255.255.254, 127.255.255.255 + // These indicate RBL operational issues, not actual listings + if addrs[0] == "127.255.255.253" || addrs[0] == "127.255.255.254" || addrs[0] == "127.255.255.255" { check.Listed = false - check.Error = api.PtrTo(fmt.Sprintf("RBL %s returned error code %s (RBL operational issue)", list, addrs[0])) + check.Error = api.PtrTo(fmt.Sprintf("RBL %s returned error code %s (RBL operational issue)", rbl, addrs[0])) } else { + // Normal listing response check.Listed = true } } @@ -275,47 +250,44 @@ func (r *DNSListChecker) checkIP(ip, list string) api.BlacklistCheck { return check } -// reverseIP reverses an IPv4 address for DNSBL/DNSWL queries +// reverseIP reverses an IPv4 address for DNSBL queries // Example: 192.0.2.1 -> 1.2.0.192 -func (r *DNSListChecker) reverseIP(ipStr string) string { +func (r *RBLChecker) reverseIP(ipStr string) string { ip := net.ParseIP(ipStr) if ip == nil { return "" } + // Convert to IPv4 ipv4 := ip.To4() if ipv4 == nil { return "" // IPv6 not supported yet } + // Reverse the octets return fmt.Sprintf("%d.%d.%d.%d", ipv4[3], ipv4[2], ipv4[1], ipv4[0]) } -// CalculateScore calculates the list contribution to deliverability. -// Informational lists are not counted in the score. -func (r *DNSListChecker) CalculateScore(results *DNSListResults) (int, string) { +// CalculateRBLScore calculates the blacklist contribution to deliverability +func (r *RBLChecker) CalculateRBLScore(results *RBLResults) (int, string) { if results == nil || len(results.IPsChecked) == 0 { + // No IPs to check, give benefit of doubt return 100, "" } - scoringListCount := len(r.Lists) - len(r.informationalSet) - if scoringListCount <= 0 { - return 100, "A+" - } - - percentage := 100 - results.RelevantListedCount*100/scoringListCount + percentage := 100 - results.ListedCount*100/len(r.RBLs) return percentage, ScoreToGrade(percentage) } -// GetUniqueListedIPs returns a list of unique IPs that are listed on at least one entry -func (r *DNSListChecker) GetUniqueListedIPs(results *DNSListResults) []string { +// GetUniqueListedIPs returns a list of unique IPs that are listed on at least one RBL +func (r *RBLChecker) GetUniqueListedIPs(results *RBLResults) []string { var listedIPs []string - for ip, checks := range results.Checks { - for _, check := range checks { + for ip, rblChecks := range results.Checks { + for _, check := range rblChecks { if check.Listed { listedIPs = append(listedIPs, ip) - break + break // Only add the IP once } } } @@ -323,17 +295,17 @@ func (r *DNSListChecker) GetUniqueListedIPs(results *DNSListResults) []string { return listedIPs } -// GetListsForIP returns all lists that match a specific IP -func (r *DNSListChecker) GetListsForIP(results *DNSListResults, ip string) []string { - var lists []string +// GetRBLsForIP returns all RBLs that list a specific IP +func (r *RBLChecker) GetRBLsForIP(results *RBLResults, ip string) []string { + var rbls []string - if checks, exists := results.Checks[ip]; exists { - for _, check := range checks { + if rblChecks, exists := results.Checks[ip]; exists { + for _, check := range rblChecks { if check.Listed { - lists = append(lists, check.Rbl) + rbls = append(rbls, check.Rbl) } } } - return lists + return rbls } diff --git a/pkg/analyzer/rbl_test.go b/pkg/analyzer/rbl_test.go index 86f3c95..a1de270 100644 --- a/pkg/analyzer/rbl_test.go +++ b/pkg/analyzer/rbl_test.go @@ -59,8 +59,8 @@ func TestNewRBLChecker(t *testing.T) { if checker.Timeout != tt.expectedTimeout { t.Errorf("Timeout = %v, want %v", checker.Timeout, tt.expectedTimeout) } - if len(checker.Lists) != tt.expectedRBLs { - t.Errorf("RBLs count = %d, want %d", len(checker.Lists), tt.expectedRBLs) + if len(checker.RBLs) != tt.expectedRBLs { + t.Errorf("RBLs count = %d, want %d", len(checker.RBLs), tt.expectedRBLs) } if checker.resolver == nil { t.Error("Resolver should not be nil") @@ -326,7 +326,7 @@ func TestGetBlacklistScore(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - score, _ := checker.CalculateScore(tt.results) + score, _ := checker.CalculateRBLScore(tt.results) if score != tt.expectedScore { t.Errorf("GetBlacklistScore() = %v, want %v", score, tt.expectedScore) } @@ -402,7 +402,7 @@ func TestGetRBLsForIP(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - rbls := checker.GetListsForIP(results, tt.ip) + rbls := checker.GetRBLsForIP(results, tt.ip) if len(rbls) != len(tt.expectedRBLs) { t.Errorf("Got %d RBLs, want %d", len(rbls), len(tt.expectedRBLs)) diff --git a/pkg/analyzer/report.go b/pkg/analyzer/report.go index bd12960..dc420fb 100644 --- a/pkg/analyzer/report.go +++ b/pkg/analyzer/report.go @@ -35,8 +35,7 @@ type ReportGenerator struct { spamAnalyzer *SpamAssassinAnalyzer rspamdAnalyzer *RspamdAnalyzer dnsAnalyzer *DNSAnalyzer - rblChecker *DNSListChecker - dnswlChecker *DNSListChecker + rblChecker *RBLChecker contentAnalyzer *ContentAnalyzer headerAnalyzer *HeaderAnalyzer } @@ -46,7 +45,6 @@ func NewReportGenerator( dnsTimeout time.Duration, httpTimeout time.Duration, rbls []string, - dnswls []string, checkAllIPs bool, ) *ReportGenerator { return &ReportGenerator{ @@ -55,7 +53,6 @@ func NewReportGenerator( rspamdAnalyzer: NewRspamdAnalyzer(), dnsAnalyzer: NewDNSAnalyzer(dnsTimeout), rblChecker: NewRBLChecker(dnsTimeout, rbls, checkAllIPs), - dnswlChecker: NewDNSWLChecker(dnsTimeout, dnswls, checkAllIPs), contentAnalyzer: NewContentAnalyzer(httpTimeout), headerAnalyzer: NewHeaderAnalyzer(), } @@ -68,8 +65,7 @@ type AnalysisResults struct { Content *ContentResults DNS *api.DNSResults Headers *api.HeaderAnalysis - RBL *DNSListResults - DNSWL *DNSListResults + RBL *RBLResults SpamAssassin *api.SpamAssassinResult Rspamd *api.RspamdResult } @@ -85,7 +81,6 @@ func (r *ReportGenerator) AnalyzeEmail(email *EmailMessage) *AnalysisResults { results.Headers = r.headerAnalyzer.GenerateHeaderAnalysis(email, results.Authentication) results.DNS = r.dnsAnalyzer.AnalyzeDNS(email, results.Authentication, results.Headers) results.RBL = r.rblChecker.CheckEmail(email) - results.DNSWL = r.dnswlChecker.CheckEmail(email) results.SpamAssassin = r.spamAnalyzer.AnalyzeSpamAssassin(email) results.Rspamd = r.rspamdAnalyzer.AnalyzeRspamd(email) results.Content = r.contentAnalyzer.AnalyzeContent(email) @@ -140,7 +135,7 @@ func (r *ReportGenerator) GenerateReport(testID uuid.UUID, results *AnalysisResu blacklistScore := 0 var blacklistGrade string if results.RBL != nil { - blacklistScore, blacklistGrade = r.rblChecker.CalculateScore(results.RBL) + blacklistScore, blacklistGrade = r.rblChecker.CalculateRBLScore(results.RBL) } saScore, saGrade := r.spamAnalyzer.CalculateSpamAssassinScore(results.SpamAssassin) @@ -202,11 +197,6 @@ func (r *ReportGenerator) GenerateReport(testID uuid.UUID, results *AnalysisResu report.Blacklists = &results.RBL.Checks } - // Add whitelist checks as a map of IP -> array of BlacklistCheck (informational only) - if results.DNSWL != nil && len(results.DNSWL.Checks) > 0 { - report.Whitelists = &results.DNSWL.Checks - } - // Add SpamAssassin result with individual deliverability score if results.SpamAssassin != nil { saGradeTyped := api.SpamAssassinResultDeliverabilityGrade(saGrade) diff --git a/pkg/analyzer/report_test.go b/pkg/analyzer/report_test.go index 82e923e..5a325b1 100644 --- a/pkg/analyzer/report_test.go +++ b/pkg/analyzer/report_test.go @@ -32,7 +32,7 @@ import ( ) func TestNewReportGenerator(t *testing.T) { - gen := NewReportGenerator(10*time.Second, 10*time.Second, DefaultRBLs, DefaultDNSWLs, false) + gen := NewReportGenerator(10*time.Second, 10*time.Second, DefaultRBLs, false) if gen == nil { t.Fatal("Expected report generator, got nil") } @@ -55,7 +55,7 @@ func TestNewReportGenerator(t *testing.T) { } func TestAnalyzeEmail(t *testing.T) { - gen := NewReportGenerator(10*time.Second, 10*time.Second, DefaultRBLs, DefaultDNSWLs, false) + gen := NewReportGenerator(10*time.Second, 10*time.Second, DefaultRBLs, false) email := createTestEmail() @@ -75,7 +75,7 @@ func TestAnalyzeEmail(t *testing.T) { } func TestGenerateReport(t *testing.T) { - gen := NewReportGenerator(10*time.Second, 10*time.Second, DefaultRBLs, DefaultDNSWLs, false) + gen := NewReportGenerator(10*time.Second, 10*time.Second, DefaultRBLs, false) testID := uuid.New() email := createTestEmail() @@ -130,7 +130,7 @@ func TestGenerateReport(t *testing.T) { } func TestGenerateReportWithSpamAssassin(t *testing.T) { - gen := NewReportGenerator(10*time.Second, 10*time.Second, DefaultRBLs, DefaultDNSWLs, false) + gen := NewReportGenerator(10*time.Second, 10*time.Second, DefaultRBLs, false) testID := uuid.New() email := createTestEmailWithSpamAssassin() @@ -150,7 +150,7 @@ func TestGenerateReportWithSpamAssassin(t *testing.T) { } func TestGenerateRawEmail(t *testing.T) { - gen := NewReportGenerator(10*time.Second, 10*time.Second, DefaultRBLs, DefaultDNSWLs, false) + gen := NewReportGenerator(10*time.Second, 10*time.Second, DefaultRBLs, false) tests := []struct { name string diff --git a/web/src/lib/components/SummaryCard.svelte b/web/src/lib/components/SummaryCard.svelte index 5d93513..fe8af8e 100644 --- a/web/src/lib/components/SummaryCard.svelte +++ b/web/src/lib/components/SummaryCard.svelte @@ -331,7 +331,7 @@ highlight: { color: "good", bold: true }, link: "#dns-bimi", }); - if (bimiResult?.details && bimiResult.details.indexOf("declined") == 0) { + if (bimiResult.details && bimiResult.details.indexOf("declined") == 0) { segments.push({ text: " declined to participate" }); } else if (bimiResult?.result === "fail") { segments.push({ text: " but " }); diff --git a/web/src/lib/components/WhitelistCard.svelte b/web/src/lib/components/WhitelistCard.svelte deleted file mode 100644 index ee0b0e2..0000000 --- a/web/src/lib/components/WhitelistCard.svelte +++ /dev/null @@ -1,62 +0,0 @@ - - -
- DNS whitelists identify trusted senders. Being listed here is a positive signal, but has - no impact on the overall score. -
- -| - - {check.error - ? "Error" - : check.listed - ? "Listed" - : "Not listed"} - - | -{check.rbl} |
-