Fail fast when a Zip64 jar is encountered

Previously, jars (either top-level or nested) in Zip64 format were
treated as normal jar files. This would lead to a failure later on
when an attempt was made to read an entry from the file.

This commit updates the loader to fail fast when it encounters a
Zip64 jar file. Such files are identified by the number of entries
in the central directory end record being 0xFFFF.

Closes gh-8735
pull/8929/head
Andy Wilkinson 8 years ago
parent b280e3092d
commit 456327260b

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -100,8 +100,14 @@ public class JarFileArchive implements Archive {
if (jarEntry.getComment().startsWith(UNPACK_MARKER)) { if (jarEntry.getComment().startsWith(UNPACK_MARKER)) {
return getUnpackedNestedArchive(jarEntry); return getUnpackedNestedArchive(jarEntry);
} }
JarFile jarFile = this.jarFile.getNestedJarFile(jarEntry); try {
return new JarFileArchive(jarFile); JarFile jarFile = this.jarFile.getNestedJarFile(jarEntry);
return new JarFileArchive(jarFile);
}
catch (Exception ex) {
throw new IllegalStateException(
"Failed to get nested archive for entry " + entry.getName(), ex);
}
} }
private Archive getUnpackedNestedArchive(JarEntry jarEntry) throws IOException { private Archive getUnpackedNestedArchive(JarEntry jarEntry) throws IOException {

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -24,6 +24,7 @@ import org.springframework.boot.loader.data.RandomAccessData;
* A ZIP File "End of central directory record" (EOCD). * A ZIP File "End of central directory record" (EOCD).
* *
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson
* @see <a href="http://en.wikipedia.org/wiki/Zip_%28file_format%29">Zip File Format</a> * @see <a href="http://en.wikipedia.org/wiki/Zip_%28file_format%29">Zip File Format</a>
*/ */
class CentralDirectoryEndRecord { class CentralDirectoryEndRecord {
@ -118,7 +119,11 @@ class CentralDirectoryEndRecord {
* @return the number of records in the zip * @return the number of records in the zip
*/ */
public int getNumberOfRecords() { public int getNumberOfRecords() {
return (int) Bytes.littleEndianValue(this.block, this.offset + 10, 2); long numberOfRecords = Bytes.littleEndianValue(this.block, this.offset + 10, 2);
if (numberOfRecords == 0xFFFF) {
throw new IllegalStateException("Zip64 archives are not supported");
}
return (int) numberOfRecords;
} }
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,20 +16,30 @@
package org.springframework.boot.loader.archive; package org.springframework.boot.loader.archive;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import org.springframework.boot.loader.TestJarCreator; import org.springframework.boot.loader.TestJarCreator;
import org.springframework.boot.loader.archive.Archive.Entry; import org.springframework.boot.loader.archive.Archive.Entry;
import org.springframework.util.FileCopyUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.equalTo;
/** /**
* Tests for {@link JarFileArchive}. * Tests for {@link JarFileArchive}.
@ -42,6 +52,9 @@ public class JarFileArchiveTests {
@Rule @Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder(); public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Rule
public ExpectedException thrown = ExpectedException.none();
private File rootJarFile; private File rootJarFile;
private JarFileArchive archive; private JarFileArchive archive;
@ -120,6 +133,48 @@ public class JarFileArchiveTests {
assertThat(nested.getParent()).isEqualTo(anotherNested.getParent()); assertThat(nested.getParent()).isEqualTo(anotherNested.getParent());
} }
@Test
public void zip64ArchivesAreHandledGracefully() throws IOException {
File file = this.temporaryFolder.newFile("test.jar");
FileCopyUtils.copy(writeZip64Jar(), file);
this.thrown.expectMessage(equalTo("Zip64 archives are not supported"));
new JarFileArchive(file);
}
@Test
public void nestedZip64ArchivesAreHandledGracefully() throws IOException {
File file = this.temporaryFolder.newFile("test.jar");
JarOutputStream output = new JarOutputStream(new FileOutputStream(file));
JarEntry zip64JarEntry = new JarEntry("nested/zip64.jar");
output.putNextEntry(zip64JarEntry);
byte[] zip64JarData = writeZip64Jar();
zip64JarEntry.setSize(zip64JarData.length);
zip64JarEntry.setCompressedSize(zip64JarData.length);
zip64JarEntry.setMethod(ZipEntry.STORED);
CRC32 crc32 = new CRC32();
crc32.update(zip64JarData);
zip64JarEntry.setCrc(crc32.getValue());
output.write(zip64JarData);
output.closeEntry();
output.close();
JarFileArchive jarFileArchive = new JarFileArchive(file);
this.thrown.expectMessage(
equalTo("Failed to get nested archive for entry nested/zip64.jar"));
jarFileArchive
.getNestedArchive(getEntriesMap(jarFileArchive).get("nested/zip64.jar"));
}
private byte[] writeZip64Jar() throws IOException {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
JarOutputStream jarOutput = new JarOutputStream(bytes);
for (int i = 0; i < 65537; i++) {
jarOutput.putNextEntry(new JarEntry(i + ".dat"));
jarOutput.closeEntry();
}
jarOutput.close();
return bytes.toByteArray();
}
private Map<String, Archive.Entry> getEntriesMap(Archive archive) { private Map<String, Archive.Entry> getEntriesMap(Archive archive) {
Map<String, Archive.Entry> entries = new HashMap<String, Archive.Entry>(); Map<String, Archive.Entry> entries = new HashMap<String, Archive.Entry>();
for (Archive.Entry entry : archive) { for (Archive.Entry entry : archive) {

Loading…
Cancel
Save