|
18 | 18 |
|
19 | 19 | package com.dtstack.taier.common.util; |
20 | 20 |
|
| 21 | +import com.dtstack.taier.common.exception.TaierDefineException; |
21 | 22 | import org.apache.tools.zip.ZipFile; |
22 | 23 | import org.slf4j.Logger; |
23 | 24 | import org.slf4j.LoggerFactory; |
@@ -54,6 +55,12 @@ public class ZipUtil { |
54 | 55 |
|
55 | 56 | private static byte[] _byte = new byte[1024]; |
56 | 57 |
|
| 58 | + private static final int MAX_ZIP_ENTRY_COUNT = 1000; |
| 59 | + private static final int MAX_ZIP_RECURSION_DEPTH = 3; |
| 60 | + private static final long MAX_ZIP_TOTAL_UNCOMPRESSED_SIZE = 100L * 1024 * 1024; |
| 61 | + private static final long MAX_ZIP_ENTRY_UNCOMPRESSED_SIZE = 50L * 1024 * 1024; |
| 62 | + private static final long MAX_ZIP_COMPRESSION_RATIO = 100L; |
| 63 | + |
57 | 64 | public static byte[] compress(byte[] rowData) { |
58 | 65 | byte[] backData = null; |
59 | 66 | ZipOutputStream zip = null; |
@@ -224,68 +231,124 @@ public static List<File> upzipFile(String zipPath, String descDir) { |
224 | 231 | */ |
225 | 232 | @SuppressWarnings("rawtypes") |
226 | 233 | public static List<File> upzipFile(File zipFile, String descDir) { |
| 234 | + try { |
| 235 | + return upzipFile(zipFile, descDir, new UnzipContext(), 0); |
| 236 | + } catch (IOException e) { |
| 237 | + throw new TaierDefineException(String.format("Unzip exception : %s", e.getMessage()), e); |
| 238 | + } |
| 239 | + } |
| 240 | + |
| 241 | + @SuppressWarnings("rawtypes") |
| 242 | + private static List<File> upzipFile(File zipFile, String descDir, UnzipContext context, int depth) throws IOException { |
| 243 | + if (depth > MAX_ZIP_RECURSION_DEPTH) { |
| 244 | + throw new IOException(String.format("zip recursion depth exceeds limit: %s", MAX_ZIP_RECURSION_DEPTH)); |
| 245 | + } |
227 | 246 | List<File> _list = new ArrayList<>(); |
| 247 | + File baseDir = new File(descDir); |
| 248 | + String basePath = getCanonicalDirPath(baseDir); |
228 | 249 | ZipFile _zipFile = null; |
229 | | - OutputStream _out = null; |
230 | | - InputStream _in = null; |
231 | 250 | try { |
232 | 251 | _zipFile = new ZipFile(zipFile, "GBK"); |
233 | 252 | for (Enumeration entries = _zipFile.getEntries(); entries.hasMoreElements(); ) { |
234 | 253 | org.apache.tools.zip.ZipEntry entry = (org.apache.tools.zip.ZipEntry) entries.nextElement(); |
235 | | - File _file = new File(descDir + File.separator + entry.getName()); |
| 254 | + context.addEntry(entry.getName()); |
| 255 | + File _file = resolveZipEntryFile(baseDir, basePath, entry.getName()); |
236 | 256 | if (_file.isHidden()) { |
237 | 257 | continue; |
238 | 258 | } |
239 | 259 | if (entry.isDirectory()) { |
240 | | - _file.mkdirs(); |
| 260 | + makeDirs(_file); |
241 | 261 | } else { |
242 | 262 | File _parent = _file.getParentFile(); |
243 | | - if (!_parent.exists()) { |
244 | | - _parent.mkdirs(); |
245 | | - } |
246 | | - _in = _zipFile.getInputStream(entry); |
247 | | - _out = new FileOutputStream(_file); |
| 263 | + makeDirs(_parent); |
248 | 264 | byte[] buffer = new byte[4]; |
249 | | - int length = _in.read(buffer, 0, 4); |
250 | | - int len = 0; |
251 | | - _out.write(buffer); |
252 | | - while ((len = _in.read(_byte)) > 0) { |
253 | | - _out.write(_byte, 0, len); |
| 265 | + byte[] fileBuffer = new byte[1024]; |
| 266 | + int length; |
| 267 | + try (InputStream _in = _zipFile.getInputStream(entry); |
| 268 | + OutputStream _out = new FileOutputStream(_file)) { |
| 269 | + length = _in.read(buffer, 0, 4); |
| 270 | + long written = 0L; |
| 271 | + if (length > 0) { |
| 272 | + _out.write(buffer, 0, length); |
| 273 | + written += length; |
| 274 | + context.addUncompressedSize(length); |
| 275 | + validateEntrySize(entry, written); |
| 276 | + } |
| 277 | + int len = 0; |
| 278 | + while ((len = _in.read(fileBuffer)) > 0) { |
| 279 | + _out.write(fileBuffer, 0, len); |
| 280 | + written += len; |
| 281 | + context.addUncompressedSize(len); |
| 282 | + validateEntrySize(entry, written); |
| 283 | + } |
| 284 | + _out.flush(); |
254 | 285 | } |
255 | | - _out.flush(); |
256 | 286 |
|
257 | 287 | if (length == 4 && (Arrays.equals(ZIP_HEADER_1, buffer) || Arrays.equals(ZIP_HEADER_2, buffer))) { |
258 | | - _list.addAll(upzipFile(_file, _file.getPath() + "tmp")); |
| 288 | + _list.addAll(upzipFile(_file, _file.getPath() + "tmp", context, depth + 1)); |
259 | 289 | } else { |
260 | 290 | _list.add(_file); |
261 | 291 | } |
262 | 292 |
|
263 | 293 | } |
264 | 294 | } |
265 | | - } catch (IOException e) { |
266 | 295 | } finally { |
267 | | - if (_out != null) { |
268 | | - try { |
269 | | - _out.close(); |
270 | | - } catch (IOException e) { |
271 | | - } |
272 | | - } |
273 | | - if (_in != null) { |
274 | | - try { |
275 | | - _in.close(); |
276 | | - } catch (IOException e) { |
277 | | - } |
278 | | - } |
279 | 296 | if (_zipFile != null) { |
280 | | - try { |
281 | | - _zipFile.close(); |
282 | | - } catch (IOException e) { |
283 | | - } |
| 297 | + _zipFile.close(); |
284 | 298 | } |
285 | 299 | } |
286 | 300 | return _list; |
287 | 301 | } |
288 | 302 |
|
| 303 | + private static File resolveZipEntryFile(File baseDir, String basePath, String entryName) throws IOException { |
| 304 | + File targetFile = new File(baseDir, entryName); |
| 305 | + String targetPath = targetFile.getCanonicalPath(); |
| 306 | + if (!targetPath.equals(basePath) && !targetPath.startsWith(basePath + File.separator)) { |
| 307 | + throw new IOException(String.format("zip entry is outside of target dir: %s", entryName)); |
| 308 | + } |
| 309 | + return targetFile; |
| 310 | + } |
| 311 | + |
| 312 | + private static String getCanonicalDirPath(File dir) throws IOException { |
| 313 | + makeDirs(dir); |
| 314 | + return dir.getCanonicalPath(); |
| 315 | + } |
| 316 | + |
| 317 | + private static void makeDirs(File dir) throws IOException { |
| 318 | + if (dir != null && !dir.exists() && !dir.mkdirs()) { |
| 319 | + throw new IOException(String.format("failed to create directory: %s", dir)); |
| 320 | + } |
| 321 | + } |
| 322 | + |
| 323 | + private static void validateEntrySize(org.apache.tools.zip.ZipEntry entry, long written) throws IOException { |
| 324 | + if (written > MAX_ZIP_ENTRY_UNCOMPRESSED_SIZE) { |
| 325 | + throw new IOException(String.format("zip entry size exceeds limit: %s", entry.getName())); |
| 326 | + } |
| 327 | + long compressedSize = entry.getCompressedSize(); |
| 328 | + if (compressedSize > 0 && written > compressedSize * MAX_ZIP_COMPRESSION_RATIO) { |
| 329 | + throw new IOException(String.format("zip entry compression ratio exceeds limit: %s", entry.getName())); |
| 330 | + } |
| 331 | + } |
| 332 | + |
| 333 | + private static class UnzipContext { |
| 334 | + private int entryCount; |
| 335 | + private long totalUncompressedSize; |
| 336 | + |
| 337 | + private void addEntry(String entryName) throws IOException { |
| 338 | + entryCount++; |
| 339 | + if (entryCount > MAX_ZIP_ENTRY_COUNT) { |
| 340 | + throw new IOException(String.format("zip entry count exceeds limit: %s", entryName)); |
| 341 | + } |
| 342 | + } |
| 343 | + |
| 344 | + private void addUncompressedSize(long size) throws IOException { |
| 345 | + totalUncompressedSize += size; |
| 346 | + if (totalUncompressedSize > MAX_ZIP_TOTAL_UNCOMPRESSED_SIZE) { |
| 347 | + throw new IOException("zip total uncompressed size exceeds limit"); |
| 348 | + } |
| 349 | + } |
| 350 | + } |
| 351 | + |
289 | 352 | /** |
290 | 353 | * 对临时生成的文件夹和文件夹下的文件进行删除 |
291 | 354 | */ |
|
0 commit comments