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 | |
34 | using namespace Poco::Zip; |
35 | |
36 | |
37 | ZipTest::ZipTest(const std::string& name): CppUnit::TestCase(name) |
38 | { |
39 | } |
40 | |
41 | |
42 | ZipTest::~ZipTest() |
43 | { |
44 | } |
45 | |
46 | |
47 | void 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 | |
69 | void 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 | |
98 | void 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 | |
113 | void 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 | |
128 | void 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 | |
142 | void 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 | |
164 | std::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 | |
189 | void 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 | |
203 | void 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 | |
217 | void 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 | |
231 | void 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 | |
245 | void 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 | |
267 | void 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 | |
290 | void 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 | |
327 | void ZipTest::(const void* pSender, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>& info) |
328 | { |
329 | ++_errCnt; |
330 | } |
331 | |
332 | |
333 | void ZipTest::setUp() |
334 | { |
335 | _errCnt = 0; |
336 | } |
337 | |
338 | |
339 | void ZipTest::tearDown() |
340 | { |
341 | } |
342 | |
343 | |
344 | CppUnit::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 | |