Defensively copy sentDate in SimpleMailMessage

Use defensive Date copies for sentDate to avoid shared mutable state.

Apply consistent handling in setSentDate, getSentDate, the copy constructor, and copyTo.

Add regression tests for mutation safety and copy isolation.

Closes gh-36626

Signed-off-by: Junseo Bae <ferrater1013@gmail.com>
This commit is contained in:
Junseo Bae
2026-04-08 23:07:33 +09:00
committed by GitHub
parent e940a38014
commit 4b0101a9dc
2 changed files with 118 additions and 5 deletions
@@ -79,7 +79,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
this.to = copyOrNull(original.getTo());
this.cc = copyOrNull(original.getCc());
this.bcc = copyOrNull(original.getBcc());
this.sentDate = original.getSentDate();
this.sentDate = copyOrNull(original.sentDate);
this.subject = original.getSubject();
this.text = original.getText();
}
@@ -147,11 +147,11 @@ public class SimpleMailMessage implements MailMessage, Serializable {
@Override
public void setSentDate(@Nullable Date sentDate) {
this.sentDate = sentDate;
this.sentDate = copyOrNull(sentDate);
}
public @Nullable Date getSentDate() {
return this.sentDate;
return copyOrNull(this.sentDate);
}
@Override
@@ -194,8 +194,9 @@ public class SimpleMailMessage implements MailMessage, Serializable {
if (getBcc() != null) {
target.setBcc(copy(getBcc()));
}
if (getSentDate() != null) {
target.setSentDate(getSentDate());
if (this.sentDate != null) {
Date sentDate = this.sentDate;
target.setSentDate((Date) sentDate.clone());
}
if (getSubject() != null) {
target.setSubject(getSubject());
@@ -247,6 +248,10 @@ public class SimpleMailMessage implements MailMessage, Serializable {
return copy(state);
}
private static @Nullable Date copyOrNull(@Nullable Date date) {
return (date != null ? (Date) date.clone() : null);
}
private static String[] copy(String[] state) {
return state.clone();
}
@@ -98,6 +98,60 @@ class SimpleMailMessageTests {
assertThat(copy.getBcc()[0]).isEqualTo("us@mail.org");
}
@Test
void setSentDateStoresACopy() {
SimpleMailMessage message = new SimpleMailMessage();
Date sentDate = new Date(1234L);
message.setSentDate(sentDate);
sentDate.setTime(0L);
assertThat(message.getSentDate()).isEqualTo(new Date(1234L));
}
@Test
void getSentDateReturnsACopy() {
SimpleMailMessage message = new SimpleMailMessage();
Date sentDate = new Date(1234L);
message.setSentDate(sentDate);
Date exportedDate = message.getSentDate();
exportedDate.setTime(0L);
assertThat(message.getSentDate()).isEqualTo(new Date(1234L));
}
@Test
void copyConstructorCopiesSentDate() {
Date sentDate = new Date(1234L);
SimpleMailMessage original = new SimpleMailMessage();
original.setSentDate(sentDate);
SimpleMailMessage copy = new SimpleMailMessage(original);
sentDate.setTime(0L);
Date copiedDate = copy.getSentDate();
assertThat(copiedDate).isNotNull();
copiedDate.setTime(1L);
assertThat(original.getSentDate()).isEqualTo(new Date(1234L));
assertThat(copy.getSentDate()).isEqualTo(new Date(1234L));
}
@Test
void copyToCopiesSentDate() {
SimpleMailMessage source = new SimpleMailMessage();
source.setSentDate(new Date(1234L));
TestMailMessage target = new TestMailMessage();
source.copyTo(target);
assertThat(target.getSentDate()).isNotNull();
target.getSentDate().setTime(0L);
assertThat(source.getSentDate()).isEqualTo(new Date(1234L));
}
/**
* Tests that two equal SimpleMailMessages have equal hash codes.
*/
@@ -166,4 +220,58 @@ class SimpleMailMessageTests {
assertThatIllegalArgumentException().isThrownBy(() -> new SimpleMailMessage().copyTo(null));
}
private static class TestMailMessage implements MailMessage {
private Date sentDate;
Date getSentDate() {
return this.sentDate;
}
@Override
public void setFrom(String from) {
}
@Override
public void setReplyTo(String replyTo) {
}
@Override
public void setTo(String to) {
}
@Override
public void setTo(String... to) {
}
@Override
public void setCc(String cc) {
}
@Override
public void setCc(String... cc) {
}
@Override
public void setBcc(String bcc) {
}
@Override
public void setBcc(String... bcc) {
}
@Override
public void setSentDate(Date sentDate) {
this.sentDate = sentDate;
}
@Override
public void setSubject(String subject) {
}
@Override
public void setText(String text) {
}
}
}