mirror of
https://github.com/pocoproject/poco.git
synced 2025-01-09 03:08:31 +01:00
feat(Poco::Zip): Check archive consistency before extracting (#4807)
* feat(Zip): add checkConsistency() method for checking archive's consistency * refactor(Zip): check archive consistency when decompressing all files * test(Zip): add archive consistency tests * refactor(Zip): make archive consistency check optional * test(Zip): test optional consistency check
This commit is contained in:
parent
854d8c89d6
commit
a8bac051c3
@ -55,9 +55,10 @@ public:
|
|||||||
~Decompress() override;
|
~Decompress() override;
|
||||||
/// Destroys the Decompress.
|
/// Destroys the Decompress.
|
||||||
|
|
||||||
ZipArchive decompressAllFiles();
|
ZipArchive decompressAllFiles(const bool checkConsistency = true);
|
||||||
/// Decompresses all files stored in the zip File. Can only be called once per Decompress object.
|
/// Decompresses all files stored in the zip File. Can only be called once per Decompress object.
|
||||||
/// Use mapping to retrieve the location of the decompressed files
|
/// Use mapping to retrieve the location of the decompressed files
|
||||||
|
/// if checkConsistency is set to false, archive won't be checked for consistency before decompression
|
||||||
|
|
||||||
bool handleZipEntry(std::istream& zipStream, const ZipLocalFileHeader& hdr) override;
|
bool handleZipEntry(std::istream& zipStream, const ZipLocalFileHeader& hdr) override;
|
||||||
|
|
||||||
|
@ -52,6 +52,9 @@ public:
|
|||||||
~ZipArchive();
|
~ZipArchive();
|
||||||
/// Destroys the ZipArchive.
|
/// Destroys the ZipArchive.
|
||||||
|
|
||||||
|
void checkConsistency();
|
||||||
|
/// Check archive's consistency
|
||||||
|
|
||||||
FileInfos::const_iterator fileInfoBegin() const;
|
FileInfos::const_iterator fileInfoBegin() const;
|
||||||
|
|
||||||
FileInfos::const_iterator fileInfoEnd() const;
|
FileInfos::const_iterator fileInfoEnd() const;
|
||||||
|
@ -64,10 +64,14 @@ Decompress::~Decompress()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ZipArchive Decompress::decompressAllFiles()
|
ZipArchive Decompress::decompressAllFiles(const bool checkConsistency)
|
||||||
{
|
{
|
||||||
poco_assert (_mapping.empty());
|
poco_assert (_mapping.empty());
|
||||||
ZipArchive arch(_in, *this);
|
ZipArchive arch(_in, *this);
|
||||||
|
|
||||||
|
if (checkConsistency)
|
||||||
|
arch.checkConsistency();
|
||||||
|
|
||||||
return arch;
|
return arch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "Poco/Zip/ZipArchive.h"
|
#include "Poco/Zip/ZipArchive.h"
|
||||||
#include "Poco/Zip/SkipCallback.h"
|
#include "Poco/Zip/SkipCallback.h"
|
||||||
|
#include "Poco/Zip/ZipException.h"
|
||||||
#include "Poco/Exception.h"
|
#include "Poco/Exception.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
@ -62,6 +63,32 @@ ZipArchive::~ZipArchive()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ZipArchive::checkConsistency()
|
||||||
|
{
|
||||||
|
for (const auto& [filename, dirHeader]: _infos)
|
||||||
|
{
|
||||||
|
Poco::UInt32 dirCRC = dirHeader.getCRC();
|
||||||
|
Poco::UInt64 dirUncompressedSize = dirHeader.getUncompressedSize();
|
||||||
|
|
||||||
|
const auto dataIt = _entries.find(filename);
|
||||||
|
if (dataIt != _entries.end())
|
||||||
|
{
|
||||||
|
const ZipLocalFileHeader &dataHeader = dataIt->second;
|
||||||
|
|
||||||
|
Poco::UInt32 dataCRC = dataHeader.getCRC();
|
||||||
|
Poco::UInt64 dataUncompressedSize = dataHeader.getUncompressedSize();
|
||||||
|
|
||||||
|
if (dataCRC != dirCRC)
|
||||||
|
throw ZipException("CRC-32 mismatch: ", filename);
|
||||||
|
|
||||||
|
if (dataUncompressedSize != dirUncompressedSize)
|
||||||
|
throw ZipException("Uncompressed size mismatch: ", filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ZipArchive::parse(std::istream& in, ParseCallback& pc)
|
void ZipArchive::parse(std::istream& in, ParseCallback& pc)
|
||||||
{
|
{
|
||||||
// read 4 bytes
|
// read 4 bytes
|
||||||
|
BIN
Zip/testsuite/data/consistency-crc32.zip
Normal file
BIN
Zip/testsuite/data/consistency-crc32.zip
Normal file
Binary file not shown.
BIN
Zip/testsuite/data/consistency-uncompressed.zip
Normal file
BIN
Zip/testsuite/data/consistency-uncompressed.zip
Normal file
Binary file not shown.
@ -11,6 +11,7 @@
|
|||||||
#include "ZipTest.h"
|
#include "ZipTest.h"
|
||||||
#include "Poco/Zip/SkipCallback.h"
|
#include "Poco/Zip/SkipCallback.h"
|
||||||
#include "Poco/Zip/ZipLocalFileHeader.h"
|
#include "Poco/Zip/ZipLocalFileHeader.h"
|
||||||
|
#include "Poco/Zip/ZipException.h"
|
||||||
#include "Poco/Zip/ZipArchive.h"
|
#include "Poco/Zip/ZipArchive.h"
|
||||||
#include "Poco/Zip/ZipStream.h"
|
#include "Poco/Zip/ZipStream.h"
|
||||||
#include "Poco/Zip/Decompress.h"
|
#include "Poco/Zip/Decompress.h"
|
||||||
@ -287,6 +288,80 @@ void ZipTest::testDecompressZip64()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ZipTest::testDecompressConsistency()
|
||||||
|
{
|
||||||
|
// test archives with borked file headers, but correct directory
|
||||||
|
const std::string testArchives[] =
|
||||||
|
{
|
||||||
|
"consistency-crc32.zip",
|
||||||
|
"consistency-uncompressed.zip"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
for (const std::string &archive : testArchives)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// test decompressing all files
|
||||||
|
//
|
||||||
|
{
|
||||||
|
Poco::FileInputStream inputStream(getTestFile("data", archive));
|
||||||
|
assertTrue (inputStream.good());
|
||||||
|
|
||||||
|
Decompress dec(inputStream, Poco::Path::temp());
|
||||||
|
|
||||||
|
try {
|
||||||
|
dec.decompressAllFiles();
|
||||||
|
assertTrue (false);
|
||||||
|
}
|
||||||
|
catch (const ZipException &e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// test decompressing all files (ignore consistency check)
|
||||||
|
//
|
||||||
|
{
|
||||||
|
Poco::FileInputStream inputStream(getTestFile("data", archive));
|
||||||
|
assertTrue (inputStream.good());
|
||||||
|
|
||||||
|
Decompress dec(inputStream, Poco::Path::temp());
|
||||||
|
|
||||||
|
// should not throw since consistency checks are ignored
|
||||||
|
try {
|
||||||
|
dec.decompressAllFiles(false);
|
||||||
|
}
|
||||||
|
catch (const ZipException &e)
|
||||||
|
{
|
||||||
|
assertTrue (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// test decompressing single file
|
||||||
|
//
|
||||||
|
{
|
||||||
|
Poco::FileInputStream inputStream(getTestFile("data", archive));
|
||||||
|
assertTrue (inputStream.good());
|
||||||
|
|
||||||
|
ZipArchive archive(inputStream);
|
||||||
|
|
||||||
|
// File decompression code is skipped since
|
||||||
|
// we are not expecting archive to be processed further
|
||||||
|
try
|
||||||
|
{
|
||||||
|
archive.checkConsistency();
|
||||||
|
assertTrue (false);
|
||||||
|
}
|
||||||
|
catch (const ZipException &e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void ZipTest::testValidPath()
|
void ZipTest::testValidPath()
|
||||||
{
|
{
|
||||||
assertTrue (ZipCommon::isValidPath("."));
|
assertTrue (ZipCommon::isValidPath("."));
|
||||||
@ -357,6 +432,7 @@ CppUnit::Test* ZipTest::suite()
|
|||||||
CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataEncapsulated);
|
CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataEncapsulated);
|
||||||
CppUnit_addTest(pSuite, ZipTest, testDecompressZip64);
|
CppUnit_addTest(pSuite, ZipTest, testDecompressZip64);
|
||||||
CppUnit_addTest(pSuite, ZipTest, testValidPath);
|
CppUnit_addTest(pSuite, ZipTest, testValidPath);
|
||||||
|
CppUnit_addTest(pSuite, ZipTest, testDecompressConsistency);
|
||||||
|
|
||||||
return pSuite;
|
return pSuite;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ public:
|
|||||||
void testDecompressSingleFile();
|
void testDecompressSingleFile();
|
||||||
void testDecompressSingleFileInDir();
|
void testDecompressSingleFileInDir();
|
||||||
void testDecompress();
|
void testDecompress();
|
||||||
|
void testDecompressConsistency();
|
||||||
void testDecompressFlat();
|
void testDecompressFlat();
|
||||||
void testDecompressVuln();
|
void testDecompressVuln();
|
||||||
void testDecompressFlatVuln();
|
void testDecompressFlatVuln();
|
||||||
|
Loading…
Reference in New Issue
Block a user