|
20 | 20 | import java.math.RoundingMode; |
21 | 21 | import java.net.URI; |
22 | 22 | import java.util.Locale; |
| 23 | +import java.util.concurrent.atomic.AtomicLong; |
23 | 24 | import java.util.zip.ZipEntry; |
24 | 25 | import java.util.zip.ZipInputStream; |
25 | 26 |
|
|
32 | 33 | import org.w3c.dom.ls.LSResourceResolver; |
33 | 34 |
|
34 | 35 | import com.helger.annotation.Nonempty; |
| 36 | +import com.helger.annotation.Nonnegative; |
35 | 37 | import com.helger.base.enforce.ValueEnforcer; |
36 | 38 | import com.helger.base.io.nonblocking.NonBlockingByteArrayOutputStream; |
37 | 39 | import com.helger.base.numeric.BigHelper; |
@@ -77,13 +79,39 @@ public class DefaultVESLoaderXSD implements IVESLoaderXSD |
77 | 79 | public static final String RESOURCE_TYPE_XSD = "xsd"; |
78 | 80 | public static final String FILE_EXT_XSD = '.' + RESOURCE_TYPE_XSD; |
79 | 81 |
|
| 82 | + /** Default maximum unzipped size: 50 MB */ |
| 83 | + public static final long DEFAULT_MAX_UNZIPPED_SIZE = 50L * 1024 * 1024; |
| 84 | + |
| 85 | + private static final AtomicLong MAX_UNZIPPED_SIZE = new AtomicLong (DEFAULT_MAX_UNZIPPED_SIZE); |
80 | 86 | private static final Logger LOGGER = LoggerFactory.getLogger (DefaultVESLoaderXSD.class); |
81 | 87 |
|
| 88 | + /** |
| 89 | + * @return The maximum allowed unzipped size in bytes. Defaults to |
| 90 | + * {@link #DEFAULT_MAX_UNZIPPED_SIZE}. |
| 91 | + */ |
| 92 | + @Nonnegative |
| 93 | + public static long getMaxUnzippedSize () |
| 94 | + { |
| 95 | + return MAX_UNZIPPED_SIZE.get (); |
| 96 | + } |
| 97 | + |
| 98 | + /** |
| 99 | + * Set the maximum allowed unzipped size in bytes. This is a safeguard against ZIP bomb attacks. |
| 100 | + * |
| 101 | + * @param nMaxUnzippedSize |
| 102 | + * The maximum unzipped size in bytes. Must be > 0. |
| 103 | + */ |
| 104 | + public static void setMaxUnzippedSize (@Nonnegative final long nMaxUnzippedSize) |
| 105 | + { |
| 106 | + ValueEnforcer.isGT0 (nMaxUnzippedSize, "MaxUnzippedSize"); |
| 107 | + MAX_UNZIPPED_SIZE.set (nMaxUnzippedSize); |
| 108 | + } |
| 109 | + |
82 | 110 | @Nullable |
83 | | - private static final String _unifyPath (@Nullable final String x) |
| 111 | + private static final String _unifyPath (@Nullable final String sPath) |
84 | 112 | { |
85 | 113 | // Convert any "\" to "/" |
86 | | - String ret = FilenameHelper.getPathUsingUnixSeparator (x); |
| 114 | + String ret = FilenameHelper.getPathUsingUnixSeparator (sPath); |
87 | 115 | if (ret != null) |
88 | 116 | { |
89 | 117 | // Make absolute to simply LS resource resolving |
@@ -225,13 +253,23 @@ public IValidationExecutor <IValidationSourceXML> loadXSD (@NonNull final IRepoS |
225 | 253 | bFoundMain = true; |
226 | 254 |
|
227 | 255 | // Read ZIP entry |
| 256 | + final long nMaxLen = getMaxUnzippedSize (); |
228 | 257 | try (final NonBlockingByteArrayOutputStream aBAOS = new NonBlockingByteArrayOutputStream ()) |
229 | 258 | { |
230 | 259 | int nLen; |
231 | 260 | while ((nLen = aZIS.read (aBuffer)) > 0) |
232 | 261 | { |
233 | | - aBAOS.write (aBuffer, 0, nLen); |
234 | 262 | nUnzippedLen += nLen; |
| 263 | + if (nUnzippedLen > nMaxLen) |
| 264 | + { |
| 265 | + aErrorList.add (SingleError.builderError () |
| 266 | + .errorText ("XSD ZIP file exceeds maximum unzipped size of " + |
| 267 | + SizeHelper.getSizeHelperOfLocale (Locale.ROOT) |
| 268 | + .getAsMatching (nMaxLen)) |
| 269 | + .build ()); |
| 270 | + return null; |
| 271 | + } |
| 272 | + aBAOS.write (aBuffer, 0, nLen); |
235 | 273 | } |
236 | 274 |
|
237 | 275 | // Remember ZIP entry in map |
|
0 commit comments