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 7659c43fcc82ffbb505d7c14e8855e493bd0002f Author: Benoit TELLIER <[email protected]> AuthorDate: Wed Nov 4 21:51:34 2026 +0100 [FIX] Message: strip the WSP after a field name in linear time rawName removed the trailing WSP with replaceAll("[ \t]+$", ""). On a run of WSP inside the name, the regex retried from each position of the run and backed off one character at a time: quadratic, seconds of CPU for an 8 KB field and hours for a 1 MB one, on every message parsed, signed or not, before any signature is examined. The name now ends after a backward scan from the colon. This also keeps the WSP before a final \x85 or \r, which "$" treated as the end of the name. --- .../java/org/apache/james/jdkim/impl/Message.java | 6 +++- .../apache/james/jdkim/impl/MessageLimitsTest.java | 32 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) 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 687e669..91a89e0 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 @@ -144,7 +144,11 @@ public class Message implements Headers { * without trailing WSP. */ private static String rawName(String field) { - return field.substring(0, field.indexOf(':')).replaceAll("[ \t]+$", "").toLowerCase(Locale.US); + int end = field.indexOf(':'); + while (end > 0 && (field.charAt(end - 1) == ' ' || field.charAt(end - 1) == '\t')) { + end--; + } + return field.substring(0, end).toLowerCase(Locale.US); } public InputStream getBodyInputStream() { diff --git a/main/src/test/java/org/apache/james/jdkim/impl/MessageLimitsTest.java b/main/src/test/java/org/apache/james/jdkim/impl/MessageLimitsTest.java index b2945f0..a90d76d 100644 --- a/main/src/test/java/org/apache/james/jdkim/impl/MessageLimitsTest.java +++ b/main/src/test/java/org/apache/james/jdkim/impl/MessageLimitsTest.java @@ -62,6 +62,38 @@ public class MessageLimitsTest { assertThat(message.getFields("To")).hasSize(1); } + @Test(timeout = 10_000) + public void shouldParseFieldNameWithLongWspRunInLinearTime() throws Exception { + StringBuilder sb = new StringBuilder("From: [email protected]\r\nx"); + for (int i = 0; i < 200_000; i++) { + sb.append(' '); + } + sb.append("y: v\r\n\r\nbody\r\n"); + + Message message = new Message(stream(sb)); + + assertThat(message.getFields()).hasSize(2); + } + + @Test + public void shouldStillMatchFieldNameWithoutItsTrailingWsp() throws Exception { + StringBuilder sb = new StringBuilder("From: [email protected]\r\nX-Long \t : v\r\n\r\nbody\r\n"); + + Message message = new Message(stream(sb)); + + assertThat(message.getFields("X-Long")).hasSize(1); + } + + @Test + public void shouldKeepWspBeforeLineTerminatorLikeCharInFieldName() throws Exception { + byte[] bytes = ("From: [email protected]\r\nx " + (char) 0x85 + ": v\r\n\r\nbody\r\n") + .getBytes(StandardCharsets.ISO_8859_1); + + Message message = new Message(new ByteArrayInputStream(bytes)); + + assertThat(message.getFields("x " + (char) 0x85)).hasSize(1); + } + @Test public void shouldParseLongLine() throws Exception { StringBuilder sb = new StringBuilder("From: [email protected]\r\nX-Long: "); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
