Skip to content

Commit be3b160

Browse files
committed
validate CSVFormat invariants when deserializing
1 parent e6614b4 commit be3b160

2 files changed

Lines changed: 28 additions & 0 deletions

File tree

src/main/java/org/apache/commons/csv/CSVFormat.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.io.File;
2525
import java.io.IOException;
2626
import java.io.InputStream;
27+
import java.io.InvalidObjectException;
2728
import java.io.NotActiveException;
2829
import java.io.ObjectInputStream;
2930
import java.io.OutputStream;
@@ -2648,6 +2649,14 @@ private void printWithQuotes(final Reader reader, final Appendable appendable) t
26482649
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
26492650
in.defaultReadObject();
26502651
writeLock = new Object();
2652+
// The constructor validates these invariants, but deserialization bypasses it, so a crafted stream
2653+
// could yield a format the Builder rejects (for example the quote char equal to the delimiter, or
2654+
// QuoteMode.NONE with no escape char) that then misparses or throws in a print/parse callee.
2655+
try {
2656+
validate();
2657+
} catch (final IllegalArgumentException e) {
2658+
throw (InvalidObjectException) new InvalidObjectException(e.getMessage()).initCause(e);
2659+
}
26512660
}
26522661

26532662
@Override

src/test/java/org/apache/commons/csv/CSVFormatTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@
3838
import java.io.ByteArrayInputStream;
3939
import java.io.ByteArrayOutputStream;
4040
import java.io.IOException;
41+
import java.io.InvalidObjectException;
4142
import java.io.ObjectInputStream;
4243
import java.io.ObjectOutputStream;
4344
import java.io.Reader;
4445
import java.io.StringReader;
46+
import java.lang.reflect.Field;
4547
import java.lang.reflect.Method;
4648
import java.lang.reflect.Modifier;
4749
import java.sql.ResultSet;
@@ -1158,6 +1160,23 @@ void testSerialization() throws Exception {
11581160
assertEquals(CSVFormat.DEFAULT.getIgnoreEmptyLines(), format.getIgnoreEmptyLines(), "empty lines");
11591161
}
11601162

1163+
@Test
1164+
void testSerializationRejectsInvalidInvariant() throws Exception {
1165+
// A format the Builder rejects: QuoteMode.NONE with no escape character. Reflection corrupts a fresh
1166+
// instance to build a serialized stream a hostile source could send.
1167+
final CSVFormat format = CSVFormat.DEFAULT.builder().get();
1168+
final Field quoteModeField = CSVFormat.class.getDeclaredField("quoteMode");
1169+
quoteModeField.setAccessible(true);
1170+
quoteModeField.set(format, QuoteMode.NONE);
1171+
final ByteArrayOutputStream out = new ByteArrayOutputStream();
1172+
try (ObjectOutputStream oos = new ObjectOutputStream(out)) {
1173+
oos.writeObject(format);
1174+
}
1175+
try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()))) {
1176+
assertThrows(InvalidObjectException.class, in::readObject);
1177+
}
1178+
}
1179+
11611180
@Test
11621181
void testToString() {
11631182

0 commit comments

Comments
 (0)