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 18885745350e2146d0b73ab04ac3c7c4c65bb462 Author: Quan Tran <[email protected]> AuthorDate: Thu Nov 5 13:12:29 2026 +0700 [FIX] a/mx mechanisms: a CIDR length with a leading zero is a permerror The a and mx mechanisms read a CIDR length with a leading zero, as "a/024" or "mx//064", as the number, where the ip4 mechanism already gives permerror: RFC 7208 5.6 allows "0" or a number without a leading zero, ip4-cidr-length = "/" ("0" / %x31-39 0*1DIGIT) and ip6-cidr-length = "/" ("0" / %x31-39 0*2DIGIT), and a record that does not match the grammar is a permerror (4.6). Reject such a length in AMechanism, which MXMechanism extends. "/0" stays valid. --- .../org/apache/james/jspf/terms/AMechanism.java | 7 ++--- .../org/apache/james/jspf/SPFExecutorBaseTest.java | 30 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/resolver/src/main/java/org/apache/james/jspf/terms/AMechanism.java b/resolver/src/main/java/org/apache/james/jspf/terms/AMechanism.java index c7f4098..f5b4ba8 100644 --- a/resolver/src/main/java/org/apache/james/jspf/terms/AMechanism.java +++ b/resolver/src/main/java/org/apache/james/jspf/terms/AMechanism.java @@ -112,9 +112,10 @@ public class AMechanism extends GenericMechanism implements SPFCheckerDNSRespons */ public synchronized void config(Configuration params) throws PermErrorException { super.config(params); - // the regex accepts any number of digits: more than 3 are invalid, and could overflow an int + // the regex accepts any number of digits: more than 3, which could overflow an int, or a leading zero, as + // in "a/024", are invalid (RFC 7208 5.6: "0" / %x31-39 0*1DIGIT, or 0*2DIGIT for ip6-cidr-length) if (params.groupCount() >= 2 && params.group(2) != null) { - if (params.group(2).length() > 3) { + if (params.group(2).length() > 3 || (params.group(2).length() > 1 && params.group(2).startsWith("0"))) { throw new PermErrorException("Ivalid IP4 CIDR length"); } ip4cidr = Integer.parseInt(params.group(2)); @@ -125,7 +126,7 @@ public class AMechanism extends GenericMechanism implements SPFCheckerDNSRespons ip4cidr = 32; } if (params.groupCount() >= 3 && params.group(3) != null) { - if (params.group(3).length() > 3) { + if (params.group(3).length() > 3 || (params.group(3).length() > 1 && params.group(3).startsWith("0"))) { throw new PermErrorException("Ivalid IP6 CIDR length"); } ip6cidr = Integer.parseInt(params.group(3).toString()); 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 def8399..4710ee5 100644 --- a/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java +++ b/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java @@ -1326,6 +1326,36 @@ public abstract class SPFExecutorBaseTest { assertEquals(SPFErrorConstants.PERM_ERROR_CONV, result.getResult()); } + @Test(timeout = 10000) + public void shouldReturnPermErrorOnAIp4CidrWithALeadingZero() throws IOException { + SPFResult result = checkRecord("aIp4CidrZero.spfexecutor.fake", "v=spf1 a/024 -all", "1.2.3.4"); + + // RFC 7208 5.6: ip4-cidr-length = "/" ("0" / %x31-39 0*1DIGIT) + assertEquals(SPFErrorConstants.PERM_ERROR_CONV, result.getResult()); + } + + @Test(timeout = 10000) + public void shouldReturnPermErrorOnMxIp6CidrWithALeadingZero() throws IOException { + SPFResult result = checkRecord("mxIp6CidrZero.spfexecutor.fake", "v=spf1 mx//064 -all", "1.2.3.4"); + + // RFC 7208 5.6: ip6-cidr-length = "/" ("0" / %x31-39 0*2DIGIT) + assertEquals(SPFErrorConstants.PERM_ERROR_CONV, result.getResult()); + } + + @Test(timeout = 10000) + public void shouldAcceptACidrLengthOfZero() throws IOException { + String hostname = "aCidrZero.spfexecutor.fake"; + FakeResolver fakeResolver = new FakeResolver(); + fakeResolver.addRecord(new TXTRecord(Name.fromString(hostname + "."), DClass.IN, 30L, "v=spf1 a/0//0 -all")); + fakeResolver.addRecord(new ARecord(Name.fromString(hostname + "."), DClass.IN, 30L, InetAddress.getByName("192.0.2.1"))); + SPF spf = createCustomSPF(new DNSServiceXBillImpl(fakeResolver)); + + SPFResult result = spf.checkSPF("1.2.3.4", "a_user@" + hostname, hostname); + + // "0" alone is a CIDR length: a/0 matches every IPv4 client of a domain having an IPv4 address + assertEquals(SPFErrorConstants.PASS_CONV, result.getResult()); + } + @Test(timeout = 10000) public void shouldReturnPermErrorOnMxIp6CidrOverflowingAnInt() throws IOException { SPFResult result = checkRecord("mxIp6CidrOverflow.spfexecutor.fake", "v=spf1 mx//99999999999 -all", "1.2.3.4"); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
