aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/util/ReceptionUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/util/ReceptionUtil.java')
-rw-r--r--plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/util/ReceptionUtil.java103
1 files changed, 84 insertions, 19 deletions
diff --git a/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/util/ReceptionUtil.java b/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/util/ReceptionUtil.java
index c26286da..6b0c762a 100644
--- a/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/util/ReceptionUtil.java
+++ b/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/util/ReceptionUtil.java
@@ -20,9 +20,13 @@
package org.onap.policy.distribution.reception.util;
+import java.io.BufferedInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InvalidClassException;
import java.nio.file.Path;
+import java.util.Enumeration;
+import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.onap.policy.common.utils.coder.CoderException;
@@ -44,25 +48,20 @@ public class ReceptionUtil {
private static final StandardCoder coder = new StandardCoder();
private static final StandardYamlCoder yamlCoder = new StandardYamlCoder();
- private static final long MAX_FILE_SIZE = 512L * 1024;
+ private static final long THRESHOLD_SIZE = 512L * 1024;
+ private static final int THRESHOLD_ENTRIES = 10000;
+ private static final double THRESHOLD_RATIO = 20;
/**
* Method to ensure validation of entries in the Zipfile. Attempts to solve path
* injection java security issues.
*
* @param entryName name of the ZipEntry to check
- * @param csarPath Absolute path to the csar the ZipEntry is in
- * @param entrySize size of the ZipEntry
+ * @param csarPath Absolute path to the csar the ZipEntry is in
* @throws PolicyDecodingException if the file size is too large
*/
- public static void validateZipEntry(String entryName, String csarPath, long entrySize)
- throws PolicyDecodingException {
- //
- // Check file size
- //
- if (entrySize > MAX_FILE_SIZE) {
- throw new PolicyDecodingException("Zip entry for " + entryName + " is too large " + entrySize);
- }
+ public static void validateZipEntry(String entryName, String csarPath)
+ throws PolicyDecodingException {
//
// Now ensure that there is no path injection
//
@@ -78,19 +77,85 @@ public class ReceptionUtil {
/**
* Method to decode either a json or yaml file into an object.
*
- * @param zipFile the zip file
- * @param entry the entry to read in the zip file.
+ * @param zipEntryName the zip file name.
+ * @param entryData the data from entry to decode.
* @return the decoded ToscaServiceTemplate object.
* @throws CoderException IOException if the file decoding fails.
*/
- public static ToscaServiceTemplate decodeFile(ZipFile zipFile, final ZipEntry entry)
- throws IOException, CoderException {
+ public static ToscaServiceTemplate decodeFile(String zipEntryName, InputStream entryData) throws CoderException {
ToscaServiceTemplate toscaServiceTemplate = null;
- if (entry.getName().endsWith(".json")) {
- toscaServiceTemplate = coder.decode(zipFile.getInputStream(entry), ToscaServiceTemplate.class);
- } else if (entry.getName().endsWith(".yml")) {
- toscaServiceTemplate = yamlCoder.decode(zipFile.getInputStream(entry), ToscaServiceTemplate.class);
+ if (zipEntryName.endsWith(".json")) {
+ toscaServiceTemplate = coder.decode(entryData, ToscaServiceTemplate.class);
+ } else if (zipEntryName.endsWith(".yml")) {
+ toscaServiceTemplate = yamlCoder.decode(entryData, ToscaServiceTemplate.class);
}
return toscaServiceTemplate;
}
+
+ /**
+ * Unzip the csar file following the security recommendations from sonar cloud to avoid a zip bomb attack.
+ *
+ * @param csarFilename csar file which is a zip file.
+ * @param unzippedTemplates the templates which should be unzipped.
+ * @param filter contains the keywords for the entry name.
+ * @throws PolicyDecodingException in case zip file can't be read
+ * @throws CoderException in case files can't be decoded to a template
+ */
+ public static void unzip(String csarFilename, Map<String, ToscaServiceTemplate> unzippedTemplates, String... filter)
+ throws PolicyDecodingException, CoderException {
+ try (var zipFile = new ZipFile(csarFilename)) {
+ int totalSizeArchive = 0;
+ int totalEntryArchive = 0;
+
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ totalEntryArchive++;
+
+ if (checkEntryFilter(entry.getName(), filter)) {
+ validateZipEntry(entry.getName(), csarFilename);
+ InputStream entryData = new BufferedInputStream(zipFile.getInputStream(entry));
+
+ int bufferedBytes;
+ byte[] buffer = new byte[2048];
+ int totalSizeEntry = 0;
+
+ boolean isValidThreshold = true;
+ while ((bufferedBytes = entryData.read(buffer)) > 0 && isValidThreshold) {
+ totalSizeEntry += bufferedBytes;
+ totalSizeArchive += bufferedBytes;
+
+ double compressionRatio = Math.floorDiv(totalSizeEntry, entry.getCompressedSize());
+ isValidThreshold = compressionRatio <= THRESHOLD_RATIO;
+ }
+
+ unzippedTemplates.put(entry.getName(), decodeFile(entry.getName(), zipFile.getInputStream(entry)));
+ }
+
+ if (totalSizeArchive > THRESHOLD_SIZE || totalEntryArchive > THRESHOLD_ENTRIES) {
+ // the uncompressed data size is too much for the application resource capacity
+ break;
+ }
+ }
+ } catch (IOException exception) {
+ throw new PolicyDecodingException("Couldn't read the zipFile", exception);
+ }
+ }
+
+ /**
+ * Check if entry is the one to be unzipped based on the filter.
+ *
+ * @param entryName zip file entry name.
+ * @param filter the list of filter to be looked up on entry names.
+ * @return true if entry is in filter, false otherwise.
+ */
+ public static boolean checkEntryFilter(String entryName, String... filter) {
+ for (String f : filter) {
+ if (entryName.contains(f)) {
+ return true;
+ }
+ }
+ return false;
+ }
}