fix: Cannot open certain zip files, #2467

This commit is contained in:
Feng Hao 2018-09-20 10:16:03 +08:00
parent 5d1c06fdcd
commit 9faebd9c52
6 changed files with 97 additions and 1 deletions

View File

@ -54,6 +54,9 @@ public:
static void sync(std::istream& in);
/// Searches the next valid header in the input stream, stops right before it
static void syncDataDescriptor(std::istream& in, bool force64);
/// Searches the next data descriptor
static void verifyZipEntryFileName(const std::string& zipPath);
/// Verifies that the name of the ZipEntry is a valid path

View File

@ -37,7 +37,7 @@ bool SkipCallback::handleZipEntry(std::istream& zipStream, const ZipLocalFileHea
if (!hdr.searchCRCAndSizesAfterData())
zipStream.seekg(hdr.getCompressedSize(), std::ios_base::cur);
else
ZipUtil::sync(zipStream);
ZipUtil::syncDataDescriptor(zipStream, hdr.needsZip64());
if (!zipStream.good()) throw Poco::IOException("Failed to seek on input stream");
return true;
}

View File

@ -160,6 +160,69 @@ void ZipUtil::sync(std::istream& in)
}
}
void ZipUtil::syncDataDescriptor(std::istream & in, bool force64)
{
std::streampos start = in.tellg();
const int eof = std::char_traits<char>::eof();
int c = in.get();
do
{
while (c != eof && c != ZipDataInfo::HEADER[0]) { c = in.get(); }
if (c == eof) return;
bool match = true;
for (int i = 1; i < 4 && match; i++)
{
c = in.get();
if (c != ZipDataInfo::HEADER[i]) match = false;
}
if (match)
{
std::streampos end = in.tellg();
if (force64)
{
ZipDataInfo64 nfo(in, true);
if (nfo.isValid())
{
if (end - start == nfo.getCompressedSize() + 4)
{
in.seekg(-static_cast<int>(ZipDataInfo64::getFullHeaderSize()), std::ios::cur);
if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
break;
}
else
{
in.seekg(-static_cast<int>(ZipDataInfo64::getFullHeaderSize()) + 4, std::ios::cur);
if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
}
}
}
else
{
ZipDataInfo nfo(in, true);
if (nfo.isValid())
{
if (end - start == nfo.getCompressedSize() + 4)
{
in.seekg(-static_cast<int>(ZipDataInfo::getFullHeaderSize()), std::ios::cur);
if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
break;
}
else
{
in.seekg(-static_cast<int>(ZipDataInfo::getFullHeaderSize()) + 4, std::ios::cur);
if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
}
}
}
}
} while (c != eof);
}
void ZipUtil::verifyZipEntryFileName(const std::string& fn)
{

Binary file not shown.

View File

@ -65,6 +65,34 @@ void ZipTest::testSkipSingleFile()
const std::string& POCO_UNUSED fileName = hdr.getFileName();
}
void ZipTest::testCrcAndSizeAfterDataEncapsulated()
{
// touch empty.txt
// zip -fd foo.zip empty.txt
// zip -fd encapsulated.zip foo.zip
std::string testFile = getTestFile("data", "encapsulated.zip");
Poco::FileInputStream inp(testFile);
assertTrue(inp.good());
ZipArchive arch(inp);
ZipArchive::FileHeaders::const_iterator it = arch.findHeader("foo.zip");
assertTrue(it != arch.headerEnd());
inp.clear(); // inp eof(), should clear
ZipInputStream zipin(inp, it->second);
std::ostringstream out(std::ios::binary);
Poco::StreamCopier::copyStream(zipin, out);
std::string result = out.str();
// sub zip
std::istringstream istr(result);
ZipArchive subArch(istr);
it = subArch.findHeader("empty.txt");
assertTrue(it != subArch.headerEnd());
assertTrue(it->second.getCompressedSize() == 0);
assertTrue(it->second.getUncompressedSize() == 0);
}
void ZipTest::testDecompressSingleFile()
{
@ -325,6 +353,7 @@ CppUnit::Test* ZipTest::suite()
CppUnit_addTest(pSuite, ZipTest, testDecompressFlatVuln);
CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterData);
CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataWithArchive);
CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataEncapsulated);
CppUnit_addTest(pSuite, ZipTest, testDecompressZip64);
CppUnit_addTest(pSuite, ZipTest, testValidPath);

View File

@ -34,6 +34,7 @@ public:
void testDecompressFlatVuln();
void testCrcAndSizeAfterData();
void testCrcAndSizeAfterDataWithArchive();
void testCrcAndSizeAfterDataEncapsulated();
static const Poco::UInt64 KB = 1024;
static const Poco::UInt64 MB = 1024*KB;