Change RBL test to return map of ips
This commit is contained in:
parent
1be917136c
commit
954a9d705e
4 changed files with 113 additions and 72 deletions
|
|
@ -277,9 +277,18 @@ components:
|
|||
items:
|
||||
$ref: '#/components/schemas/DNSRecord'
|
||||
blacklists:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/BlacklistCheck'
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/BlacklistCheck'
|
||||
description: Map of IP addresses to their blacklist check results (array of checks per IP)
|
||||
example:
|
||||
"192.0.2.1":
|
||||
- rbl: "zen.spamhaus.org"
|
||||
listed: false
|
||||
- rbl: "bl.spamcop.net"
|
||||
listed: false
|
||||
raw_headers:
|
||||
type: string
|
||||
description: Raw email headers
|
||||
|
|
@ -498,14 +507,9 @@ components:
|
|||
BlacklistCheck:
|
||||
type: object
|
||||
required:
|
||||
- ip
|
||||
- rbl
|
||||
- listed
|
||||
properties:
|
||||
ip:
|
||||
type: string
|
||||
description: IP address checked
|
||||
example: "192.0.2.1"
|
||||
rbl:
|
||||
type: string
|
||||
description: RBL/DNSBL name
|
||||
|
|
|
|||
|
|
@ -68,14 +68,14 @@ func NewRBLChecker(timeout time.Duration, rbls []string) *RBLChecker {
|
|||
|
||||
// RBLResults represents the results of RBL checks
|
||||
type RBLResults struct {
|
||||
Checks []RBLCheck
|
||||
Checks map[string][]RBLCheck // Map of IP -> list of RBL checks for that IP
|
||||
IPsChecked []string
|
||||
ListedCount int
|
||||
}
|
||||
|
||||
// RBLCheck represents a single RBL check result
|
||||
// Note: IP is not included here as it's used as the map key in the API
|
||||
type RBLCheck struct {
|
||||
IP string
|
||||
RBL string
|
||||
Listed bool
|
||||
Response string
|
||||
|
|
@ -84,7 +84,9 @@ type RBLCheck struct {
|
|||
|
||||
// CheckEmail checks all IPs found in the email headers against RBLs
|
||||
func (r *RBLChecker) CheckEmail(email *EmailMessage) *RBLResults {
|
||||
results := &RBLResults{}
|
||||
results := &RBLResults{
|
||||
Checks: make(map[string][]RBLCheck),
|
||||
}
|
||||
|
||||
// Extract IPs from Received headers
|
||||
ips := r.extractIPs(email)
|
||||
|
|
@ -98,7 +100,7 @@ func (r *RBLChecker) CheckEmail(email *EmailMessage) *RBLResults {
|
|||
for _, ip := range ips {
|
||||
for _, rbl := range r.RBLs {
|
||||
check := r.checkIP(ip, rbl)
|
||||
results.Checks = append(results.Checks, check)
|
||||
results.Checks[ip] = append(results.Checks[ip], check)
|
||||
if check.Listed {
|
||||
results.ListedCount++
|
||||
}
|
||||
|
|
@ -179,7 +181,6 @@ func (r *RBLChecker) isPublicIP(ipStr string) bool {
|
|||
// checkIP checks a single IP against a single RBL
|
||||
func (r *RBLChecker) checkIP(ip, rbl string) RBLCheck {
|
||||
check := RBLCheck{
|
||||
IP: ip,
|
||||
RBL: rbl,
|
||||
}
|
||||
|
||||
|
|
@ -285,14 +286,16 @@ func (r *RBLChecker) GenerateRBLChecks(results *RBLResults) []api.Check {
|
|||
checks = append(checks, summaryCheck)
|
||||
|
||||
// Create individual checks for each listing and RBL errors
|
||||
for _, check := range results.Checks {
|
||||
if check.Listed {
|
||||
detailCheck := r.generateListingCheck(&check)
|
||||
checks = append(checks, detailCheck)
|
||||
} else if check.Error != "" && strings.Contains(check.Error, "RBL operational issue") {
|
||||
// Generate info check for RBL errors
|
||||
detailCheck := r.generateRBLErrorCheck(&check)
|
||||
checks = append(checks, detailCheck)
|
||||
for ip, rblChecks := range results.Checks {
|
||||
for _, check := range rblChecks {
|
||||
if check.Listed {
|
||||
detailCheck := r.generateListingCheck(ip, &check)
|
||||
checks = append(checks, detailCheck)
|
||||
} else if check.Error != "" && strings.Contains(check.Error, "RBL operational issue") {
|
||||
// Generate info check for RBL errors
|
||||
detailCheck := r.generateRBLErrorCheck(ip, &check)
|
||||
checks = append(checks, detailCheck)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -310,7 +313,11 @@ func (r *RBLChecker) generateSummaryCheck(results *RBLResults) api.Check {
|
|||
check.Score = score
|
||||
check.Grade = ScoreToCheckGrade(score)
|
||||
|
||||
totalChecks := len(results.Checks)
|
||||
// Calculate total checks across all IPs
|
||||
totalChecks := 0
|
||||
for _, rblChecks := range results.Checks {
|
||||
totalChecks += len(rblChecks)
|
||||
}
|
||||
listedCount := results.ListedCount
|
||||
|
||||
if listedCount == 0 {
|
||||
|
|
@ -345,7 +352,7 @@ func (r *RBLChecker) generateSummaryCheck(results *RBLResults) api.Check {
|
|||
}
|
||||
|
||||
// generateListingCheck creates a check for a specific RBL listing
|
||||
func (r *RBLChecker) generateListingCheck(rblCheck *RBLCheck) api.Check {
|
||||
func (r *RBLChecker) generateListingCheck(ip string, rblCheck *RBLCheck) api.Check {
|
||||
check := api.Check{
|
||||
Category: api.Blacklist,
|
||||
Name: fmt.Sprintf("RBL: %s", rblCheck.RBL),
|
||||
|
|
@ -354,7 +361,7 @@ func (r *RBLChecker) generateListingCheck(rblCheck *RBLCheck) api.Check {
|
|||
Grade: ScoreToCheckGrade(0),
|
||||
}
|
||||
|
||||
check.Message = fmt.Sprintf("IP %s is listed on %s", rblCheck.IP, rblCheck.RBL)
|
||||
check.Message = fmt.Sprintf("IP %s is listed on %s", ip, rblCheck.RBL)
|
||||
|
||||
// Determine severity based on which RBL
|
||||
if strings.Contains(rblCheck.RBL, "spamhaus") {
|
||||
|
|
@ -381,7 +388,7 @@ func (r *RBLChecker) generateListingCheck(rblCheck *RBLCheck) api.Check {
|
|||
}
|
||||
|
||||
// generateRBLErrorCheck creates an info-level check for RBL operational errors
|
||||
func (r *RBLChecker) generateRBLErrorCheck(rblCheck *RBLCheck) api.Check {
|
||||
func (r *RBLChecker) generateRBLErrorCheck(ip string, rblCheck *RBLCheck) api.Check {
|
||||
check := api.Check{
|
||||
Category: api.Blacklist,
|
||||
Name: fmt.Sprintf("RBL: %s", rblCheck.RBL),
|
||||
|
|
@ -391,7 +398,7 @@ func (r *RBLChecker) generateRBLErrorCheck(rblCheck *RBLCheck) api.Check {
|
|||
Severity: api.PtrTo(api.CheckSeverityInfo),
|
||||
}
|
||||
|
||||
check.Message = fmt.Sprintf("RBL %s returned an error code for IP %s", rblCheck.RBL, rblCheck.IP)
|
||||
check.Message = fmt.Sprintf("RBL %s returned an error code for IP %s", rblCheck.RBL, ip)
|
||||
|
||||
advice := fmt.Sprintf("The RBL %s is experiencing operational issues (error code: %s).", rblCheck.RBL, rblCheck.Response)
|
||||
check.Advice = &advice
|
||||
|
|
@ -406,13 +413,14 @@ func (r *RBLChecker) generateRBLErrorCheck(rblCheck *RBLCheck) api.Check {
|
|||
|
||||
// GetUniqueListedIPs returns a list of unique IPs that are listed on at least one RBL
|
||||
func (r *RBLChecker) GetUniqueListedIPs(results *RBLResults) []string {
|
||||
seenIPs := make(map[string]bool)
|
||||
var listedIPs []string
|
||||
|
||||
for _, check := range results.Checks {
|
||||
if check.Listed && !seenIPs[check.IP] {
|
||||
listedIPs = append(listedIPs, check.IP)
|
||||
seenIPs[check.IP] = true
|
||||
for ip, rblChecks := range results.Checks {
|
||||
for _, check := range rblChecks {
|
||||
if check.Listed {
|
||||
listedIPs = append(listedIPs, ip)
|
||||
break // Only add the IP once
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -423,9 +431,11 @@ func (r *RBLChecker) GetUniqueListedIPs(results *RBLResults) []string {
|
|||
func (r *RBLChecker) GetRBLsForIP(results *RBLResults, ip string) []string {
|
||||
var rbls []string
|
||||
|
||||
for _, check := range results.Checks {
|
||||
if check.IP == ip && check.Listed {
|
||||
rbls = append(rbls, check.RBL)
|
||||
if rblChecks, exists := results.Checks[ip]; exists {
|
||||
for _, check := range rblChecks {
|
||||
if check.Listed {
|
||||
rbls = append(rbls, check.RBL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -347,7 +347,9 @@ func TestGenerateSummaryCheck(t *testing.T) {
|
|||
results: &RBLResults{
|
||||
IPsChecked: []string{"198.51.100.1"},
|
||||
ListedCount: 0,
|
||||
Checks: make([]RBLCheck, 6), // 6 default RBLs
|
||||
Checks: map[string][]RBLCheck{
|
||||
"198.51.100.1": make([]RBLCheck, 6), // 6 default RBLs
|
||||
},
|
||||
},
|
||||
expectedStatus: api.CheckStatusPass,
|
||||
expectedScore: 200,
|
||||
|
|
@ -357,7 +359,9 @@ func TestGenerateSummaryCheck(t *testing.T) {
|
|||
results: &RBLResults{
|
||||
IPsChecked: []string{"198.51.100.1"},
|
||||
ListedCount: 1,
|
||||
Checks: make([]RBLCheck, 6),
|
||||
Checks: map[string][]RBLCheck{
|
||||
"198.51.100.1": make([]RBLCheck, 6),
|
||||
},
|
||||
},
|
||||
expectedStatus: api.CheckStatusWarn,
|
||||
expectedScore: 100,
|
||||
|
|
@ -367,7 +371,9 @@ func TestGenerateSummaryCheck(t *testing.T) {
|
|||
results: &RBLResults{
|
||||
IPsChecked: []string{"198.51.100.1"},
|
||||
ListedCount: 2,
|
||||
Checks: make([]RBLCheck, 6),
|
||||
Checks: map[string][]RBLCheck{
|
||||
"198.51.100.1": make([]RBLCheck, 6),
|
||||
},
|
||||
},
|
||||
expectedStatus: api.CheckStatusWarn,
|
||||
expectedScore: 50,
|
||||
|
|
@ -377,7 +383,9 @@ func TestGenerateSummaryCheck(t *testing.T) {
|
|||
results: &RBLResults{
|
||||
IPsChecked: []string{"198.51.100.1"},
|
||||
ListedCount: 4,
|
||||
Checks: make([]RBLCheck, 6),
|
||||
Checks: map[string][]RBLCheck{
|
||||
"198.51.100.1": make([]RBLCheck, 6),
|
||||
},
|
||||
},
|
||||
expectedStatus: api.CheckStatusFail,
|
||||
expectedScore: 0,
|
||||
|
|
@ -413,7 +421,6 @@ func TestGenerateListingCheck(t *testing.T) {
|
|||
{
|
||||
name: "Spamhaus listing",
|
||||
rblCheck: &RBLCheck{
|
||||
IP: "198.51.100.1",
|
||||
RBL: "zen.spamhaus.org",
|
||||
Listed: true,
|
||||
Response: "127.0.0.2",
|
||||
|
|
@ -424,7 +431,6 @@ func TestGenerateListingCheck(t *testing.T) {
|
|||
{
|
||||
name: "SpamCop listing",
|
||||
rblCheck: &RBLCheck{
|
||||
IP: "198.51.100.1",
|
||||
RBL: "bl.spamcop.net",
|
||||
Listed: true,
|
||||
Response: "127.0.0.2",
|
||||
|
|
@ -435,7 +441,6 @@ func TestGenerateListingCheck(t *testing.T) {
|
|||
{
|
||||
name: "Other RBL listing",
|
||||
rblCheck: &RBLCheck{
|
||||
IP: "198.51.100.1",
|
||||
RBL: "dnsbl.sorbs.net",
|
||||
Listed: true,
|
||||
Response: "127.0.0.2",
|
||||
|
|
@ -449,7 +454,7 @@ func TestGenerateListingCheck(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
check := checker.generateListingCheck(tt.rblCheck)
|
||||
check := checker.generateListingCheck("198.51.100.1", tt.rblCheck)
|
||||
|
||||
if check.Status != tt.expectedStatus {
|
||||
t.Errorf("Status = %v, want %v", check.Status, tt.expectedStatus)
|
||||
|
|
@ -490,9 +495,11 @@ func TestGenerateRBLChecks(t *testing.T) {
|
|||
results: &RBLResults{
|
||||
IPsChecked: []string{"198.51.100.1"},
|
||||
ListedCount: 0,
|
||||
Checks: []RBLCheck{
|
||||
{IP: "198.51.100.1", RBL: "zen.spamhaus.org", Listed: false},
|
||||
{IP: "198.51.100.1", RBL: "bl.spamcop.net", Listed: false},
|
||||
Checks: map[string][]RBLCheck{
|
||||
"198.51.100.1": {
|
||||
{RBL: "zen.spamhaus.org", Listed: false},
|
||||
{RBL: "bl.spamcop.net", Listed: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
minChecks: 1, // Summary check only
|
||||
|
|
@ -502,10 +509,12 @@ func TestGenerateRBLChecks(t *testing.T) {
|
|||
results: &RBLResults{
|
||||
IPsChecked: []string{"198.51.100.1"},
|
||||
ListedCount: 2,
|
||||
Checks: []RBLCheck{
|
||||
{IP: "198.51.100.1", RBL: "zen.spamhaus.org", Listed: true},
|
||||
{IP: "198.51.100.1", RBL: "bl.spamcop.net", Listed: true},
|
||||
{IP: "198.51.100.1", RBL: "dnsbl.sorbs.net", Listed: false},
|
||||
Checks: map[string][]RBLCheck{
|
||||
"198.51.100.1": {
|
||||
{RBL: "zen.spamhaus.org", Listed: true},
|
||||
{RBL: "bl.spamcop.net", Listed: true},
|
||||
{RBL: "dnsbl.sorbs.net", Listed: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
minChecks: 3, // Summary + 2 listing checks
|
||||
|
|
@ -534,12 +543,18 @@ func TestGenerateRBLChecks(t *testing.T) {
|
|||
|
||||
func TestGetUniqueListedIPs(t *testing.T) {
|
||||
results := &RBLResults{
|
||||
Checks: []RBLCheck{
|
||||
{IP: "198.51.100.1", RBL: "zen.spamhaus.org", Listed: true},
|
||||
{IP: "198.51.100.1", RBL: "bl.spamcop.net", Listed: true},
|
||||
{IP: "198.51.100.2", RBL: "zen.spamhaus.org", Listed: true},
|
||||
{IP: "198.51.100.2", RBL: "bl.spamcop.net", Listed: false},
|
||||
{IP: "198.51.100.3", RBL: "zen.spamhaus.org", Listed: false},
|
||||
Checks: map[string][]RBLCheck{
|
||||
"198.51.100.1": {
|
||||
{RBL: "zen.spamhaus.org", Listed: true},
|
||||
{RBL: "bl.spamcop.net", Listed: true},
|
||||
},
|
||||
"198.51.100.2": {
|
||||
{RBL: "zen.spamhaus.org", Listed: true},
|
||||
{RBL: "bl.spamcop.net", Listed: false},
|
||||
},
|
||||
"198.51.100.3": {
|
||||
{RBL: "zen.spamhaus.org", Listed: false},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -556,11 +571,15 @@ func TestGetUniqueListedIPs(t *testing.T) {
|
|||
|
||||
func TestGetRBLsForIP(t *testing.T) {
|
||||
results := &RBLResults{
|
||||
Checks: []RBLCheck{
|
||||
{IP: "198.51.100.1", RBL: "zen.spamhaus.org", Listed: true},
|
||||
{IP: "198.51.100.1", RBL: "bl.spamcop.net", Listed: true},
|
||||
{IP: "198.51.100.1", RBL: "dnsbl.sorbs.net", Listed: false},
|
||||
{IP: "198.51.100.2", RBL: "zen.spamhaus.org", Listed: true},
|
||||
Checks: map[string][]RBLCheck{
|
||||
"198.51.100.1": {
|
||||
{RBL: "zen.spamhaus.org", Listed: true},
|
||||
{RBL: "bl.spamcop.net", Listed: true},
|
||||
{RBL: "dnsbl.sorbs.net", Listed: false},
|
||||
},
|
||||
"198.51.100.2": {
|
||||
{RBL: "zen.spamhaus.org", Listed: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -190,21 +190,29 @@ func (r *ReportGenerator) GenerateReport(testID uuid.UUID, results *AnalysisResu
|
|||
}
|
||||
}
|
||||
|
||||
// Add blacklist checks
|
||||
// Add blacklist checks as a map of IP -> array of BlacklistCheck
|
||||
if results.RBL != nil && len(results.RBL.Checks) > 0 {
|
||||
blacklistChecks := make([]api.BlacklistCheck, 0, len(results.RBL.Checks))
|
||||
for _, check := range results.RBL.Checks {
|
||||
blCheck := api.BlacklistCheck{
|
||||
Ip: check.IP,
|
||||
Rbl: check.RBL,
|
||||
Listed: check.Listed,
|
||||
blacklistMap := make(map[string][]api.BlacklistCheck)
|
||||
|
||||
// Convert internal RBL checks to API format
|
||||
for ip, rblChecks := range results.RBL.Checks {
|
||||
apiChecks := make([]api.BlacklistCheck, 0, len(rblChecks))
|
||||
for _, check := range rblChecks {
|
||||
blCheck := api.BlacklistCheck{
|
||||
Rbl: check.RBL,
|
||||
Listed: check.Listed,
|
||||
}
|
||||
if check.Response != "" {
|
||||
blCheck.Response = &check.Response
|
||||
}
|
||||
apiChecks = append(apiChecks, blCheck)
|
||||
}
|
||||
if check.Response != "" {
|
||||
blCheck.Response = &check.Response
|
||||
}
|
||||
blacklistChecks = append(blacklistChecks, blCheck)
|
||||
blacklistMap[ip] = apiChecks
|
||||
}
|
||||
|
||||
if len(blacklistMap) > 0 {
|
||||
report.Blacklists = &blacklistMap
|
||||
}
|
||||
report.Blacklists = &blacklistChecks
|
||||
}
|
||||
|
||||
// Add raw headers
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue