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 6d1ba8b66a985426b4990690feb73e44b07cfc6a
Author: Quan Tran <[email protected]>
AuthorDate: Tue Nov 3 09:59:37 2026 +0700

    [FIX] Domain-spec regexes: match a toplabel in linear time
    
    The first toplabel alternative, *alphanum ALPHA *alphanum, can split an
    alphanumeric label around any of its letters. When the rest of the term
    does not match, java.util.regex tries every split, which is quadratic in
    the length of the label: "v=spf1 include:x." followed by 16000 letters
    and "!" took 5 seconds to parse, 32000 letters 20 seconds. The record
    comes from the DNS of the checked domain, so its owner sets the CPU
    each check costs. Before the domain-spec regexes stopped recursing once
    per character, the stack overflowed past a few thousand characters;
    below that, a parse took up to a second.
    
    Split the label before its first letter: *DIGIT ALPHA *alphanum matches
    the same toplabels (RFC 7208 7.1) one way only.
---
 .../java/org/apache/james/jspf/core/SPFTermsRegexps.java    |  6 +++++-
 .../java/org/apache/james/jspf/SPFExecutorBaseTest.java     | 13 +++++++++++++
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git 
a/resolver/src/main/java/org/apache/james/jspf/core/SPFTermsRegexps.java 
b/resolver/src/main/java/org/apache/james/jspf/core/SPFTermsRegexps.java
index a0a9da4..dc39a89 100644
--- a/resolver/src/main/java/org/apache/james/jspf/core/SPFTermsRegexps.java
+++ b/resolver/src/main/java/org/apache/james/jspf/core/SPFTermsRegexps.java
@@ -82,9 +82,13 @@ public interface SPFTermsRegexps {
      * ABNF: toplabel = ( *alphanum ALPHA *alphanum ) / ( 1*alphanum "-" *(
      * alphanum / "-" ) alphanum ) ; LDH rule plus additional TLD restrictions 
;
      * (see [RFC3696], Section 2)
+     *
+     * The first alternative splits the label before its first letter, so it 
matches one way only:
+     * *alphanum ALPHA *alphanum can split it around any of its letters, and 
trying them all when
+     * the rest of the term does not match is quadratic in the length of the 
label.
      */
     final String TOP_LABEL_REGEX = "(?:"
-            + ALPHA_DIGIT_PATTERN + "*" + SPFTermsRegexps.ALPHA_PATTERN
+            + "[0-9]*" + SPFTermsRegexps.ALPHA_PATTERN
             + "{1}" + ALPHA_DIGIT_PATTERN + "*|(?:"
             + ALPHA_DIGIT_PATTERN + "+" + "\\-" + "[a-zA-Z0-9\\-]*"
             + ALPHA_DIGIT_PATTERN + "))";
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 fd5ae51..4e66f42 100644
--- a/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
+++ b/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
@@ -658,4 +658,17 @@ public abstract class SPFExecutorBaseTest {
         // RFC 7208 6.1: the result of the redirect target
         assertEquals(SPFErrorConstants.NEUTRAL_CONV, result.getResult());
     }
+
+    @Test(timeout = 10000)
+    public void shouldReturnPermErrorOnLongInvalidTopLabel() throws 
IOException {
+        String hostname = 
"shouldReturnPermErrorOnLongInvalidTopLabel.spfexecutor.fake";
+        FakeResolver fakeResolver = new FakeResolver();
+        // a toplabel of 50000 letters, then "!", which no toplabel holds: no 
term matches
+        fakeResolver.addRecord(longTxtRecord(hostname, "v=spf1 include:x." + 
"a".repeat(50000) + "! -all"));
+
+        SPF spf = createCustomSPF(new DNSServiceXBillImpl(fakeResolver));
+        SPFResult result = spf.checkSPF("1.2.3.4", "a_user@" + hostname, 
hostname);
+
+        assertEquals(SPFErrorConstants.PERM_ERROR_CONV, result.getResult());
+    }
 }


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

Reply via email to