This is an automated email from the ASF dual-hosted git repository.

chibenwa pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-jspf.git

commit 803d0395c10d12c9289b3a7c44273dfcb4c8afb3
Author: Quan Tran <[email protected]>
AuthorDate: Thu Nov 5 10:33:42 2026 +0700

    [FIX] SPFRetriever: discard a record not beginning with v=spf1
    
    A record was selected when it began with "v=spf1" once trimmed, so
    " v=spf1 +all", with a leading space, counted as an SPF record: next to
    "v=spf1 -all" the check gave permerror for two records, and alone it
    was selected before the record of the obsolete SPF type. RFC 7208 4.5
    discards "records that do not begin with a version section of exactly
    "v=spf1"", which a space or the end of the record ends.
    
    Compare the record as it is, ignoring case only.
---
 .../apache/james/jspf/policies/SPFRetriever.java   |  4 ++--
 .../org/apache/james/jspf/SPFExecutorBaseTest.java | 27 ++++++++++++++++++++++
 2 files changed, 29 insertions(+), 2 deletions(-)

diff --git 
a/resolver/src/main/java/org/apache/james/jspf/policies/SPFRetriever.java 
b/resolver/src/main/java/org/apache/james/jspf/policies/SPFRetriever.java
index 765f15e..57d2df7 100644
--- a/resolver/src/main/java/org/apache/james/jspf/policies/SPFRetriever.java
+++ b/resolver/src/main/java/org/apache/james/jspf/policies/SPFRetriever.java
@@ -126,8 +126,8 @@ public class SPFRetriever implements SPFChecker {
             // DO NOT trim the result!
             String compare = all.next();
 
-            // We trim the compare value only for the comparison
-            if 
(compare.toLowerCase().trim().startsWith(SPF1Constants.SPF_VERSION1 + " ") || 
compare.trim().equalsIgnoreCase(SPF1Constants.SPF_VERSION1)) {
+            // RFC 7208 4.5: a record begins with exactly "v=spf1", ended by a 
space or by the end of the record
+            if (compare.toLowerCase().startsWith(SPF1Constants.SPF_VERSION1 + 
" ") || compare.equalsIgnoreCase(SPF1Constants.SPF_VERSION1)) {
                 if (returnValue == null) {
                     returnValue = compare;
                 } else {
diff --git 
a/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java 
b/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
index 98057a5..b5d6f3c 100644
--- a/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
+++ b/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
@@ -944,6 +944,33 @@ public abstract class SPFExecutorBaseTest {
         assertEquals(SPFErrorConstants.TEMP_ERROR_CONV, result.getResult());
     }
 
+    @Test(timeout = 10000)
+    public void shouldDiscardARecordThatDoesNotBeginWithTheVersion() throws 
IOException {
+        String hostname = "leadingSpace.spfexecutor.fake";
+        FakeResolver fakeResolver = new FakeResolver();
+        fakeResolver.addRecord(new TXTRecord(Name.fromString(hostname + "."), 
DClass.IN, 30L, "v=spf1 -all"));
+        fakeResolver.addRecord(new TXTRecord(Name.fromString(hostname + "."), 
DClass.IN, 30L, " v=spf1 +all"));
+        SPF spf = createCustomSPF(new DNSServiceXBillImpl(fakeResolver));
+
+        SPFResult result = spf.checkSPF("192.0.2.10", "a_user@" + hostname, 
hostname);
+
+        // RFC 7208 4.5: "discard records that do not begin with a version 
section of exactly "v=spf1""
+        assertEquals(SPFErrorConstants.FAIL_CONV, result.getResult());
+    }
+
+    @Test(timeout = 10000)
+    public void shouldReadTheSpfTypeWhenNoTxtRecordBeginsWithTheVersion() 
throws IOException {
+        String hostname = "leadingSpaceOnly.spfexecutor.fake";
+        FakeResolver fakeResolver = new FakeResolver();
+        fakeResolver.addRecord(new TXTRecord(Name.fromString(hostname + "."), 
DClass.IN, 30L, " v=spf1 +all"));
+        fakeResolver.addRecord(new SPFRecord(Name.fromString(hostname + "."), 
DClass.IN, 30L, "v=spf1 -all"));
+        SPF spf = createCustomSPF(new DNSServiceXBillImpl(fakeResolver));
+
+        SPFResult result = spf.checkSPF("192.0.2.10", "a_user@" + hostname, 
hostname);
+
+        assertEquals(SPFErrorConstants.FAIL_CONV, result.getResult());
+    }
+
     @Test(timeout = 10000)
     public void shouldCheckTheTxtRecordWhenTheLookupOfTheSpfTypeFails() throws 
IOException {
         String hostname = "spftypeservfail.spfexecutor.fake";


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to