diff --git a/CVE-2024-21742.patch b/CVE-2024-21742.patch new file mode 100644 index 0000000000000000000000000000000000000000..92cce6ed14c28314e3f334515c02b7e3cca05ab6 --- /dev/null +++ b/CVE-2024-21742.patch @@ -0,0 +1,159 @@ +From 9dec5df2a588fed8027839815daefa79ee66efd1 Mon Sep 17 00:00:00 2001 +From: Benoit TELLIER +Date: Fri, 5 Jan 2024 08:12:54 +0100 +Subject: [PATCH] [FIX] Prevent header injection with MIME4J DOM (#91) + +--- + core/pom.xml | 5 +++ + .../apache/james/mime4j/stream/RawField.java | 23 +++++++++++ + .../james/mime4j/stream/RawFieldTest.java | 40 +++++++++++++++++-- + .../message/DefaultMessageWriterTest.java | 11 +++++ + 4 files changed, 76 insertions(+), 3 deletions(-) + +diff --git a/core/pom.xml b/core/pom.xml +index 942e3e3..412ad67 100644 +--- a/core/pom.xml ++++ b/core/pom.xml +@@ -42,6 +42,11 @@ + commons-io + test + ++ ++ org.assertj ++ assertj-core ++ test ++ + + + +diff --git a/core/src/main/java/org/apache/james/mime4j/stream/RawField.java b/core/src/main/java/org/apache/james/mime4j/stream/RawField.java +index 8bcaa77..03a00dd 100644 +--- a/core/src/main/java/org/apache/james/mime4j/stream/RawField.java ++++ b/core/src/main/java/org/apache/james/mime4j/stream/RawField.java +@@ -55,6 +55,29 @@ public final class RawField implements Field { + + public RawField(String name, String body) { + this(null, -1, name, body); ++ ++ int pos = 0; ++ ++ while (true) { ++ pos = body.indexOf('\r', pos); ++ if (pos < 0) { ++ break; ++ } ++ if (pos < body.length() + 2) { ++ if (body.charAt(pos + 1) != '\n') { ++ throw new IllegalArgumentException("Injection of un-encoded line breaks inside header field could be assimilated to header injection"); ++ } ++ if (pos != body.length() - 2 && !isSpace(body, pos + 2)) { ++ throw new IllegalArgumentException("Injection of un-encoded line breaks inside header field could be assimilated to header injection"); ++ } ++ } ++ pos ++; ++ } ++ } ++ ++ private static boolean isSpace(String body, int pos) { ++ return body.charAt(pos) == ' ' ++ || body.charAt(pos) == '\t'; + } + + public ByteSequence getRaw() { +diff --git a/core/src/test/java/org/apache/james/mime4j/stream/RawFieldTest.java b/core/src/test/java/org/apache/james/mime4j/stream/RawFieldTest.java +index 5a1cc7d..90d8513 100644 +--- a/core/src/test/java/org/apache/james/mime4j/stream/RawFieldTest.java ++++ b/core/src/test/java/org/apache/james/mime4j/stream/RawFieldTest.java +@@ -19,6 +19,9 @@ + + package org.apache.james.mime4j.stream; + ++import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; ++import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; ++ + import junit.framework.Assert; + import org.apache.james.mime4j.util.ByteSequence; + import org.apache.james.mime4j.util.ContentUtil; +@@ -45,11 +48,11 @@ public class RawFieldTest { + Assert.assertEquals("stuff", field1.getBody()); + Assert.assertEquals("raw: stuff", field1.toString()); + +- RawField field2 = new RawField("raw", null); ++ RawField field2 = new RawField("raw", "any"); + Assert.assertNull(field2.getRaw()); + Assert.assertEquals("raw", field2.getName()); +- Assert.assertEquals(null, field2.getBody()); +- Assert.assertEquals("raw: ", field2.toString()); ++ Assert.assertEquals("any", field2.getBody()); ++ Assert.assertEquals("raw: any", field2.toString()); + } + + @Test +@@ -63,4 +66,35 @@ public class RawFieldTest { + Assert.assertEquals(s, field.toString()); + } + ++ @Test ++ public void shouldRejectAmbiguousLineEnding() { ++ assertThatThrownBy(() -> new RawField("Name", "Value\r\ncheating")).isInstanceOf(IllegalArgumentException.class); ++ } ++ ++ @Test ++ public void shouldAcceptCRLFTerminatedHeader() { ++ assertThatCode(() -> new RawField("Name", "Value\r\n")).doesNotThrowAnyException(); ++ } ++ ++ @Test ++ public void shouldAcceptTabFolding() { ++ assertThatCode(() -> new RawField("Name", "Value\r\n\thello")).doesNotThrowAnyException(); ++ } ++ ++ @Test ++ public void shouldAcceptSpaceFolding() { ++ assertThatCode(() -> new RawField("Name", "Value\r\n hello")).doesNotThrowAnyException(); ++ } ++ ++ @Test ++ public void shouldAcceptOnlyDelimiter() { ++ assertThatCode(() -> new RawField("Name", "\r\n")).doesNotThrowAnyException(); ++ } ++ ++ ++ @Test ++ public void shouldAcceptNoDelimiter() { ++ assertThatCode(() -> new RawField("Name", "Value")).doesNotThrowAnyException(); ++ } ++ + } +diff --git a/dom/src/test/java/org/apache/james/mime4j/message/DefaultMessageWriterTest.java b/dom/src/test/java/org/apache/james/mime4j/message/DefaultMessageWriterTest.java +index dece2b5..19eafe2 100644 +--- a/dom/src/test/java/org/apache/james/mime4j/message/DefaultMessageWriterTest.java ++++ b/dom/src/test/java/org/apache/james/mime4j/message/DefaultMessageWriterTest.java +@@ -20,6 +20,7 @@ + package org.apache.james.mime4j.message; + + import static org.assertj.core.api.Assertions.assertThat; ++import static org.assertj.core.api.Assertions.assertThatThrownBy; + + import org.apache.james.mime4j.Charsets; + import org.apache.james.mime4j.dom.Message; +@@ -46,5 +47,15 @@ public class DefaultMessageWriterTest { + "\r\n" + + "this is the body"); + } ++ ++ @Test ++ public void shouldThrowOnHeaderInjectionAttempt() throws Exception { ++ Message.Builder builder = Message.Builder.of() ++ .setBody("this is the body", Charsets.UTF_8) ++ .setFrom("sender@localhost"); ++ ++ assertThatThrownBy(() -> builder.setContentTransferEncoding("victim@attacker.com\r\nReply-To: attacker@evil.com")) ++ .isInstanceOf(IllegalArgumentException.class); ++ } + + } +\ No newline at end of file +-- +2.33.0 + diff --git a/apache-mime4j.spec b/apache-mime4j.spec index e3975cf7ca17d2b33cf584503b725469997e1475..372837a574d00e89ca92423dda218a4786979630 100644 --- a/apache-mime4j.spec +++ b/apache-mime4j.spec @@ -1,10 +1,12 @@ Name: apache-mime4j Version: 0.8.3 -Release: 1 +Release: 2 Summary: Apache JAMES Mime4j -License: ASL 2.0 and Artistic +License: Apache-2.0 URL: http://james.apache.org/mime4j Source0: http://archive.apache.org/dist/james/mime4j/0.8.3/james-mime4j-sources-0.8.3.zip +# https://github.com/apache/james-mime4j/commit/9dec5df2a588fed8027839815daefa79ee66efd1 +Patch0: CVE-2024-21742.patch BuildRequires: maven-local mvn(com.google.guava:guava:18.0) mvn(commons-io:commons-io) BuildRequires: mvn(commons-logging:commons-logging) mvn(junit:junit) BuildRequires: mvn(org.apache:apache:pom:) mvn(org.apache.felix:maven-bundle-plugin) @@ -20,7 +22,7 @@ Summary: Javadoc for %{name} API documentation for %{name}. %prep -%setup -q -n james-mime4j +%autosetup -n james-mime4j -p1 %pom_remove_plugin :apache-rat-plugin %pom_remove_plugin :maven-jar-plugin %pom_disable_module assemble @@ -29,7 +31,7 @@ for p in core dom storage; do done %build -%mvn_build +%mvn_build -f -- -Dsource=8 %install %mvn_install @@ -42,6 +44,9 @@ done %license LICENSE NOTICE %changelog +* Thu Feb 29 2024 yaoxin - 0.8.3-2 +- Fix CVE-2024-21742 + * Tue Jun 14 2022 SimpleUpdate Robot - 0.8.3-1 - Upgrade to version 0.8.3