happyDeliver/pkg/analyzer/authentication_iprev_test.go

225 lines
7.4 KiB
Go

// This file is part of the happyDeliver (R) project.
// Copyright (c) 2025 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// This program is offered under a commercial and under the AGPL license.
// For commercial licensing, contact us at <contact@happydomain.org>.
//
// For AGPL licensing:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package analyzer
import (
"testing"
"git.happydns.org/happyDeliver/internal/api"
)
func TestParseIPRevResult(t *testing.T) {
tests := []struct {
name string
part string
expectedResult api.IPRevResultResult
expectedIP *string
expectedHostname *string
}{
{
name: "IPRev pass with IP and hostname",
part: "iprev=pass smtp.remote-ip=195.110.101.58 (authsmtp74.register.it)",
expectedResult: api.Pass,
expectedIP: api.PtrTo("195.110.101.58"),
expectedHostname: api.PtrTo("authsmtp74.register.it"),
},
{
name: "IPRev pass without smtp prefix",
part: "iprev=pass remote-ip=192.0.2.1 (mail.example.com)",
expectedResult: api.Pass,
expectedIP: api.PtrTo("192.0.2.1"),
expectedHostname: api.PtrTo("mail.example.com"),
},
{
name: "IPRev fail",
part: "iprev=fail smtp.remote-ip=198.51.100.42 (unknown.host.com)",
expectedResult: api.Fail,
expectedIP: api.PtrTo("198.51.100.42"),
expectedHostname: api.PtrTo("unknown.host.com"),
},
{
name: "IPRev temperror",
part: "iprev=temperror smtp.remote-ip=203.0.113.1",
expectedResult: api.Temperror,
expectedIP: api.PtrTo("203.0.113.1"),
expectedHostname: nil,
},
{
name: "IPRev permerror",
part: "iprev=permerror smtp.remote-ip=192.0.2.100",
expectedResult: api.Permerror,
expectedIP: api.PtrTo("192.0.2.100"),
expectedHostname: nil,
},
{
name: "IPRev with IPv6",
part: "iprev=pass smtp.remote-ip=2001:db8::1 (ipv6.example.com)",
expectedResult: api.Pass,
expectedIP: api.PtrTo("2001:db8::1"),
expectedHostname: api.PtrTo("ipv6.example.com"),
},
{
name: "IPRev with subdomain hostname",
part: "iprev=pass smtp.remote-ip=192.0.2.50 (mail.subdomain.example.com)",
expectedResult: api.Pass,
expectedIP: api.PtrTo("192.0.2.50"),
expectedHostname: api.PtrTo("mail.subdomain.example.com"),
},
{
name: "IPRev pass without parentheses",
part: "iprev=pass smtp.remote-ip=192.0.2.200",
expectedResult: api.Pass,
expectedIP: api.PtrTo("192.0.2.200"),
expectedHostname: nil,
},
}
analyzer := NewAuthenticationAnalyzer()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := analyzer.parseIPRevResult(tt.part)
// Check result
if result.Result != tt.expectedResult {
t.Errorf("Result = %v, want %v", result.Result, tt.expectedResult)
}
// Check IP
if tt.expectedIP != nil {
if result.Ip == nil {
t.Errorf("IP = nil, want %v", *tt.expectedIP)
} else if *result.Ip != *tt.expectedIP {
t.Errorf("IP = %v, want %v", *result.Ip, *tt.expectedIP)
}
} else {
if result.Ip != nil {
t.Errorf("IP = %v, want nil", *result.Ip)
}
}
// Check hostname
if tt.expectedHostname != nil {
if result.Hostname == nil {
t.Errorf("Hostname = nil, want %v", *tt.expectedHostname)
} else if *result.Hostname != *tt.expectedHostname {
t.Errorf("Hostname = %v, want %v", *result.Hostname, *tt.expectedHostname)
}
} else {
if result.Hostname != nil {
t.Errorf("Hostname = %v, want nil", *result.Hostname)
}
}
// Check details
if result.Details == nil {
t.Error("Expected Details to be set, got nil")
}
})
}
}
func TestParseAuthenticationResultsHeader_IPRev(t *testing.T) {
tests := []struct {
name string
header string
expectedIPRevResult *api.IPRevResultResult
expectedIP *string
expectedHostname *string
}{
{
name: "IPRev pass in Authentication-Results",
header: "mx.google.com; iprev=pass smtp.remote-ip=195.110.101.58 (authsmtp74.register.it)",
expectedIPRevResult: api.PtrTo(api.Pass),
expectedIP: api.PtrTo("195.110.101.58"),
expectedHostname: api.PtrTo("authsmtp74.register.it"),
},
{
name: "IPRev with other authentication methods",
header: "mx.google.com; spf=pass smtp.mailfrom=sender@example.com; iprev=pass smtp.remote-ip=192.0.2.1 (mail.example.com); dkim=pass header.d=example.com",
expectedIPRevResult: api.PtrTo(api.Pass),
expectedIP: api.PtrTo("192.0.2.1"),
expectedHostname: api.PtrTo("mail.example.com"),
},
{
name: "IPRev fail",
header: "mx.google.com; iprev=fail smtp.remote-ip=198.51.100.42",
expectedIPRevResult: api.PtrTo(api.Fail),
expectedIP: api.PtrTo("198.51.100.42"),
expectedHostname: nil,
},
{
name: "No IPRev in header",
header: "mx.google.com; spf=pass smtp.mailfrom=sender@example.com",
expectedIPRevResult: nil,
},
{
name: "Multiple IPRev results - only first is parsed",
header: "mx.google.com; iprev=pass smtp.remote-ip=192.0.2.1 (first.com); iprev=fail smtp.remote-ip=192.0.2.2 (second.com)",
expectedIPRevResult: api.PtrTo(api.Pass),
expectedIP: api.PtrTo("192.0.2.1"),
expectedHostname: api.PtrTo("first.com"),
},
}
analyzer := NewAuthenticationAnalyzer()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
results := &api.AuthenticationResults{}
analyzer.parseAuthenticationResultsHeader(tt.header, results)
// Check IPRev
if tt.expectedIPRevResult != nil {
if results.Iprev == nil {
t.Errorf("Expected IPRev result, got nil")
} else {
if results.Iprev.Result != *tt.expectedIPRevResult {
t.Errorf("IPRev Result = %v, want %v", results.Iprev.Result, *tt.expectedIPRevResult)
}
if tt.expectedIP != nil {
if results.Iprev.Ip == nil || *results.Iprev.Ip != *tt.expectedIP {
var gotIP string
if results.Iprev.Ip != nil {
gotIP = *results.Iprev.Ip
}
t.Errorf("IPRev IP = %v, want %v", gotIP, *tt.expectedIP)
}
}
if tt.expectedHostname != nil {
if results.Iprev.Hostname == nil || *results.Iprev.Hostname != *tt.expectedHostname {
var gotHostname string
if results.Iprev.Hostname != nil {
gotHostname = *results.Iprev.Hostname
}
t.Errorf("IPRev Hostname = %v, want %v", gotHostname, *tt.expectedHostname)
}
}
}
} else {
if results.Iprev != nil {
t.Errorf("Expected no IPRev result, got %+v", results.Iprev)
}
}
})
}
}