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]
