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 f22f3254ae089cb3c1ed3a95b7f14fbd1393893c
Author: Quan Tran <[email protected]>
AuthorDate: Thu Nov 5 13:23:33 2026 +0700

    [FIX] ip4 mechanism: an octet with a leading zero is a permerror
    
    The ip4 mechanism read an octet with a leading zero, as in
    "ip4:01.2.3.4", as the number, so the term matched 1.2.3.4. RFC 7208
    12 writes ip4-network = qnum "." qnum "." qnum "." qnum, where a qnum
    is a number from 0 to 255 without a leading zero, and a record that
    does not match the grammar is a permerror (4.6).
    
    Reject such an octet in IP4Mechanism.isValidAddress, the syntax of the
    record only: Inet6Util.isValidIPV4Address, which also reads the client
    address, is unchanged.
---
 .../java/org/apache/james/jspf/terms/IP4Mechanism.java   |  6 +++++-
 .../java/org/apache/james/jspf/SPFExecutorBaseTest.java  | 16 ++++++++++++++++
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git 
a/resolver/src/main/java/org/apache/james/jspf/terms/IP4Mechanism.java 
b/resolver/src/main/java/org/apache/james/jspf/terms/IP4Mechanism.java
index b5fab5a..4c99eb9 100644
--- a/resolver/src/main/java/org/apache/james/jspf/terms/IP4Mechanism.java
+++ b/resolver/src/main/java/org/apache/james/jspf/terms/IP4Mechanism.java
@@ -20,6 +20,8 @@
 
 package org.apache.james.jspf.terms;
 
+import java.util.Arrays;
+
 import org.apache.james.jspf.core.DNSLookupContinuation;
 import org.apache.james.jspf.core.IPAddr;
 import org.apache.james.jspf.core.Inet6Util;
@@ -92,10 +94,12 @@ public class IP4Mechanism extends GenericMechanism {
     }
 
     /**
+     * @return true for an ip4-network of RFC 7208 12: four qnum, decimal 
numbers from 0 to 255 without a leading zero
      * @see org.apache.james.jspf.core.Inet6Util#isValidIPV4Address(String)
      */
     protected boolean isValidAddress(String ipString) {
-        return Inet6Util.isValidIPV4Address(ipString);
+        return Inet6Util.isValidIPV4Address(ipString)
+            && Arrays.stream(ipString.split("\\.")).noneMatch(qnum -> 
qnum.length() > 1 && qnum.charAt(0) == '0');
     }
 
     /**
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 4710ee5..f611195 100644
--- a/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
+++ b/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
@@ -643,6 +643,22 @@ public abstract class SPFExecutorBaseTest {
         assertEquals(SPFErrorConstants.PERM_ERROR_CONV, result.getResult());
     }
 
+    @Test(timeout = 10000)
+    public void shouldReturnPermErrorOnAnIp4NetworkWithALeadingZero() throws 
IOException {
+        SPFResult result = checkRecord("ip4Zero.spfexecutor.fake", "v=spf1 
ip4:01.2.3.4 -all", "1.2.3.4");
+
+        // RFC 7208 12: qnum = DIGIT / %x31-39 DIGIT / "1" 2DIGIT / "2" 
%x30-34 DIGIT / "25" %x30-35
+        assertEquals(SPFErrorConstants.PERM_ERROR_CONV, result.getResult());
+    }
+
+    @Test(timeout = 10000)
+    public void shouldMatchAnIp4NetworkOfZeroOctets() throws IOException {
+        SPFResult result = checkRecord("ip4ZeroOctet.spfexecutor.fake", 
"v=spf1 ip4:10.0.0.0/8 -all", "10.0.2.4");
+
+        // a qnum of 0 alone stays valid
+        assertEquals(SPFErrorConstants.PASS_CONV, result.getResult());
+    }
+
     @Test(timeout = 10000)
     public void shouldReturnPermErrorOnIp6CidrOverflowingAnInt() throws 
IOException {
         SPFResult result = checkRecord("ip6CidrOverflow.spfexecutor.fake", 
"v=spf1 ip6:2001:db8::/99999999999 -all", "1.2.3.4");


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

Reply via email to