24
24
public class CsvParser
25
25
extends ParserMinimalBase
26
26
{
27
- /**
27
+ // @since 2.9.9: just to protect against bugs, DoS, limit number of column defs we may read
28
+ private final static int MAX_COLUMNS = 99999 ;
29
+
30
+ /**
28
31
* Enumeration that defines all togglable features for CSV parsers
29
32
*/
30
33
public enum Feature
@@ -351,7 +354,10 @@ private Feature(boolean defaultState) {
351
354
public CsvParser (CsvIOContext ctxt , int stdFeatures , int csvFeatures ,
352
355
ObjectCodec codec , Reader reader )
353
356
{
354
- super (stdFeatures );
357
+ super (stdFeatures );
358
+ if (reader == null ) {
359
+ throw new IllegalArgumentException ("Can not pass `null` as `java.io.Reader` to read from" );
360
+ }
355
361
_objectCodec = codec ;
356
362
_textBuffer = ctxt .csvTextBuffer ();
357
363
DupDetector dups = JsonParser .Feature .STRICT_DUPLICATE_DETECTION .enabledIn (stdFeatures )
@@ -720,17 +726,22 @@ protected void _readHeaderLine() throws IOException {
720
726
if ((name = _reader .nextString ()) != null ) {
721
727
_reportError (String .format ("Extra header %s" , name ));
722
728
}
723
- }
724
- else {
725
- //noinspection StatementWithEmptyBody
726
- while (_reader .nextString () != null ) { /* does nothing */ }
729
+ } else {
730
+ int allowed = MAX_COLUMNS ;
731
+ while (_reader .nextString () != null ) {
732
+ // If we don't care about validation, just skip. But protect against infinite loop
733
+ if (--allowed < 0 ) {
734
+ _reportError ("Internal error: skipped " +MAX_COLUMNS +" header columns" );
735
+ }
736
+ }
727
737
}
728
738
return ;
729
739
}
730
740
731
741
// either the schema is empty or reorder columns flag is set
732
742
String name ;
733
743
CsvSchema .Builder builder = _schema .rebuild ().clearColumns ();
744
+ int count = 0 ;
734
745
735
746
while ((name = _reader .nextString ()) != null ) {
736
747
// one more thing: always trim names, regardless of config settings
@@ -743,6 +754,9 @@ protected void _readHeaderLine() throws IOException {
743
754
} else {
744
755
builder .addColumn (name );
745
756
}
757
+ if (++count > MAX_COLUMNS ) {
758
+ _reportError ("Internal error: reached maximum of " +MAX_COLUMNS +" header columns" );
759
+ }
746
760
}
747
761
748
762
// Ok: did we get any columns?
@@ -777,9 +791,8 @@ protected JsonToken _handleStartDoc() throws IOException
777
791
_reader .skipLeadingComments ();
778
792
}
779
793
780
- /* Only one real complication, actually; empy documents (zero bytes).
781
- * Those have no entries. Should be easy enough to detect like so:
782
- */
794
+ // Only one real complication, actually; empty documents (zero bytes).
795
+ // Those have no entries. Should be easy enough to detect like so:
783
796
final boolean wrapAsArray = Feature .WRAP_AS_ARRAY .enabledIn (_formatFeatures );
784
797
if (!_reader .hasMoreInput ()) {
785
798
_state = STATE_DOC_END ;
0 commit comments