Skip to content

Commit 17a6955

Browse files
authored
Merge pull request #397 from dxbjavid/reject-crlf-header-injection
Reject CR/LF in SimpleSMTPHeader and SimpleNNTPHeader fields
2 parents b0e48a9 + 61092a4 commit 17a6955

4 files changed

Lines changed: 105 additions & 5 deletions

File tree

src/main/java/org/apache/commons/net/nntp/SimpleNNTPHeader.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,20 @@ public class SimpleNNTPHeader {
5656
* @param subject The value of the {@code Subject:} header field. This should be the subject of the article.
5757
*/
5858
public SimpleNNTPHeader(final String from, final String subject) {
59-
this.from = from;
60-
this.subject = subject;
59+
this.from = validate(from);
60+
this.subject = validate(subject);
6161
this.newsgroups = new StringBuilder();
6262
this.headerFields = new StringBuilder();
6363
this.newsgroupCount = 0;
6464
}
6565

66+
private static String validate(final String value) {
67+
if (value != null && (value.indexOf('\r') >= 0 || value.indexOf('\n') >= 0)) {
68+
throw new IllegalArgumentException("Header value cannot contain CR or LF characters");
69+
}
70+
return value;
71+
}
72+
6673
/**
6774
* Adds an arbitrary header field with the given value to the article header.
6875
* 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) {
7683
* @param value The value of the added header field.
7784
*/
7885
public void addHeaderField(final String headerField, final String value) {
86+
validate(headerField);
87+
validate(value);
7988
headerFields.append(headerField);
8089
headerFields.append(": ");
8190
headerFields.append(value);
@@ -88,6 +97,7 @@ public void addHeaderField(final String headerField, final String value) {
8897
* @param newsgroup The newsgroup to add to the article's newsgroup distribution list.
8998
*/
9099
public void addNewsgroup(final String newsgroup) {
100+
validate(newsgroup);
91101
if (newsgroupCount++ > 0) {
92102
newsgroups.append(',');
93103
}

src/main/java/org/apache/commons/net/smtp/SimpleSMTPHeader.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,27 @@ public SimpleSMTPHeader(final String from, final String to, final String subject
6666
if (from == null) {
6767
throw new IllegalArgumentException("From cannot be null");
6868
}
69-
this.to = to;
70-
this.from = from;
71-
this.subject = subject;
69+
this.to = validate(to);
70+
this.from = validate(from);
71+
this.subject = validate(subject);
7272
this.headerFields = new StringBuffer();
7373
this.cc = null;
7474
}
7575

76+
private static String validate(final String value) {
77+
if (value != null && (value.indexOf('\r') >= 0 || value.indexOf('\n') >= 0)) {
78+
throw new IllegalArgumentException("Header value cannot contain CR or LF characters");
79+
}
80+
return value;
81+
}
82+
7683
/**
7784
* Add an email address to the CC (carbon copy or courtesy copy) list.
7885
*
7986
* @param address The email address to add to the CC list.
8087
*/
8188
public void addCC(final String address) {
89+
validate(address);
8290
if (cc == null) {
8391
cc = new StringBuffer();
8492
} else {
@@ -101,6 +109,8 @@ public void addCC(final String address) {
101109
* @param value The value of the added header field.
102110
*/
103111
public void addHeaderField(final String headerField, final String value) {
112+
validate(headerField);
113+
validate(value);
104114
if (!hasHeaderDate && "Date".equals(headerField)) {
105115
hasHeaderDate = true;
106116
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.net.nntp;
18+
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertThrows;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
/**
25+
* Tests {@link SimpleNNTPHeader}.
26+
*/
27+
class SimpleNNTPHeaderTestCase {
28+
29+
@Test
30+
void testRejectCarriageReturnInConstructor() {
31+
assertThrows(IllegalArgumentException.class, () -> new SimpleNNTPHeader("foobar@foo.invalid", "Subject\rInjected: header"));
32+
}
33+
34+
@Test
35+
void testRejectLineFeedInAddHeaderField() {
36+
final SimpleNNTPHeader header = new SimpleNNTPHeader("foobar@foo.invalid", "Just testing");
37+
assertThrows(IllegalArgumentException.class, () -> header.addHeaderField("Organization", "Foobar, Inc.\nInjected: header"));
38+
}
39+
40+
@Test
41+
void testRejectLineFeedInAddNewsgroup() {
42+
final SimpleNNTPHeader header = new SimpleNNTPHeader("foobar@foo.invalid", "Just testing");
43+
assertThrows(IllegalArgumentException.class, () -> header.addNewsgroup("alt.test\nInjected: header"));
44+
}
45+
46+
@Test
47+
void testRejectLineFeedInConstructor() {
48+
assertThrows(IllegalArgumentException.class, () -> new SimpleNNTPHeader("foobar@foo.invalid", "Subject\nInjected: header"));
49+
}
50+
51+
@Test
52+
void testToString() {
53+
final SimpleNNTPHeader header = new SimpleNNTPHeader("foobar@foo.invalid", "Just testing");
54+
header.addNewsgroup("alt.test");
55+
header.addHeaderField("Organization", "Foobar, Inc.");
56+
assertEquals("From: foobar@foo.invalid\nNewsgroups: alt.test\nSubject: Just testing\nOrganization: Foobar, Inc.\n\n", header.toString());
57+
}
58+
}

src/test/java/org/apache/commons/net/smtp/SimpleSMTPHeaderTestCase.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,28 @@ void testToStringAddHeaderDate() {
105105
assertEquals("Date: dummy date\nFrom: from@here.invalid\n\n", header.toString());
106106
}
107107

108+
@Test
109+
void testRejectCarriageReturnInConstructor() {
110+
assertThrows(IllegalArgumentException.class, () -> new SimpleSMTPHeader("from@here.invalid", "to@there.invalid", "Subject\rInjected: header"));
111+
}
112+
113+
@Test
114+
void testRejectLineFeedInAddCC() {
115+
final SimpleSMTPHeader header = new SimpleSMTPHeader("from@here.invalid", null, null);
116+
assertThrows(IllegalArgumentException.class, () -> header.addCC("cc@there.invalid\nBcc: victim@there.invalid"));
117+
}
118+
119+
@Test
120+
void testRejectLineFeedInAddHeaderField() {
121+
final SimpleSMTPHeader header = new SimpleSMTPHeader("from@here.invalid", null, null);
122+
assertThrows(IllegalArgumentException.class, () -> header.addHeaderField("X-Header1", "value 1\nX-Injected: value 2"));
123+
}
124+
125+
@Test
126+
void testRejectLineFeedInConstructor() {
127+
assertThrows(IllegalArgumentException.class, () -> new SimpleSMTPHeader("from@here.invalid", "to@there.invalid", "Subject\nInjected: header"));
128+
}
129+
108130
@Test
109131
void testToStringNoFrom() {
110132
assertThrows(IllegalArgumentException.class, () -> new SimpleSMTPHeader(null, null, null));

0 commit comments

Comments
 (0)