@@ -23,7 +23,9 @@ This file is part of the iText (R) project.
23
23
package com .itextpdf .commons .utils ;
24
24
25
25
import com .itextpdf .commons .exceptions .CommonsExceptionMessageConstant ;
26
+ import com .itextpdf .commons .logs .CommonsLogMessageConstant ;
26
27
28
+ import java .io .BufferedInputStream ;
27
29
import java .io .Closeable ;
28
30
import java .io .IOException ;
29
31
import java .io .InputStream ;
@@ -33,14 +35,21 @@ This file is part of the iText (R) project.
33
35
import java .util .Set ;
34
36
import java .util .zip .ZipEntry ;
35
37
import java .util .zip .ZipFile ;
38
+ import org .slf4j .Logger ;
39
+ import org .slf4j .LoggerFactory ;
36
40
37
41
/**
38
42
* Allows reading entries from a zip file.
39
43
*/
40
44
public class ZipFileReader implements Closeable {
45
+ private static final Logger LOGGER = LoggerFactory .getLogger (ZipFileReader .class );
41
46
42
47
private final ZipFile zipFile ;
43
48
49
+ private int thresholdSize = 1_000_000_000 ;
50
+ private int thresholdEntries = 10000 ;
51
+ private double thresholdRatio = 10 ;
52
+
44
53
/**
45
54
* Creates an instance for zip file reading.
46
55
*
@@ -59,13 +68,49 @@ public ZipFileReader(String archivePath) throws IOException {
59
68
* Get all file entries paths inside the reading zip file.
60
69
*
61
70
* @return the {@link Set} of all file entries paths
71
+ *
72
+ * @throws IOException if some I/O exception occurs
62
73
*/
63
- public Set <String > getFileNames () {
74
+ public Set <String > getFileNames () throws IOException {
64
75
final Set <String > fileNames = new HashSet <>();
65
76
66
77
final Enumeration <? extends ZipEntry > entries = zipFile .entries ();
78
+
79
+ int totalSizeArchive = 0 ;
80
+ int totalEntryArchive = 0 ;
67
81
while (entries .hasMoreElements ()) {
68
82
ZipEntry entry = entries .nextElement ();
83
+ boolean zipBombSuspicious = false ;
84
+ try (InputStream in = new BufferedInputStream (zipFile .getInputStream (entry ))) {
85
+ totalEntryArchive ++;
86
+ int nBytes ;
87
+ byte [] buffer = new byte [2048 ];
88
+ int totalSizeEntry = 0 ;
89
+ while ((nBytes = in .read (buffer )) > 0 ) {
90
+ totalSizeEntry += nBytes ;
91
+ totalSizeArchive += nBytes ;
92
+ double compressionRatio = (double ) totalSizeEntry / entry .getCompressedSize ();
93
+ if (compressionRatio > thresholdRatio ) {
94
+ zipBombSuspicious = true ;
95
+ break ;
96
+ }
97
+ }
98
+ if (zipBombSuspicious ) {
99
+ LOGGER .warn (MessageFormatUtil .format (CommonsLogMessageConstant .RATIO_IS_HIGHLY_SUSPICIOUS ,
100
+ thresholdRatio ));
101
+ break ;
102
+ }
103
+ if (totalSizeArchive > thresholdSize ) {
104
+ LOGGER .warn (MessageFormatUtil .format (CommonsLogMessageConstant .UNCOMPRESSED_DATA_SIZE_IS_TOO_MUCH ,
105
+ thresholdSize ));
106
+ break ;
107
+ }
108
+ if (totalEntryArchive > thresholdEntries ) {
109
+ LOGGER .warn (MessageFormatUtil .format (CommonsLogMessageConstant .TOO_MUCH_ENTRIES_IN_ARCHIVE ,
110
+ thresholdEntries ));
111
+ break ;
112
+ }
113
+ }
69
114
if (!entry .isDirectory ()) {
70
115
fileNames .add (entry .getName ());
71
116
}
@@ -94,6 +139,34 @@ public InputStream readFromZip(String fileName) throws IOException {
94
139
return zipFile .getInputStream (entry );
95
140
}
96
141
142
+ /**
143
+ * Sets the maximum total uncompressed data size to prevent a Zip Bomb Attack. Default value is 1 GB (1000000000).
144
+ *
145
+ * @param thresholdSize the threshold for maximum total size of the uncompressed data
146
+ */
147
+ public void setThresholdSize (int thresholdSize ) {
148
+ this .thresholdSize = thresholdSize ;
149
+ }
150
+
151
+ /**
152
+ * Sets the maximum number of file entries in the archive to prevent a Zip Bomb Attack. Default value is 10000.
153
+ *
154
+ * @param thresholdEntries maximum number of file entries in the archive
155
+ */
156
+ public void setThresholdEntries (int thresholdEntries ) {
157
+ this .thresholdEntries = thresholdEntries ;
158
+ }
159
+
160
+ /**
161
+ * Sets the maximum ratio between compressed and uncompressed data to prevent a Zip Bomb Attack. In general
162
+ * the data compression ratio for most of the legit archives is 1 to 3. Default value is 10.
163
+ *
164
+ * @param thresholdRatio maximum ratio between compressed and uncompressed data
165
+ */
166
+ public void setThresholdRatio (double thresholdRatio ) {
167
+ this .thresholdRatio = thresholdRatio ;
168
+ }
169
+
97
170
@ Override
98
171
public void close () throws IOException {
99
172
zipFile .close ();
0 commit comments