1//
2// ZipTest.cpp
3//
4// Copyright (c) 2007, Applied Informatics Software Engineering GmbH.
5// and Contributors.
6//
7// SPDX-License-Identifier: BSL-1.0
8//
9
10
11#include "ZipTest.h"
12#include "Poco/Zip/SkipCallback.h"
13#include "Poco/Zip/ZipLocalFileHeader.h"
14#include "Poco/Zip/ZipArchive.h"
15#include "Poco/Zip/ZipStream.h"
16#include "Poco/Zip/Decompress.h"
17#include "Poco/Zip/ZipCommon.h"
18#include "Poco/StreamCopier.h"
19#include "Poco/File.h"
20#include "Poco/FileStream.h"
21#include "Poco/URI.h"
22#include "Poco/Path.h"
23#include "Poco/Delegate.h"
24#include "Poco/StreamCopier.h"
25#include "Poco/Environment.h"
26#include "Poco/CppUnit/TestCaller.h"
27#include "Poco/CppUnit/TestSuite.h"
28#undef min
29#include <algorithm>
30#include <iostream>
31#include <sstream>
32
33
34using namespace Poco::Zip;
35
36
37ZipTest::ZipTest(const std::string& name): CppUnit::TestCase(name)
38{
39}
40
41
42ZipTest::~ZipTest()
43{
44}
45
46
47void ZipTest::testSkipSingleFile()
48{
49 std::string testFile = getTestFile("data", "test.zip");
50 Poco::FileInputStream inp(testFile);
51 assertTrue (inp.good());
52 SkipCallback skip;
53 ZipLocalFileHeader hdr(inp, false, skip);
54 assertTrue (ZipCommon::HS_FAT == hdr.getHostSystem());
55 int major = hdr.getMajorVersionNumber();
56 int POCO_UNUSED minor = hdr.getMinorVersionNumber();
57 assertTrue (major <= 2);
58 std::size_t hdrSize = hdr.getHeaderSize();
59 assertTrue (hdrSize > 30);
60 ZipCommon::CompressionMethod POCO_UNUSED cm = hdr.getCompressionMethod();
61 assertTrue (!hdr.isEncrypted());
62 Poco::DateTime aDate = hdr.lastModifiedAt();
63 Poco::UInt64 POCO_UNUSED cS = hdr.getCompressedSize();
64 Poco::UInt64 POCO_UNUSED uS = hdr.getUncompressedSize();
65 const std::string& POCO_UNUSED fileName = hdr.getFileName();
66}
67
68
69void ZipTest::testCrcAndSizeAfterDataEncapsulated()
70{
71 // touch empty.txt
72 // zip -fd foo.zip empty.txt
73 // zip -fd encapsulated.zip foo.zip
74 std::string testFile = getTestFile("data", "encapsulated.zip");
75 Poco::FileInputStream inp(testFile);
76 assertTrue(inp.good());
77
78 ZipArchive arch(inp);
79 ZipArchive::FileHeaders::const_iterator it = arch.findHeader("foo.zip");
80 assertTrue(it != arch.headerEnd());
81 inp.clear(); // inp eof(), should clear
82
83 ZipInputStream zipin(inp, it->second);
84 std::ostringstream out(std::ios::binary);
85 Poco::StreamCopier::copyStream(zipin, out);
86
87 std::string result = out.str();
88 // sub zip
89 std::istringstream istr(result);
90 ZipArchive subArch(istr);
91 it = subArch.findHeader("empty.txt");
92 assertTrue(it != subArch.headerEnd());
93 assertTrue(it->second.getCompressedSize() == 0);
94 assertTrue(it->second.getUncompressedSize() == 0);
95}
96
97
98void ZipTest::testDecompressSingleFile()
99{
100 std::string testFile = getTestFile("data", "test.zip");
101 Poco::FileInputStream inp(testFile);
102 assertTrue (inp.good());
103 ZipArchive arch(inp);
104 ZipArchive::FileHeaders::const_iterator it = arch.findHeader("testfile.txt");
105 assertTrue (it != arch.headerEnd());
106 ZipInputStream zipin (inp, it->second);
107 std::ostringstream out(std::ios::binary);
108 Poco::StreamCopier::copyStream(zipin, out);
109 assertTrue (!out.str().empty());
110}
111
112
113void ZipTest::testDecompressSingleFileInDir()
114{
115 std::string testFile = getTestFile("data","test.zip");
116 Poco::FileInputStream inp(testFile);
117 assertTrue (inp.good());
118 ZipArchive arch(inp);
119 ZipArchive::FileHeaders::const_iterator it = arch.findHeader("testdir/testfile.txt");
120 assertTrue (it != arch.headerEnd());
121 ZipInputStream zipin (inp, it->second);
122 std::ostringstream out(std::ios::binary);
123 Poco::StreamCopier::copyStream(zipin, out);
124 assertTrue (!out.str().empty());
125}
126
127
128void ZipTest::testCrcAndSizeAfterData()
129{
130 std::string testFile = getTestFile("data", "data.zip");
131 Poco::FileInputStream inp(testFile);
132 assertTrue (inp.good());
133 Decompress dec(inp, Poco::Path::temp());
134 dec.EError += Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
135 dec.decompressAllFiles();
136 dec.EError -= Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
137 assertTrue (_errCnt == 0);
138 assertTrue (!dec.mapping().empty());
139}
140
141
142void ZipTest::testCrcAndSizeAfterDataWithArchive()
143{
144 std::string testFile = getTestFile("data", "data.zip");
145 Poco::FileInputStream inp(testFile);
146 assertTrue (inp.good());
147 Poco::Zip::ZipArchive zip(inp);
148 inp.clear();
149 inp.seekg(0);
150 Poco::Zip::ZipArchive::FileHeaders::const_iterator it = zip.headerBegin();
151 for ( ; it!=zip.headerEnd(); ++it)
152 {
153 Poco::Zip::ZipInputStream zipis(inp,it->second);
154 Poco::Path path(it->second.getFileName());
155 if (path.isFile())
156 {
157 Poco::FileOutputStream os(Poco::Path::temp() + "test.dat");
158 Poco::StreamCopier::copyStream(zipis,os);
159 }
160 }
161}
162
163
164std::string ZipTest::getTestFile(const std::string& directory, const std::string& file)
165{
166 std::ostringstream ostr;
167 ostr << directory << '/' << file;
168 std::string validDir(ostr.str());
169 Poco::Path pathPattern(validDir);
170 if (Poco::File(pathPattern).exists())
171 {
172 return validDir;
173 }
174
175 ostr.str("");
176 ostr << "/Zip/testsuite/" << directory << '/' << file;
177 validDir = Poco::Environment::get("POCO_BASE") + ostr.str();
178 pathPattern = validDir;
179
180 if (!Poco::File(pathPattern).exists())
181 {
182 std::cout << "Can't find " << validDir << std::endl;
183 throw Poco::NotFoundException("cannot locate directory containing valid Zip test files");
184 }
185 return validDir;
186}
187
188
189void ZipTest::testDecompress()
190{
191 std::string testFile = getTestFile("data", "test.zip");
192 Poco::FileInputStream inp(testFile);
193 assertTrue (inp.good());
194 Decompress dec(inp, Poco::Path::temp());
195 dec.EError += Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
196 dec.decompressAllFiles();
197 dec.EError -= Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
198 assertTrue (_errCnt == 0);
199 assertTrue (!dec.mapping().empty());
200}
201
202
203void ZipTest::testDecompressFlat()
204{
205 std::string testFile = getTestFile("data", "test.zip");
206 Poco::FileInputStream inp(testFile);
207 assertTrue (inp.good());
208 Decompress dec(inp, Poco::Path::temp(), true);
209 dec.EError += Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
210 dec.decompressAllFiles();
211 dec.EError -= Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
212 assertTrue (_errCnt == 0);
213 assertTrue (!dec.mapping().empty());
214}
215
216
217void ZipTest::testDecompressVuln()
218{
219 std::string testFile = getTestFile("data", "vuln.zip");
220 Poco::FileInputStream inp(testFile);
221 assertTrue (inp.good());
222 Decompress dec(inp, Poco::Path::temp());
223 dec.EError += Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
224 dec.decompressAllFiles();
225 dec.EError -= Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
226 assertTrue (_errCnt == 1);
227 assertTrue (dec.mapping().empty());
228}
229
230
231void ZipTest::testDecompressFlatVuln()
232{
233 std::string testFile = getTestFile("data", "vuln.zip");
234 Poco::FileInputStream inp(testFile);
235 assertTrue (inp.good());
236 Decompress dec(inp, Poco::Path::temp(), true);
237 dec.EError += Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
238 dec.decompressAllFiles();
239 dec.EError -= Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
240 assertTrue (_errCnt == 0);
241 assertTrue (!dec.mapping().empty());
242}
243
244
245void ZipTest::verifyDataFile(const std::string& path, Poco::UInt64 size)
246{
247 Poco::FileInputStream in(path);
248 assertTrue ( ! in.fail() );
249 Poco::Buffer<char> buffer1(MB);
250 Poco::Buffer<char> buffer2(MB);
251 for (int i = 0; size != 0; i++)
252 {
253 std::memset(buffer1.begin(), i, buffer1.size());
254 std::memset(buffer2.begin(), 0, buffer2.size());
255 Poco::UInt64 bytesToRead = std::min(size, static_cast<Poco::UInt64>(buffer2.size()));
256 in.read(buffer2.begin(), bytesToRead);
257 assertTrue (!in.fail() );
258 assertTrue (std::memcmp(buffer1.begin(), buffer2.begin(), static_cast<std::size_t>(bytesToRead)) == 0);
259 size -= bytesToRead;
260 }
261 char c;
262 in.read(&c, 1);
263 assertTrue ( in.eof() );
264}
265
266
267void ZipTest::testDecompressZip64()
268{
269 std::map<std::string, Poco::UInt64> files;
270 files[Poco::Path::temp() + "data1.bin"] = static_cast<Poco::UInt64>(KB)*4096+1;
271 files[Poco::Path::temp() + "data2.bin"] = static_cast<Poco::UInt64>(KB)*16;
272 files[Poco::Path::temp() + "data3.bin"] = static_cast<Poco::UInt64>(KB)*4096-1;
273
274 for(std::map<std::string, Poco::UInt64>::const_iterator it = files.begin(); it != files.end(); it++)
275 {
276 Poco::File file(it->first);
277 if(file.exists())
278 file.remove();
279 }
280 Poco::FileInputStream in(Poco::Path::temp() + "zip64.zip");
281 Decompress c(in, Poco::Path::temp());
282 c.decompressAllFiles();
283 for(std::map<std::string, Poco::UInt64>::const_iterator it = files.begin(); it != files.end(); it++)
284 {
285 verifyDataFile(it->first, it->second);
286 }
287}
288
289
290void ZipTest::testValidPath()
291{
292 assertTrue (ZipCommon::isValidPath("."));
293 assertTrue (ZipCommon::isValidPath("file.txt"));
294 assertTrue (ZipCommon::isValidPath(".file.txt"));
295 assertTrue (ZipCommon::isValidPath("..file.txt"));
296 assertTrue (ZipCommon::isValidPath("file.txt.."));
297 assertTrue (ZipCommon::isValidPath(".file..txt"));
298 assertTrue (ZipCommon::isValidPath("~file..txt"));
299 assertTrue (ZipCommon::isValidPath("~file/~"));
300 assertTrue (ZipCommon::isValidPath("dir/~"));
301 assertTrue (ZipCommon::isValidPath("some"));
302 assertTrue (ZipCommon::isValidPath("some/dir"));
303 assertTrue (ZipCommon::isValidPath("some/dir/or/another"));
304 assertTrue (ZipCommon::isValidPath("some/dir/./another"));
305 assertTrue (ZipCommon::isValidPath("some/dir/or/another/file.txt"));
306 assertTrue (ZipCommon::isValidPath("s~me\\d.r\\.or..\\an..her\\file.txt"));
307 assertTrue (ZipCommon::isValidPath("some\\dir\\or\\another"));
308 assertTrue (ZipCommon::isValidPath("some\\dir\\or\\another\\file.txt"));
309 assertTrue (ZipCommon::isValidPath("s~me\\d.r/.or..\\an..her\\file.txt"));
310
311 assertTrue (!ZipCommon::isValidPath("/../"));
312 assertTrue (!ZipCommon::isValidPath("/"));
313 assertTrue (!ZipCommon::isValidPath("\\..\\"));
314 assertTrue (!ZipCommon::isValidPath("/..\\"));
315 assertTrue (!ZipCommon::isValidPath("\\../"));
316 assertTrue (!ZipCommon::isValidPath(".."));
317 assertTrue (!ZipCommon::isValidPath("~/"));
318 assertTrue (!ZipCommon::isValidPath("~/~"));
319 assertTrue (!ZipCommon::isValidPath("/~"));
320 assertTrue (!ZipCommon::isValidPath("/file.txt"));
321 assertTrue (!ZipCommon::isValidPath("~/file.txt"));
322 assertTrue (!ZipCommon::isValidPath("some/dir/or/../another/file.txt"));
323 assertTrue (!ZipCommon::isValidPath("C:\\Windows\\system32"));
324}
325
326
327void ZipTest::onDecompressError(const void* pSender, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>& info)
328{
329 ++_errCnt;
330}
331
332
333void ZipTest::setUp()
334{
335 _errCnt = 0;
336}
337
338
339void ZipTest::tearDown()
340{
341}
342
343
344CppUnit::Test* ZipTest::suite()
345{
346 CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ZipTest");
347
348 CppUnit_addTest(pSuite, ZipTest, testSkipSingleFile);
349 CppUnit_addTest(pSuite, ZipTest, testDecompressSingleFile);
350 CppUnit_addTest(pSuite, ZipTest, testDecompressSingleFileInDir);
351 CppUnit_addTest(pSuite, ZipTest, testDecompress);
352 CppUnit_addTest(pSuite, ZipTest, testDecompressFlat);
353 CppUnit_addTest(pSuite, ZipTest, testDecompressVuln);
354 CppUnit_addTest(pSuite, ZipTest, testDecompressFlatVuln);
355 CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterData);
356 CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataWithArchive);
357 CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataEncapsulated);
358 CppUnit_addTest(pSuite, ZipTest, testDecompressZip64);
359 CppUnit_addTest(pSuite, ZipTest, testValidPath);
360
361 return pSuite;
362}
363