Skip to content

Commit

Permalink
feat: allow to disable zip security checks (#1579, #980)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Sep 9, 2023
1 parent 76cf959 commit 45a637f
Showing 1 changed file with 34 additions and 17 deletions.
51 changes: 34 additions & 17 deletions jadx-core/src/main/java/jadx/api/plugins/utils/ZipSecurity.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.zip.ZipEntry;
Expand All @@ -17,6 +18,8 @@
public class ZipSecurity {
private static final Logger LOG = LoggerFactory.getLogger(ZipSecurity.class);

private static final boolean DISABLE_CHECKS = Objects.equals(System.getenv("JADX_DISABLE_ZIP_SECURITY"), "true");

/**
* size of uncompressed zip entry shouldn't be bigger of compressed in
* {@link #ZIP_BOMB_DETECTION_FACTOR} times
Expand All @@ -33,44 +36,55 @@ public class ZipSecurity {
private ZipSecurity() {
}

private static boolean isInSubDirectoryInternal(File baseDir, File canonFile) {
if (canonFile == null) {
return false;
}
if (canonFile.equals(baseDir)) {
return true;
private static boolean isInSubDirectoryInternal(File baseDir, File file) {
File current = file;
while (true) {
if (current == null) {
return false;
}
if (current.equals(baseDir)) {
return true;
}
current = current.getParentFile();
}
return isInSubDirectoryInternal(baseDir, canonFile.getParentFile());
}

public static boolean isInSubDirectory(File baseDir, File file) {
if (DISABLE_CHECKS) {
return true;
}
try {
file = file.getCanonicalFile();
baseDir = baseDir.getCanonicalFile();
return isInSubDirectoryInternal(baseDir.getCanonicalFile(), file.getCanonicalFile());
} catch (IOException e) {
return false;
}
return isInSubDirectoryInternal(baseDir, file);
}

// checks that entry name contains no any traversals
// and prevents cases like "../classes.dex", to limit output only to the specified directory
/**
* Checks that entry name contains no any traversals and prevents cases like "../classes.dex",
* to limit output only to the specified directory
*/
public static boolean isValidZipEntryName(String entryName) {
if (DISABLE_CHECKS) {
return true;
}
try {
File currentPath = CommonFileUtils.CWD;
File canonical = new File(currentPath, entryName).getCanonicalFile();
if (isInSubDirectoryInternal(currentPath, canonical)) {
return true;
}
LOG.error("Invalid file name or path traversal attack detected: {}", entryName);
return false;
} catch (Exception e) {
LOG.error("Invalid file name or path traversal attack detected: {}", entryName);
return false;
// check failed
}
LOG.error("Invalid file name or path traversal attack detected: {}", entryName);
return false;
}

public static boolean isZipBomb(ZipEntry entry) {
if (DISABLE_CHECKS) {
return false;
}
long compressedSize = entry.getCompressedSize();
long uncompressedSize = entry.getSize();
boolean invalidSize = (compressedSize < 0) || (uncompressedSize < 0);
Expand All @@ -90,6 +104,9 @@ public static boolean isValidZipEntry(ZipEntry entry) {
}

public static InputStream getInputStreamForEntry(ZipFile zipFile, ZipEntry entry) throws IOException {
if (DISABLE_CHECKS) {
return new BufferedInputStream(zipFile.getInputStream(entry));
}
InputStream in = zipFile.getInputStream(entry);
LimitedInputStream limited = new LimitedInputStream(in, entry.getSize());
return new BufferedInputStream(limited);
Expand All @@ -112,7 +129,7 @@ public static <R> R visitZipEntries(File file, BiFunction<ZipFile, ZipEntry, R>
return result;
}
entriesProcessed++;
if (entriesProcessed > MAX_ENTRIES_COUNT) {
if (!DISABLE_CHECKS && entriesProcessed > MAX_ENTRIES_COUNT) {
throw new IllegalStateException("Zip entries count limit exceeded: " + MAX_ENTRIES_COUNT
+ ", last entry: " + entry.getName());
}
Expand Down

0 comments on commit 45a637f

Please sign in to comment.