diff --git a/core/src/main/java/oracle/weblogic/deploy/util/FileUtils.java b/core/src/main/java/oracle/weblogic/deploy/util/FileUtils.java index c12f57f5b..9b0b12da1 100644 --- a/core/src/main/java/oracle/weblogic/deploy/util/FileUtils.java +++ b/core/src/main/java/oracle/weblogic/deploy/util/FileUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle Corporation and/or its affiliates. + * Copyright (c) 2017, 2022, Oracle Corporation and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ package oracle.weblogic.deploy.util; @@ -8,7 +8,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; @@ -24,8 +23,6 @@ import java.util.List; import java.util.Locale; import java.util.Set; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; import javax.xml.bind.DatatypeConverter; import oracle.weblogic.deploy.exception.ExceptionHelper; @@ -617,75 +614,10 @@ public static byte[] readInputStreamToByteArray(InputStream input) throws IOExce return outputStream.toByteArray(); } - public static File writeInputStreamToFile(InputStream input, String fileName) throws IOException { - File tmpdir = getTmpDir(); - File file = new File(tmpdir, fileName); - try (FileOutputStream fos = new FileOutputStream(file)) { - byte[] byteArray = FileUtils.readInputStreamToByteArray(input); - fos.write(byteArray); - } - return file; - } - public static File getTmpDir() { return new File(System.getProperty("java.io.tmpdir")); } - public static void extractZipFileContent(WLSDeployArchive archiveFile, String zipEntry, String extractPath) { - final String METHOD = "extractZipFileContent"; - - try { - if (zipEntry != null) { - File extractDir = new File(extractPath); - extractDir.mkdirs(); - String walletZip = archiveFile.extractFile(zipEntry, - Files.createTempDirectory("tempwallet").toFile()); - - if (!Files.exists(Paths.get(extractPath))) { - Files.createDirectory(Paths.get(extractPath)); - } - - // verify that each target file is under the extract directory, - // to protect from the file overwrite security vulnerability (zip slip). - String canonicalExtractPath = extractDir.getCanonicalPath(); - - byte[] buffer = new byte[1024]; - FileInputStream fis = new FileInputStream(walletZip); - ZipInputStream zis = new ZipInputStream(fis); - ZipEntry ze = zis.getNextEntry(); - while (ze != null) { - String fileName = ze.getName(); - File newFile = new File(extractPath + File.separator + fileName); - String canonicalNewFile = newFile.getCanonicalPath(); - if(!canonicalNewFile.startsWith(canonicalExtractPath + File.separator)) { - throw new WLSDeployArchiveIOException("WLSDPLY-01119", ze.getName()); - } - - new File(newFile.getParent()).mkdirs(); - FileOutputStream fos = new FileOutputStream(newFile); - int len = zis.read(buffer); - while (len > 0) { - fos.write(buffer, 0, len); - len = zis.read(buffer); - } - fos.close(); - zis.closeEntry(); - ze = zis.getNextEntry(); - } - zis.closeEntry(); - zis.close(); - fis.close(); - Files.delete(Paths.get(walletZip)); - } - } catch (IOException | WLSDeployArchiveIOException ioe) { - String message = ExceptionHelper.getMessage("WLSDPLY-01118", archiveFile.getArchiveFileName(), - ioe.getLocalizedMessage()); - IllegalArgumentException iae = new IllegalArgumentException(message); - LOGGER.throwing(CLASS, METHOD, iae); - throw iae; - } - } - /** * Return a PrintWriter instance for the provided file name. * @param fileName Name of output file diff --git a/core/src/main/java/oracle/weblogic/deploy/util/WLSDeployArchive.java b/core/src/main/java/oracle/weblogic/deploy/util/WLSDeployArchive.java index 07804615b..b2e4ef571 100644 --- a/core/src/main/java/oracle/weblogic/deploy/util/WLSDeployArchive.java +++ b/core/src/main/java/oracle/weblogic/deploy/util/WLSDeployArchive.java @@ -11,6 +11,7 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.file.Files; import java.security.NoSuchAlgorithmException; import java.util.List; import java.util.Map; @@ -33,20 +34,26 @@ public class WLSDeployArchive { public static final String WLSDPLY_ARCHIVE_BINARY_DIR = "wlsdeploy"; /** - * Top-level archive subdirectory where the config - * will be extracted. + * Top-level archive subdirectory where the config will be extracted. */ public static final String ARCHIVE_CONFIG_TARGET_DIR = WLSDPLY_ARCHIVE_BINARY_DIR + "/config"; + // Deprecated top-level archive subdirectory where the atp wallet is stored. + public static final String OLD_ARCHIVE_ATP_WALLET_PATH = "atpwallet"; + + // Deprecated top-level archive subdirectory where the opss wallet is stored. + public static final String OLD_ARCHIVE_OPSS_WALLET_PATH = "opsswallet"; + /** - * Top-level archive subdirectory where the atp wallet is stored + * Top-level archive subdirectory where the atp wallet is stored. */ - public static final String ARCHIVE_ATP_WALLET_PATH = "atpwallet"; + public static final String ARCHIVE_ATP_WALLET_PATH = WLSDPLY_ARCHIVE_BINARY_DIR + "/atpwallet"; /** - * Top-level archive subdirectory where the opss wallet is stored + * Top-level archive subdirectory where the opss wallet is stored. */ - public static final String ARCHIVE_OPSS_WALLET_PATH = "opsswallet"; + public static final String ARCHIVE_OPSS_WALLET_PATH = WLSDPLY_ARCHIVE_BINARY_DIR + "/opsswallet"; + /** * Top-level archive subdirectory where the model is stored and the subdirectory to which it will be extracted. */ @@ -129,7 +136,8 @@ public class WLSDeployArchive { */ public static final String ARCHIVE_JMS_FOREIGN_SERVER_DIR = ARCHIVE_JMS_DIR + "/foreignServer"; - public enum ArchiveEntryType { SHARED_LIBRARIES, APPLICATIONS, + public enum ArchiveEntryType { + SHARED_LIBRARIES, APPLICATIONS, APPLICATION_PLAN, SHLIB_PLAN, DOMAIN_LIB, @@ -179,17 +187,6 @@ public WLSDeployArchive(String archiveFileName) { LOGGER.exiting(CLASS, METHOD); } - /** - * Constructor to create an archive file instance for no generation of an archive file. - */ - private WLSDeployArchive() { - final String METHOD = ""; - LOGGER.entering(CLASS, METHOD); - LOGGER.exiting(CLASS, METHOD); - } - - public static WLSDeployArchive noArchiveFile() {return new WLSDeployArchive();} - /** * Determine whether or not the specified path string is a valid archive location. * @@ -202,8 +199,9 @@ public static boolean isPathIntoArchive(String path) { LOGGER.entering(CLASS, METHOD, path); boolean result = false; if (!StringUtils.isEmpty(path)) { - result = path.startsWith(WLSDPLY_ARCHIVE_BINARY_DIR + ZIP_SEP) || path - .startsWith(ARCHIVE_ATP_WALLET_PATH + ZIP_SEP) || path.startsWith(ARCHIVE_OPSS_WALLET_PATH + ZIP_SEP); + result = path.startsWith(WLSDPLY_ARCHIVE_BINARY_DIR + ZIP_SEP) + || path.startsWith(OLD_ARCHIVE_ATP_WALLET_PATH + ZIP_SEP) + || path.startsWith(OLD_ARCHIVE_OPSS_WALLET_PATH + ZIP_SEP); } LOGGER.exiting(CLASS, METHOD, result); return result; @@ -298,7 +296,7 @@ public File extractModel(File modelDirectory) throws WLSDeployArchiveIOException } /** - * Determines whether or not the archive contains a model file. + * Determines whether the archive contains a model file. * * @return true if the archive contains a model file, false otherwise * @throws WLSDeployArchiveIOException if an error occurs while reading the archive @@ -581,6 +579,7 @@ public String getApplicationArchivePath(String appPath) { /** * Get the archive path for the application in a well-formed application directory + * * @param appPath name of the application path * @return archive path for use in the model */ @@ -625,7 +624,7 @@ public String replaceApplication(String appPath, String tempFile) throws WLSDepl } public String addApplicationFolder(String appName, String appPath) - throws WLSDeployArchiveIOException { + throws WLSDeployArchiveIOException { final String METHOD = "addApplicationFolder"; LOGGER.entering(CLASS, METHOD, appName, appPath); File zipPath = new File(appPath); @@ -639,7 +638,7 @@ public String addApplicationFolder(String appName, String appPath) } public String addApplicationPlanFolder(String appName, String planDir) - throws WLSDeployArchiveIOException { + throws WLSDeployArchiveIOException { final String METHOD = "addApplicationPathFolder"; LOGGER.entering(CLASS, METHOD, appName, planDir); File zipPlan = new File(planDir); @@ -668,43 +667,75 @@ public List listApplications() throws WLSDeployArchiveIOException { } /** - * Get the path of the ATP wallet in the archive. + * Extract the ATP wallet in the archive. * - * @return path of the ATP wallet - * @throws WLSDeployArchiveIOException if an error occurs reading the archive + * @param domainHome the domain home directory + * @return the full path to the directory containing the extracted wallet files or null, if no wallet was found. + * @throws WLSDeployArchiveIOException if an error occurs while reading or extracting the archive files. */ - public String getATPWallet() throws WLSDeployArchiveIOException { - final String METHOD = "getATPWallet"; + public String extractATPWallet(File domainHome) throws WLSDeployArchiveIOException { + final String METHOD = "extractATPWallet"; - LOGGER.entering(CLASS, METHOD); - List result = getZipFile().listZipEntries(ARCHIVE_ATP_WALLET_PATH + ZIP_SEP); - result.remove(ARCHIVE_ATP_WALLET_PATH + ZIP_SEP); - LOGGER.exiting(CLASS, METHOD, result); - if (result.isEmpty()) { - return null; + LOGGER.entering(CLASS, METHOD, domainHome); + validateExistingDirectory(domainHome, "domainHome", getArchiveFileName(), METHOD); + + // Look in the updated location first + String extractPath = null; + List zipEntries = getZipFile().listZipEntries(ARCHIVE_ATP_WALLET_PATH + ZIP_SEP); + zipEntries.remove(ARCHIVE_ATP_WALLET_PATH + ZIP_SEP); + if (!zipEntries.isEmpty()) { + extractPath = ARCHIVE_ATP_WALLET_PATH + ZIP_SEP; + extractWallet(domainHome, extractPath, zipEntries, null); + extractPath = new File(domainHome, extractPath).getAbsolutePath(); } else { - return result.get(0); + // Look in the deprecated location. + zipEntries = getZipFile().listZipEntries(OLD_ARCHIVE_ATP_WALLET_PATH + ZIP_SEP); + zipEntries.remove(OLD_ARCHIVE_ATP_WALLET_PATH + ZIP_SEP); + if (!zipEntries.isEmpty()) { + extractPath = ARCHIVE_ATP_WALLET_PATH + ZIP_SEP; + extractWallet(domainHome, extractPath, zipEntries, "WLSDPLY-01427"); + extractPath = new File(domainHome, extractPath).getAbsolutePath(); + } } + + LOGGER.exiting(CLASS, METHOD, extractPath); + return extractPath; } /** - * Get the path of the OPSS wallet in the archive. + * Extract the OPSS wallet from the archive. * - * @return path of the OPSS wallet - * @throws WLSDeployArchiveIOException if an error occurs reading the archive + * @param domainHome the domain home directory + * @return the full path to the directory containing the extracted wallet files or null, if no wallet was found. + * @throws WLSDeployArchiveIOException if an error occurs while reading or extracting the archive files. */ - public String getOPSSWallet() throws WLSDeployArchiveIOException { - final String METHOD = "getOPSSWallet"; + public String extractOPSSWallet(File domainHome) throws WLSDeployArchiveIOException { + final String METHOD = "extractOPSSWallet"; - LOGGER.entering(CLASS, METHOD); - List result = getZipFile().listZipEntries(ARCHIVE_OPSS_WALLET_PATH + ZIP_SEP); - result.remove(ARCHIVE_OPSS_WALLET_PATH + ZIP_SEP); - LOGGER.exiting(CLASS, METHOD, result); - if (result.isEmpty()) { - return null; + LOGGER.entering(CLASS, METHOD, domainHome); + validateExistingDirectory(domainHome, "domainHome", getArchiveFileName(), METHOD); + + // Look in the updated location first + String extractPath = null; + List zipEntries = getZipFile().listZipEntries(ARCHIVE_OPSS_WALLET_PATH + ZIP_SEP); + zipEntries.remove(ARCHIVE_OPSS_WALLET_PATH + ZIP_SEP); + if (!zipEntries.isEmpty()) { + extractPath = ARCHIVE_OPSS_WALLET_PATH + ZIP_SEP; + extractWallet(domainHome, extractPath, zipEntries, null); + extractPath = new File(domainHome, extractPath).getAbsolutePath(); } else { - return result.get(0); + // Look in the deprecated location. + zipEntries = getZipFile().listZipEntries(OLD_ARCHIVE_OPSS_WALLET_PATH + ZIP_SEP); + zipEntries.remove(OLD_ARCHIVE_OPSS_WALLET_PATH + ZIP_SEP); + if (!zipEntries.isEmpty()) { + extractPath = OLD_ARCHIVE_OPSS_WALLET_PATH + ZIP_SEP; + extractWallet(domainHome, extractPath, zipEntries, "WLSDPLY-01433"); + extractPath = new File(domainHome, extractPath).getAbsolutePath(); + } } + + LOGGER.exiting(CLASS, METHOD, extractPath); + return extractPath; } /** @@ -736,6 +767,7 @@ public void extractApplication(String applicationPath, File domainHome) throws W /** * Get the best guess of the name of the shared library as if it is in the archive file. * This does not reconcile duplicate names and other items that require the archive file. + * * @param shlibPath file name to find the name for * @return name for model archive file name */ @@ -809,6 +841,7 @@ public void extractSharedLibrary(String sharedLibraryPath, File domainHome) thro /** * Get the archive file name for the Domain library file. This does not reconcile duplicate names or other * items that require the archive file. + * * @param domainLibPath the file name to get the archive file name * @return model ready archive file name */ @@ -954,6 +987,7 @@ public void removeDomainBinScripts() throws WLSDeployArchiveIOException { /** * Get the archive path for the classpath library for use in the model. + * * @param libPath to get the archive path for * @return Archive path for the classpath library for use in the model */ @@ -1055,6 +1089,7 @@ public void extractCustomFiles(File domainHome) throws WLSDeployArchiveIOExcepti /** * Get the archive path of the application deployment plan. + * * @param planFile The deployment plan file name * @return Archive path for use in the model */ @@ -1099,6 +1134,7 @@ public String addApplicationDeploymentPlan(String planFile, String preferredName /** * Get the Archive Path for the Shared Library Plan + * * @param planFile Shared Library Deployment Plan file name * @return Archive path for the plan file for use in the model */ @@ -1132,6 +1168,7 @@ public String addSharedLibraryDeploymentPlan(String planFile, String preferredNa /** * Get the archive path for the scriptfile name. + * * @param scriptFile the script file to get the path name * @return The name of the file in the archive for use in the model */ @@ -1161,7 +1198,8 @@ public String addScript(String scriptFile) throws WLSDeployArchiveIOException { /** * Get the archive path for the servr identity key store file for use in the model - * @param serverName name of the server used to separate paths + * + * @param serverName name of the server used to separate paths * @param keystoreFile the file to get the archive path name * @return Archive path name for the server key store file */ @@ -1225,7 +1263,7 @@ public String addMimeMappingFile(String mimeMappingFile) throws WLSDeployArchive * Get the Coherence configuration file name in the archive to use in the model. * * @param clusterName The Coherence cluster name used to segregate the directories - * @param configFile the file name of the config file + * @param configFile the file name of the config file * @return Archive name for use in the model */ public String getCoherenceConfigArchivePath(String clusterName, String configFile) { @@ -1256,8 +1294,9 @@ public String addCoherenceConfigFile(String clusterName, String configFile) thro /** * Get the archive name for the foreign server binding file. + * * @param foreignServer The foreign server name used to segregate the directories - * @param configFile The file name to add + * @param configFile The file name to add * @return The location of the file in the archive to use in the model */ public String getForeignServerArchivePath(String foreignServer, String configFile) { @@ -1268,7 +1307,7 @@ public String getForeignServerArchivePath(String foreignServer, String configFil * Add a Foreign Server binding file to the archive * * @param foreignServer the Foreign Server name used to segregate the directories - * @param configFile the file or directory to add + * @param configFile the file or directory to add * @return the new location of the file to use in the model * @throws WLSDeployArchiveIOException if an error occurs while archiving the file * @throws IllegalArgumentException if the file does not exist or the foreignServer is empty or null @@ -1287,7 +1326,7 @@ public String addForeignServerFile(String foreignServer, String configFile) thro public String getCoherenceURLArchivePath(String clusterName, URL urlForConfigFile) { return getURLArchiveName(ARCHIVE_COHERENCE_TARGET_DIR + ZIP_SEP + clusterName, urlForConfigFile, - true); + true); } /** @@ -1307,7 +1346,7 @@ public String addCoherenceConfigFileFromUrl(String clusterName, URL urlForConfig validateNonEmptyString(clusterName, "clusterName", METHOD); validateNonNullObject(urlForConfigFile, "urlForConfigFile", METHOD); String newName = addUrlToZip(ARCHIVE_COHERENCE_TARGET_DIR + ZIP_SEP + clusterName, urlForConfigFile, - COHERENCE_CONFIG_FILE_EXTENSION, true); + COHERENCE_CONFIG_FILE_EXTENSION, true); LOGGER.exiting(CLASS, METHOD, newName); return newName; } @@ -1315,11 +1354,12 @@ public String addCoherenceConfigFileFromUrl(String clusterName, URL urlForConfig /** * Get the name of the persistence directory as an archive path. This does not reconcile duplicates or other * items deeper in the zip file logic. - * @param clusterName name of cluster specific to path + * + * @param clusterName name of cluster specific to path * @param directoryType type of persistence directory * @return Archive style path for directory */ - public String getCoherencePersistArchivePath(String clusterName, String directoryType){ + public String getCoherencePersistArchivePath(String clusterName, String directoryType) { return getArchiveName(ARCHIVE_COHERENCE_TARGET_DIR + ZIP_SEP + clusterName, directoryType); } @@ -1369,6 +1409,7 @@ public String addFileStoreDirectory(String fileStoreName) throws WLSDeployArchiv /** * Get the archive path to Node Manager Identity Key Store file. This does not reconcile duplicate names or * other items that the archive file does when adding to the archive. + * * @param keystoreFile file name of the key store file * @return archive file path for the model */ @@ -1398,20 +1439,21 @@ public String addNodeManagerKeyStoreFile(String keystoreFile) throws WLSDeployAr /** * Return the manifest for the specified path in the archive, if present. * The path may refer to a packaged EAR/JAR/WAR, or an exploded entry. + * * @param sourcePath the path to be checked * @return the Manifest object, or null * @throws WLSDeployArchiveIOException if there is a problem reading the archive, or the manifest */ public Manifest getManifest(String sourcePath) throws WLSDeployArchiveIOException { try { - if(containsFile(sourcePath)) { + if (containsFile(sourcePath)) { // a jarred app or library in the archive. - try(ZipInputStream zipStream = new ZipInputStream(getZipFile().getZipEntry(sourcePath))) { + try (ZipInputStream zipStream = new ZipInputStream(getZipFile().getZipEntry(sourcePath))) { // JarInputStream.getManifest() has problems if MANIFEST.MF is not the first entry, // so use ZipInputStream and search for the specific entry. ZipEntry zipEntry; - while((zipEntry = zipStream.getNextEntry()) != null) { - if(JarFile.MANIFEST_NAME.equals(zipEntry.getName())) { + while ((zipEntry = zipStream.getNextEntry()) != null) { + if (JarFile.MANIFEST_NAME.equals(zipEntry.getName())) { Manifest manifest = new Manifest(zipStream); zipStream.closeEntry(); return manifest; @@ -1419,7 +1461,7 @@ public Manifest getManifest(String sourcePath) throws WLSDeployArchiveIOExceptio zipStream.closeEntry(); } } - } else if(containsPath(sourcePath)) { + } else if (containsPath(sourcePath)) { // an exploded app or library in the archive. String manifestPath = sourcePath + "/" + JarFile.MANIFEST_NAME; if (containsFile(manifestPath)) { @@ -1428,9 +1470,9 @@ public Manifest getManifest(String sourcePath) throws WLSDeployArchiveIOExceptio } } } - } catch(IOException e) { + } catch (IOException e) { WLSDeployArchiveIOException aioe = new WLSDeployArchiveIOException("WLSDPLY-01426", sourcePath, - getArchiveFileName(), e.getLocalizedMessage()); + getArchiveFileName(), e.getLocalizedMessage()); LOGGER.throwing(aioe); throw aioe; } @@ -1498,6 +1540,7 @@ protected String getArchiveName(String zipPathPrefix, String itemToAdd, boolean } return newName; } + protected String addItemToZip(String zipPathPrefix, File itemToAdd) throws WLSDeployArchiveIOException { return addItemToZip(zipPathPrefix, itemToAdd, true); } @@ -1579,7 +1622,7 @@ protected String addUrlToZip(String zipPathPrefix, URL url, String extension, bo newName = addSingleFileToZip(tmpFile, newName, METHOD); } catch (IOException ioe) { WLSDeployArchiveIOException aioe = - new WLSDeployArchiveIOException("WLSDPLY-01410", ioe, url, ioe.getLocalizedMessage()); + new WLSDeployArchiveIOException("WLSDPLY-01410", ioe, url, ioe.getLocalizedMessage()); LOGGER.throwing(aioe); throw aioe; } catch (SecurityException se) { @@ -1613,6 +1656,97 @@ protected String addUrlToZip(String zipPathPrefix, URL url, String extension, bo return newName; } + protected void extractWallet(File domainHome, String extractPath, List zipEntries, String deprecationKey) + throws WLSDeployArchiveIOException { + final String METHOD = "extractWallet"; + LOGGER.entering(CLASS, METHOD, domainHome, extractPath, zipEntries, deprecationKey); + + File fullExtractPath = new File(domainHome, extractPath); + if (zipEntries != null && !zipEntries.isEmpty()) { + String firstZipEntry = zipEntries.get(0); + if (!fullExtractPath.exists() && !fullExtractPath.mkdirs()) { + WLSDeployArchiveIOException ex = new WLSDeployArchiveIOException("WLSDPLY-01430", firstZipEntry, + getArchiveFileName(), fullExtractPath.getAbsolutePath()); + LOGGER.throwing(CLASS, METHOD, ex); + throw ex; + } + + // The archive file wallet directory can either contain a single zip file containing the wallet files + // or one or more wallet files. Before starting to iterate, check for the single zip file case. + // + if (zipEntries.size() == 1 && firstZipEntry.toLowerCase().endsWith(".zip")) { + if (!StringUtils.isEmpty(deprecationKey)) { + LOGGER.warning(deprecationKey, getArchiveFileName(), firstZipEntry, extractPath); + } + unzipZippedArchiveFileEntry(firstZipEntry, fullExtractPath); + } else { + for (String zipEntry : zipEntries) { + File extractToLocation = domainHome; + if (!StringUtils.isEmpty(deprecationKey)) { + extractToLocation = new File(domainHome, WLSDPLY_ARCHIVE_BINARY_DIR); + LOGGER.warning(deprecationKey, getArchiveFileName(), zipEntry, extractPath); + } + extractFileFromZip(zipEntry, extractToLocation); + } + } + } + LOGGER.exiting(CLASS, METHOD); + } + + protected void unzipZippedArchiveFileEntry(String zippedItemToExtract, File extractToLocation) + throws WLSDeployArchiveIOException { + final String METHOD = "unzipZippedArchiveFileEntry"; + LOGGER.entering(CLASS, METHOD, zippedItemToExtract, extractToLocation); + + File tempDir; + try { + tempDir = Files.createTempDirectory("tempzip").toFile(); + tempDir.deleteOnExit(); + } catch (IOException ioe) { + WLSDeployArchiveIOException ex = new WLSDeployArchiveIOException("WLSDPLY-01428", ioe, zippedItemToExtract, + ioe.getLocalizedMessage()); + LOGGER.throwing(CLASS, METHOD, ex); + throw ex; + } + + String zippedEntryPath = extractFile(zippedItemToExtract, tempDir); + + try (FileInputStream fis = new FileInputStream(zippedEntryPath); + ZipInputStream zis = new ZipInputStream(fis)) { + + ZipEntry ze = zis.getNextEntry(); + while (ze != null) { + String zipEntryFileName = ze.getName(); + checkForZipSlip(extractToLocation, zipEntryFileName); + + File zipEntryFile = new File(extractToLocation, ze.getName()); + if (!zipEntryFile.getParentFile().exists() && !zipEntryFile.getParentFile().mkdirs()) { + WLSDeployArchiveIOException ex = new WLSDeployArchiveIOException("WLSDPLY-01429", zipEntryFileName, + extractToLocation.getAbsolutePath()); + LOGGER.throwing(CLASS, METHOD, ex); + throw ex; + } + + try (FileOutputStream fos = new FileOutputStream(zipEntryFile, false)) { + byte[] buffer = new byte[4096]; + int len = zis.read(buffer); + while (len > 0) { + fos.write(buffer, 0, len); + len = zis.read(buffer); + } + } + zis.closeEntry(); + ze = zis.getNextEntry(); + } + } catch (IOException ioe) { + WLSDeployArchiveIOException ex = new WLSDeployArchiveIOException("WLSDPLY-01432", ioe, zippedEntryPath, + zippedItemToExtract, extractToLocation, ioe.getLocalizedMessage()); + LOGGER.throwing(CLASS, METHOD, ex); + throw ex; + } + LOGGER.exiting(CLASS, METHOD); + } + protected void extractDirectoryFromZip(String directoryName, File extractToLocation) throws WLSDeployArchiveIOException { extractDirectoryFromZip(directoryName, directoryName, extractToLocation); @@ -1635,6 +1769,7 @@ protected void extractDirectoryFromZip(String fromDirectoryName, String toDirect for (Map.Entry zipEntry : zipEntries.entrySet()) { String entryName = zipEntry.getKey(); String targetFileName = entryName.replace(fromDirectoryName + ZIP_SEP, toDirectoryName + SEP); + checkForZipSlip(extractToLocation, targetFileName); targetFile = new File(extractToLocation, targetFileName); File targetDirectory; if (entryName.endsWith(ZIP_SEP)) { @@ -1683,6 +1818,12 @@ protected void extractFileFromZip(String itemToExtract, String fromDir, String t final String METHOD = "extractFileFromZip"; LOGGER.entering(CLASS, METHOD, itemToExtract, fromDir, toDir, extractToLocation); + String targetFileName = itemToExtract; + if (fromDir != null && toDir != null) { + targetFileName = itemToExtract.replace(fromDir + ZIP_SEP, toDir + SEP); + } + checkForZipSlip(extractToLocation, targetFileName); + InputStream inputStream = getZipFile().getZipEntry(itemToExtract); if (inputStream == null) { WLSDeployArchiveIOException wdaioe = @@ -1691,12 +1832,16 @@ protected void extractFileFromZip(String itemToExtract, String fromDir, String t throw wdaioe; } - String targetFileName = itemToExtract.replace(fromDir + ZIP_SEP, toDir + SEP); File targetFile = new File(extractToLocation, targetFileName); File targetDirectory = targetFile.getParentFile(); if (!targetDirectory.exists() && !targetDirectory.mkdirs()) { WLSDeployArchiveIOException wdaioe = new WLSDeployArchiveIOException("WLSDPLY-01414", getArchiveFileName(), targetDirectory.getAbsolutePath()); + try { + inputStream.close(); + } catch (IOException ignore) { + // best effort + } LOGGER.throwing(CLASS, METHOD, wdaioe); throw wdaioe; } @@ -1788,6 +1933,29 @@ private String addSingleFileToZip(File itemToAdd, String preferredName, String c return newName; } + private String walkDownFolders(String zipPrefix, File zipPath) throws WLSDeployArchiveIOException { + String newSourceName = null; + if (zipPath != null) { + File[] fileList = zipPath.listFiles(); + if (fileList != null) { + for (File item : fileList) { + newSourceName = addItemToZip(zipPrefix, item); + } + } + } + return newSourceName; + } + + private void checkForZipSlip(File extractLocation, String zipEntry) throws WLSDeployArchiveIOException { + String canonicalExtractLocation = FileUtils.getCanonicalPath(extractLocation); + String canonicalZipEntry = FileUtils.getCanonicalPath(new File(extractLocation, zipEntry)); + + if (!canonicalZipEntry.startsWith(canonicalExtractLocation)) { + throw new WLSDeployArchiveIOException("WLSDPLY-01431", getArchiveFileName(), zipEntry, canonicalZipEntry, + canonicalExtractLocation); + } + } + /////////////////////////////////////////////////////////////////////////// // Private Static Helper Methods // /////////////////////////////////////////////////////////////////////////// @@ -1884,18 +2052,4 @@ private static FileInputStream getFileInputStream(File f, String itemName, Strin } return inputStream; } - - private String walkDownFolders(String zipPrefix, File zipPath) throws WLSDeployArchiveIOException { - String newSourceName = null; - if (zipPath != null) { - File[] fileList = zipPath.listFiles(); - if (fileList != null) { - for (File item : fileList) { - newSourceName = addItemToZip(zipPrefix, item); - } - } - } - return newSourceName; - } - } diff --git a/core/src/main/python/discover.py b/core/src/main/python/discover.py index 26c24f19a..3de132213 100644 --- a/core/src/main/python/discover.py +++ b/core/src/main/python/discover.py @@ -140,7 +140,6 @@ def __process_archive_filename_arg(argument_map): _method_name = '__process_archive_filename_arg' if CommandLineArgUtil.SKIP_ARCHIVE_FILE_SWITCH in argument_map or CommandLineArgUtil.REMOTE_SWITCH in argument_map: - archive_file = WLSDeployArchive.noArchiveFile() if CommandLineArgUtil.ARCHIVE_FILE_SWITCH in argument_map: ex = exception_helper.create_cla_exception(ExitCode.ARG_VALIDATION_ERROR, 'WLSDPLY-06033') @@ -162,8 +161,7 @@ def __process_archive_filename_arg(argument_map): ie.getLocalizedMessage(), error=ie) __logger.throwing(ex, class_name=_class_name, method_name=_method_name) raise ex - argument_map[CommandLineArgUtil.ARCHIVE_FILE] = archive_file - + argument_map[CommandLineArgUtil.ARCHIVE_FILE] = archive_file def __process_variable_filename_arg(optional_arg_map): @@ -352,16 +350,16 @@ def __clear_archive_file(model_context): archive_file = model_context.get_archive_file() - if archive_file is None: - de = exception_helper.create_discover_exception('WLSDPLY-06004', model_context.get_archive_file_name()) - __logger.throwing(class_name=_class_name, method_name=_method_name, error=de) - raise de - if not model_context.skip_archive() and not model_context.is_remote(): - try: - archive_file.removeAllBinaries() - except WLSDeployArchiveIOException, wioe: - de = exception_helper.create_discover_exception('WLSDPLY-06005', wioe.getLocalizedMessage()) + if archive_file is not None: + try: + archive_file.removeAllBinaries() + except WLSDeployArchiveIOException, wioe: + de = exception_helper.create_discover_exception('WLSDPLY-06005', wioe.getLocalizedMessage()) + __logger.throwing(class_name=_class_name, method_name=_method_name, error=de) + raise de + else: + de = exception_helper.create_discover_exception('WLSDPLY-06004', model_context.get_archive_file_name()) __logger.throwing(class_name=_class_name, method_name=_method_name, error=de) raise de @@ -377,7 +375,8 @@ def __close_archive(model_context): __logger.entering(_class_name=_class_name, method_name=_method_name) archive_file = model_context.get_archive_file() - archive_file.close() + if archive_file is not None: + archive_file.close() __logger.exiting(class_name=_class_name, method_name=_method_name) diff --git a/core/src/main/python/wlsdeploy/tool/util/archive_helper.py b/core/src/main/python/wlsdeploy/tool/util/archive_helper.py index 196c212c2..6af2f1744 100644 --- a/core/src/main/python/wlsdeploy/tool/util/archive_helper.py +++ b/core/src/main/python/wlsdeploy/tool/util/archive_helper.py @@ -414,12 +414,8 @@ def extract_atp_wallet(self): wallet_path = None for archive_file in self.__archive_files[::-1]: - atp_wallet_zipentry = archive_file.getATPWallet() - if atp_wallet_zipentry: - wallet_dir = File(self.__domain_home, 'atpwallet') - wallet_dir.mkdirs() - wallet_path = wallet_dir.getPath() - FileUtils.extractZipFileContent(archive_file, atp_wallet_zipentry, wallet_path) + wallet_path = archive_file.extractATPWallet(self.__domain_home) + if wallet_path is not None: break self.__logger.exiting(class_name=self.__class_name, method_name=_method_name, result=wallet_path) @@ -436,12 +432,8 @@ def extract_opss_wallet(self): wallet_path = None for archive_file in self.__archive_files[::-1]: - atp_wallet_zipentry = archive_file.getOPSSWallet() - if atp_wallet_zipentry: - wallet_dir = File(self.__domain_home, 'opsswallet') - wallet_dir.mkdirs() - wallet_path = wallet_dir.getPath() - FileUtils.extractZipFileContent(archive_file, atp_wallet_zipentry, wallet_path) + wallet_path = archive_file.extractOPSSWallet(self.__domain_home) + if wallet_path is not None: break self.__logger.exiting(class_name=self.__class_name, method_name=_method_name, result=wallet_path) diff --git a/core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties b/core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties index a8f71f417..6b1756102 100644 --- a/core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties +++ b/core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties @@ -200,6 +200,13 @@ WLSDPLY-01423=WLSDeployArchive {0} unable to add/extract binaries because the di WLSDPLY-01424=WLSDeployArchive {0} unable to add/extract binaries because the directory {1} is not a directory WLSDPLY-01425=Failed to add entry {2} for file {1} to zip file {0}: {3} WLSDPLY-01426=Unable to open the manifest for path {0} in archive file {1}: {2} +WLSDPLY-01427=Archive file {0} contains the ATP wallet in a deprecated location {1}. Please move to the new location {2}. +WLSDPLY-01428=Failed to extract archive file entry {0} due to an error creating a temporary directory: {1} +WLSDPLY-01429=Unable to extract zip entry {0} because the target directory {1} does not exist and could not be created +WLSDPLY-01430=Failed to extract wallet file {0} from archive file {1} because the target directory {2} does not exist and could not be created +WLSDPLY-01431=Zip slip security vulnerability detected in archive file {0} entry {1} because entry extract location {2} is not under the specified extract path {3} +WLSDPLY-01432=Failed to extract zip file {0} originating from archive file entry {1} to directory {2}: {3} +WLSDPLY-01433=Archive file {0} contains the OPSS wallet in a deprecated location {1}. Please move to the new location {2}. # oracle.weblogic.deploy.util.WLSDeployZipFile.java WLSDPLY-01500=The zip file {0} has the saved entry {1} diff --git a/core/src/test/java/oracle/weblogic/deploy/util/FileUtilsTest.java b/core/src/test/java/oracle/weblogic/deploy/util/FileUtilsTest.java index 69818d7a8..0c0555ec3 100644 --- a/core/src/test/java/oracle/weblogic/deploy/util/FileUtilsTest.java +++ b/core/src/test/java/oracle/weblogic/deploy/util/FileUtilsTest.java @@ -4,21 +4,15 @@ */ package oracle.weblogic.deploy.util; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.nio.file.attribute.PosixFilePermission; import java.text.MessageFormat; import java.util.Set; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class FileUtilsTest { @@ -125,58 +119,6 @@ void testHashing() throws Exception { assertEquals(appHash, archiveHash); } - @Test - /* A wallet zip inside the archive must not contain an entry such as ../info.txt, - since this creates a file overwrite security vulnerability (zip slip). - */ - void testZipVulnerability() throws Exception { - final String extractPath = UNIT_TEST_TARGET_DIR.getPath(); - - // an entry with a simple name or path works fine - File zipFile = buildWalletArchiveZip("info.txt"); - WLSDeployArchive deployArchive = new WLSDeployArchive(zipFile.getPath()); - FileUtils.extractZipFileContent(deployArchive, WALLET_PATH, extractPath); - - // an entry with parent directory notation should throw an exception - zipFile = buildWalletArchiveZip("../info.txt"); - final WLSDeployArchive deployArchive2 = new WLSDeployArchive(zipFile.getPath()); - assertThrows(IllegalArgumentException.class, - new Executable() { - @Override - public void execute() throws Throwable { - FileUtils.extractZipFileContent(deployArchive2, WALLET_PATH, extractPath); - } - }, - "Exception not thrown for zip entry outside extract directory"); - } - - /* Build an archive zip containing a wallet zip. - The wallet contains a single entry with the name of the contentName argument. - */ - private File buildWalletArchiveZip(String contentName) throws Exception { - - // create the wallet zip content - ByteArrayOutputStream walletBytes = new ByteArrayOutputStream(); - try (ZipOutputStream zipStream = new ZipOutputStream(walletBytes)) { - ZipEntry zipEntry = new ZipEntry(contentName); - zipStream.putNextEntry(zipEntry); - byte[] data = "info".getBytes(); - zipStream.write(data, 0, data.length); - zipStream.closeEntry(); - } - - File archiveFile = new File(UNIT_TEST_TARGET_DIR, "archive.zip"); - - try (ZipOutputStream zipStream = new ZipOutputStream(new FileOutputStream(archiveFile))) { - ZipEntry zipEntry = new ZipEntry(WALLET_PATH); - zipStream.putNextEntry(zipEntry); - zipStream.write(walletBytes.toByteArray()); - zipStream.closeEntry(); - } - - return archiveFile; - } - private void assertMatch(String name, String got, String expected) { assertEquals(expected, got, MessageFormat.format(FILE_ERR_FORMAT, name, got, expected)); } diff --git a/core/src/test/java/oracle/weblogic/deploy/util/WLSDeployArchiveTest.java b/core/src/test/java/oracle/weblogic/deploy/util/WLSDeployArchiveTest.java index 7a0549469..60cb94a4b 100644 --- a/core/src/test/java/oracle/weblogic/deploy/util/WLSDeployArchiveTest.java +++ b/core/src/test/java/oracle/weblogic/deploy/util/WLSDeployArchiveTest.java @@ -5,14 +5,22 @@ package oracle.weblogic.deploy.util; import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import oracle.weblogic.deploy.logging.PlatformLogger; +import oracle.weblogic.deploy.logging.WLSDeployLogFactory; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import static oracle.weblogic.deploy.util.WLSDeployArchive.ARCHIVE_ATP_WALLET_PATH; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; public class WLSDeployArchiveTest { @@ -39,6 +47,27 @@ public class WLSDeployArchiveTest { private static final String BINARIES_MODEL_ZIP_TARGET_NAME = WLSDeployZipFileTest.UNIT_TEST_TARGET_DIR + '/' + ZIP_FILE_EXISTING_BINARIES_FILE; + private static final String ATP_EMPTY_ARCHIVE = "src/test/resources/atp-empty-archive.zip"; + private static final String ATP_EXPANDED_ARCHIVE = "src/test/resources/atp-expanded-archive.zip"; + private static final String ATP_ZIPPED_ARCHIVE = "src/test/resources/atp-zipped-archive.zip"; + + private static final String ATP_DEPRECATED_EMPTY_ARCHIVE = "src/test/resources/atp-deprecated-empty-archive.zip"; + private static final String ATP_DEPRECATED_EXPANDED_ARCHIVE = + "src/test/resources/atp-deprecated-expanded-archive.zip"; + private static final String ATP_DEPRECATED_ZIPPED_ARCHIVE = "src/test/resources/atp-deprecated-zipped-archive.zip"; + + private static final List ATP_EXPECTED_CONTENT = Arrays.asList( + ARCHIVE_ATP_WALLET_PATH + "/cwallet.sso", + ARCHIVE_ATP_WALLET_PATH + "/ewallet.p12", + ARCHIVE_ATP_WALLET_PATH + "/ewallet.pem", + ARCHIVE_ATP_WALLET_PATH + "/keystore.jks", + ARCHIVE_ATP_WALLET_PATH + "/ojdbc.properties", + ARCHIVE_ATP_WALLET_PATH + "/README", + ARCHIVE_ATP_WALLET_PATH + "/sqlnet.ora", + ARCHIVE_ATP_WALLET_PATH + "/tnsnames.ora", + ARCHIVE_ATP_WALLET_PATH + "/truststore.jks" + ); + @BeforeAll static void setup() throws Exception { File unitTestDir = new File(WLSDeployZipFileTest.UNIT_TEST_TARGET_DIR).getCanonicalFile(); @@ -48,6 +77,9 @@ static void setup() throws Exception { if (appsArchiveFile.exists()) { appsArchiveFile.delete(); } + + PlatformLogger logger = WLSDeployLogFactory.getLogger("wlsdeploy.archive"); + logger.setLevel(Level.OFF); } @Test @@ -137,4 +169,116 @@ void testClearAllBinariesWithEmptyZip() throws Exception { assertFalse(StringUtils.isEmpty(appName), "expected appName to be not empty"); archive.close(); } + + @Test + void testATPZippedWalletExtract() throws Exception { + WLSDeployArchive archive = new WLSDeployArchive(ATP_ZIPPED_ARCHIVE); + File domainHome = new File("target/unit-tests/atp-zipped"); + if (!domainHome.exists() && !domainHome.mkdirs()) { + fail("Failed to create test domain home directory " + domainHome.getAbsolutePath()); + } + + String path = archive.extractATPWallet(domainHome); + + File atpWalletDir = new File(domainHome, ARCHIVE_ATP_WALLET_PATH).getCanonicalFile(); + assertEquals(atpWalletDir.getAbsolutePath(), path, "expected extractATPWallet to return correct path"); + assertTrue(atpWalletDir.exists(), "expected " + ARCHIVE_ATP_WALLET_PATH + " directory to exist"); + assertTrue(atpWalletDir.isDirectory(), "expected " + ARCHIVE_ATP_WALLET_PATH + " to be a directory"); + + for (String atpExpectedFile : ATP_EXPECTED_CONTENT) { + File walletFile = new File(domainHome, atpExpectedFile); + assertTrue(walletFile.exists(), "expected " + atpExpectedFile + " to exist"); + } + } + + @Test + void testATPExpandedWalletExtract() throws Exception { + WLSDeployArchive archive = new WLSDeployArchive(ATP_EXPANDED_ARCHIVE); + File domainHome = new File("target/unit-tests/atp-unzipped"); + if (!domainHome.exists() && !domainHome.mkdirs()) { + fail("Failed to create test domain home directory " + domainHome.getAbsolutePath()); + } + + String path = archive.extractATPWallet(domainHome); + + File atpWalletDir = new File(domainHome, ARCHIVE_ATP_WALLET_PATH).getCanonicalFile(); + assertEquals(atpWalletDir.getAbsolutePath(), path, "expected extractATPWallet to return correct path"); + assertTrue(atpWalletDir.exists(), "expected " + ARCHIVE_ATP_WALLET_PATH + " directory to exist"); + assertTrue(atpWalletDir.isDirectory(), "expected " + ARCHIVE_ATP_WALLET_PATH + " to be a directory"); + + for (String atpExpectedFile : ATP_EXPECTED_CONTENT) { + File walletFile = new File(domainHome, atpExpectedFile); + assertTrue(walletFile.exists(), "expected " + atpExpectedFile + " to exist"); + } + } + + @Test + void testATPEmptyWalletExtract() throws Exception { + WLSDeployArchive archive = new WLSDeployArchive(ATP_EMPTY_ARCHIVE); + File domainHome = new File("target/unit-tests/atp-empty"); + if (!domainHome.exists() && !domainHome.mkdirs()) { + fail("Failed to create test domain home directory " + domainHome.getAbsolutePath()); + } + + String path = archive.extractATPWallet(domainHome); + assertNull(path, "expected extractATPWallet to return null"); + File atpWalletDir = new File(domainHome, ARCHIVE_ATP_WALLET_PATH).getCanonicalFile(); + assertFalse(atpWalletDir.exists(), "expected " + ARCHIVE_ATP_WALLET_PATH + " directory to not exist"); + } + + @Test + void testDeprecatedATPZippedWalletExtract() throws Exception { + WLSDeployArchive archive = new WLSDeployArchive(ATP_DEPRECATED_ZIPPED_ARCHIVE); + File domainHome = new File("target/unit-tests/deprecated-atp-zipped"); + if (!domainHome.exists() && !domainHome.mkdirs()) { + fail("Failed to create test domain home directory " + domainHome.getAbsolutePath()); + } + + String path = archive.extractATPWallet(domainHome); + + File atpWalletDir = new File(domainHome, ARCHIVE_ATP_WALLET_PATH).getCanonicalFile(); + assertEquals(atpWalletDir.getAbsolutePath(), path, "expected extractATPWallet to return correct path"); + assertTrue(atpWalletDir.exists(), "expected " + ARCHIVE_ATP_WALLET_PATH + " directory to exist"); + assertTrue(atpWalletDir.isDirectory(), "expected " + ARCHIVE_ATP_WALLET_PATH + " to be a directory"); + + for (String atpExpectedFile : ATP_EXPECTED_CONTENT) { + File walletFile = new File(domainHome, atpExpectedFile); + assertTrue(walletFile.exists(), "expected " + atpExpectedFile + " to exist"); + } + } + + @Test + void testDeprecatedATPExpandedWalletExtract() throws Exception { + WLSDeployArchive archive = new WLSDeployArchive(ATP_DEPRECATED_EXPANDED_ARCHIVE); + File domainHome = new File("target/unit-tests/deprecated-atp-unzipped"); + if (!domainHome.exists() && !domainHome.mkdirs()) { + fail("Failed to create test domain home directory " + domainHome.getAbsolutePath()); + } + + String path = archive.extractATPWallet(domainHome); + + File atpWalletDir = new File(domainHome, ARCHIVE_ATP_WALLET_PATH).getCanonicalFile(); + assertEquals(atpWalletDir.getAbsolutePath(), path, "expected extractATPWallet to return correct path"); + assertTrue(atpWalletDir.exists(), "expected " + ARCHIVE_ATP_WALLET_PATH + " directory to exist"); + assertTrue(atpWalletDir.isDirectory(), "expected " + ARCHIVE_ATP_WALLET_PATH + " to be a directory"); + + for (String atpExpectedFile : ATP_EXPECTED_CONTENT) { + File walletFile = new File(domainHome, atpExpectedFile); + assertTrue(walletFile.exists(), "expected " + atpExpectedFile + " to exist"); + } + } + + @Test + void testDeprecatedATPEmptyWalletExtract() throws Exception { + WLSDeployArchive archive = new WLSDeployArchive(ATP_DEPRECATED_EMPTY_ARCHIVE); + File domainHome = new File("target/unit-tests/deprecated-atp-empty"); + if (!domainHome.exists() && !domainHome.mkdirs()) { + fail("Failed to create test domain home directory " + domainHome.getAbsolutePath()); + } + + String path = archive.extractATPWallet(domainHome); + assertNull(path, "expected extractATPWallet to return null"); + File atpWalletDir = new File(domainHome, ARCHIVE_ATP_WALLET_PATH).getCanonicalFile(); + assertFalse(atpWalletDir.exists(), "expected " + ARCHIVE_ATP_WALLET_PATH + " directory to not exist"); + } } diff --git a/core/src/test/resources/atp-deprecated-empty-archive.zip b/core/src/test/resources/atp-deprecated-empty-archive.zip new file mode 100644 index 000000000..a3d877294 Binary files /dev/null and b/core/src/test/resources/atp-deprecated-empty-archive.zip differ diff --git a/core/src/test/resources/atp-deprecated-expanded-archive.zip b/core/src/test/resources/atp-deprecated-expanded-archive.zip new file mode 100644 index 000000000..8db228355 Binary files /dev/null and b/core/src/test/resources/atp-deprecated-expanded-archive.zip differ diff --git a/core/src/test/resources/atp-deprecated-zipped-archive.zip b/core/src/test/resources/atp-deprecated-zipped-archive.zip new file mode 100644 index 000000000..2857c05f2 Binary files /dev/null and b/core/src/test/resources/atp-deprecated-zipped-archive.zip differ diff --git a/core/src/test/resources/atp-empty-archive.zip b/core/src/test/resources/atp-empty-archive.zip new file mode 100644 index 000000000..ec441c8ba Binary files /dev/null and b/core/src/test/resources/atp-empty-archive.zip differ diff --git a/core/src/test/resources/atp-expanded-archive.zip b/core/src/test/resources/atp-expanded-archive.zip new file mode 100644 index 000000000..087962db7 Binary files /dev/null and b/core/src/test/resources/atp-expanded-archive.zip differ diff --git a/core/src/test/resources/atp-zipped-archive.zip b/core/src/test/resources/atp-zipped-archive.zip new file mode 100644 index 000000000..966bd5fde Binary files /dev/null and b/core/src/test/resources/atp-zipped-archive.zip differ