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]
