From 53939a66973733bf927ea93867f3754d2f980530 Mon Sep 17 00:00:00 2001 From: dxbjavid Date: Mon, 1 Jun 2026 15:14:05 +0530 Subject: [PATCH 1/2] reject CR/LF in SimpleSMTPHeader and SimpleNNTPHeader fields --- .../commons/net/nntp/SimpleNNTPHeader.java | 14 ++++++++++++-- .../commons/net/smtp/SimpleSMTPHeader.java | 16 +++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/apache/commons/net/nntp/SimpleNNTPHeader.java b/src/main/java/org/apache/commons/net/nntp/SimpleNNTPHeader.java index 3421140e8..47cd01ade 100644 --- a/src/main/java/org/apache/commons/net/nntp/SimpleNNTPHeader.java +++ b/src/main/java/org/apache/commons/net/nntp/SimpleNNTPHeader.java @@ -56,13 +56,20 @@ public class SimpleNNTPHeader { * @param subject The value of the {@code Subject:} header field. This should be the subject of the article. */ public SimpleNNTPHeader(final String from, final String subject) { - this.from = from; - this.subject = subject; + this.from = validate(from); + this.subject = validate(subject); this.newsgroups = new StringBuilder(); this.headerFields = new StringBuilder(); this.newsgroupCount = 0; } + private static String validate(final String value) { + if (value != null && (value.indexOf('\r') >= 0 || value.indexOf('\n') >= 0)) { + throw new IllegalArgumentException("Header value cannot contain CR or LF characters"); + } + return value; + } + /** * Adds an arbitrary header field with the given value to the article header. * These headers will be written after the {@code From}, Newsgroups, and Subject fields @@ -76,6 +83,8 @@ public SimpleNNTPHeader(final String from, final String subject) { * @param value The value of the added header field. */ public void addHeaderField(final String headerField, final String value) { + validate(headerField); + validate(value); headerFields.append(headerField); headerFields.append(": "); headerFields.append(value); @@ -88,6 +97,7 @@ public void addHeaderField(final String headerField, final String value) { * @param newsgroup The newsgroup to add to the article's newsgroup distribution list. */ public void addNewsgroup(final String newsgroup) { + validate(newsgroup); if (newsgroupCount++ > 0) { newsgroups.append(','); } diff --git a/src/main/java/org/apache/commons/net/smtp/SimpleSMTPHeader.java b/src/main/java/org/apache/commons/net/smtp/SimpleSMTPHeader.java index 527dc1448..30bd8f074 100644 --- a/src/main/java/org/apache/commons/net/smtp/SimpleSMTPHeader.java +++ b/src/main/java/org/apache/commons/net/smtp/SimpleSMTPHeader.java @@ -66,19 +66,27 @@ public SimpleSMTPHeader(final String from, final String to, final String subject if (from == null) { throw new IllegalArgumentException("From cannot be null"); } - this.to = to; - this.from = from; - this.subject = subject; + this.to = validate(to); + this.from = validate(from); + this.subject = validate(subject); this.headerFields = new StringBuffer(); this.cc = null; } + private static String validate(final String value) { + if (value != null && (value.indexOf('\r') >= 0 || value.indexOf('\n') >= 0)) { + throw new IllegalArgumentException("Header value cannot contain CR or LF characters"); + } + return value; + } + /** * Add an email address to the CC (carbon copy or courtesy copy) list. * * @param address The email address to add to the CC list. */ public void addCC(final String address) { + validate(address); if (cc == null) { cc = new StringBuffer(); } else { @@ -101,6 +109,8 @@ public void addCC(final String address) { * @param value The value of the added header field. */ public void addHeaderField(final String headerField, final String value) { + validate(headerField); + validate(value); if (!hasHeaderDate && "Date".equals(headerField)) { hasHeaderDate = true; } From 61092a43595c95d259b4e7c97da148bd63fefce5 Mon Sep 17 00:00:00 2001 From: dxbjavid Date: Mon, 1 Jun 2026 16:47:15 +0530 Subject: [PATCH 2/2] Add tests for CR/LF rejection in SimpleSMTPHeader and SimpleNNTPHeader --- .../net/nntp/SimpleNNTPHeaderTestCase.java | 58 +++++++++++++++++++ .../net/smtp/SimpleSMTPHeaderTestCase.java | 22 +++++++ 2 files changed, 80 insertions(+) create mode 100644 src/test/java/org/apache/commons/net/nntp/SimpleNNTPHeaderTestCase.java diff --git a/src/test/java/org/apache/commons/net/nntp/SimpleNNTPHeaderTestCase.java b/src/test/java/org/apache/commons/net/nntp/SimpleNNTPHeaderTestCase.java new file mode 100644 index 000000000..02d5f6e83 --- /dev/null +++ b/src/test/java/org/apache/commons/net/nntp/SimpleNNTPHeaderTestCase.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.net.nntp; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Tests {@link SimpleNNTPHeader}. + */ +class SimpleNNTPHeaderTestCase { + + @Test + void testRejectCarriageReturnInConstructor() { + assertThrows(IllegalArgumentException.class, () -> new SimpleNNTPHeader("foobar@foo.invalid", "Subject\rInjected: header")); + } + + @Test + void testRejectLineFeedInAddHeaderField() { + final SimpleNNTPHeader header = new SimpleNNTPHeader("foobar@foo.invalid", "Just testing"); + assertThrows(IllegalArgumentException.class, () -> header.addHeaderField("Organization", "Foobar, Inc.\nInjected: header")); + } + + @Test + void testRejectLineFeedInAddNewsgroup() { + final SimpleNNTPHeader header = new SimpleNNTPHeader("foobar@foo.invalid", "Just testing"); + assertThrows(IllegalArgumentException.class, () -> header.addNewsgroup("alt.test\nInjected: header")); + } + + @Test + void testRejectLineFeedInConstructor() { + assertThrows(IllegalArgumentException.class, () -> new SimpleNNTPHeader("foobar@foo.invalid", "Subject\nInjected: header")); + } + + @Test + void testToString() { + final SimpleNNTPHeader header = new SimpleNNTPHeader("foobar@foo.invalid", "Just testing"); + header.addNewsgroup("alt.test"); + header.addHeaderField("Organization", "Foobar, Inc."); + assertEquals("From: foobar@foo.invalid\nNewsgroups: alt.test\nSubject: Just testing\nOrganization: Foobar, Inc.\n\n", header.toString()); + } +} diff --git a/src/test/java/org/apache/commons/net/smtp/SimpleSMTPHeaderTestCase.java b/src/test/java/org/apache/commons/net/smtp/SimpleSMTPHeaderTestCase.java index 098218b33..d8cf2ed13 100644 --- a/src/test/java/org/apache/commons/net/smtp/SimpleSMTPHeaderTestCase.java +++ b/src/test/java/org/apache/commons/net/smtp/SimpleSMTPHeaderTestCase.java @@ -105,6 +105,28 @@ void testToStringAddHeaderDate() { assertEquals("Date: dummy date\nFrom: from@here.invalid\n\n", header.toString()); } + @Test + void testRejectCarriageReturnInConstructor() { + assertThrows(IllegalArgumentException.class, () -> new SimpleSMTPHeader("from@here.invalid", "to@there.invalid", "Subject\rInjected: header")); + } + + @Test + void testRejectLineFeedInAddCC() { + final SimpleSMTPHeader header = new SimpleSMTPHeader("from@here.invalid", null, null); + assertThrows(IllegalArgumentException.class, () -> header.addCC("cc@there.invalid\nBcc: victim@there.invalid")); + } + + @Test + void testRejectLineFeedInAddHeaderField() { + final SimpleSMTPHeader header = new SimpleSMTPHeader("from@here.invalid", null, null); + assertThrows(IllegalArgumentException.class, () -> header.addHeaderField("X-Header1", "value 1\nX-Injected: value 2")); + } + + @Test + void testRejectLineFeedInConstructor() { + assertThrows(IllegalArgumentException.class, () -> new SimpleSMTPHeader("from@here.invalid", "to@there.invalid", "Subject\nInjected: header")); + } + @Test void testToStringNoFrom() { assertThrows(IllegalArgumentException.class, () -> new SimpleSMTPHeader(null, null, null));