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-jdkim.git

commit f05f7ea0a5b03b96b1a5f2be25941463d41950f0
Author: Benoit TELLIER <[email protected]>
AuthorDate: Fri Oct 30 08:48:26 2026 +0100

    [FIX] Message: reject header lines that are neither fields nor continuations
    
    mime4j silently dropped header lines without a colon (an mbox "From "
    line, a stray word), so such a line could be inserted into a signed
    header block and the signature still verified.
    
    Message now throws MimeException for them, and for a header block
    starting with a continuation line; DKIMVerifier reports it as a
    "Mime parsing exception" PermFail.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../java/org/apache/james/jdkim/impl/Message.java  | 18 ++++++++++++-----
 .../test/java/org/apache/james/jdkim/DKIMTest.java | 23 ++++++++++++++++++++++
 2 files changed, 36 insertions(+), 5 deletions(-)

diff --git a/main/src/main/java/org/apache/james/jdkim/impl/Message.java 
b/main/src/main/java/org/apache/james/jdkim/impl/Message.java
index ae2cb93..8bb580d 100644
--- a/main/src/main/java/org/apache/james/jdkim/impl/Message.java
+++ b/main/src/main/java/org/apache/james/jdkim/impl/Message.java
@@ -88,9 +88,13 @@ public class Message implements Headers {
 
     /**
      * Splits a CRLF-terminated header block into raw fields: a line starting
-     * with WSP continues the previous field. Lines without a colon are 
skipped.
+     * with WSP continues the previous field.
+     *
+     * @throws MimeException on a line that is neither a field nor a
+     *                       continuation: DKIM cannot vouch for a header block
+     *                       it does not see whole.
      */
-    private static List<String> splitFields(String header) {
+    private static List<String> splitFields(String header) throws 
MimeException {
         List<String> all = new ArrayList<>();
         StringBuilder current = null;
         int start = 0;
@@ -116,10 +120,14 @@ public class Message implements Headers {
         return all;
     }
 
-    private static void addField(List<String> all, StringBuilder field) {
-        if (field != null && field.indexOf(":") != -1) {
-            all.add(field.toString());
+    private static void addField(List<String> all, StringBuilder field) throws 
MimeException {
+        if (field == null) {
+            return;
+        }
+        if (field.indexOf(":") == -1 || field.charAt(0) == ' ' || 
field.charAt(0) == '\t') {
+            throw new MimeException("Malformed header field: " + field);
         }
+        all.add(field.toString());
     }
 
     /**
diff --git a/main/src/test/java/org/apache/james/jdkim/DKIMTest.java 
b/main/src/test/java/org/apache/james/jdkim/DKIMTest.java
index b5557a2..49c7ee4 100644
--- a/main/src/test/java/org/apache/james/jdkim/DKIMTest.java
+++ b/main/src/test/java/org/apache/james/jdkim/DKIMTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.jdkim;
 
+import org.apache.james.mime4j.MimeException;
 import java.security.MessageDigest;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -364,4 +365,26 @@ public class DKIMTest {
         assertThat(folded.getFields()).containsExactly("Subject: a\r\n\tb\r\n 
c", "From: d");
         assertThat(headerOnly.getFields()).containsExactly("From: d", "To: e");
     }
+
+    @Test
+    public void headerLineWithoutColonShouldMakeVerificationFail() throws 
Exception {
+        byte[] mail = "From: 
[email protected]\r\n\r\nbody\r\n".getBytes(StandardCharsets.US_ASCII);
+        String field = new DKIMSigner("v=1; a=rsa-sha256; c=simple; 
d=messiah.edu; h=from; s=selector2;", TestKeys.privateKey)
+                .sign(new ByteArrayInputStream(mail));
+        byte[] tampered = (field + "\r\nReply-To [email protected]\r\nFrom: 
[email protected]\r\n\r\nbody\r\n")
+                .getBytes(StandardCharsets.US_ASCII);
+
+        assertThatThrownBy(() -> verifier.verify(new 
ByteArrayInputStream(tampered)))
+                .isInstanceOf(PermFailException.class)
+                .hasMessageContaining("Mime parsing exception");
+    }
+
+    @Test
+    public void malformedHeaderBlocksShouldBeRejected() {
+        for (String header : Arrays.asList("j\t\r\n", "From: a\r\nD(*\r\n", " 
x: y\r\nFrom: a\r\n")) {
+            assertThatThrownBy(() -> new Message(new 
ByteArrayInputStream((header + 
"\r\nbody\r\n").getBytes(StandardCharsets.US_ASCII))))
+                    .as(header)
+                    .isInstanceOf(MimeException.class);
+        }
+    }
 }


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

Reply via email to