1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <fstream>
16
17 #include "contrib/libzip/sandboxed.h"
18 #include "contrib/libzip/utils/utils_zip.h"
19 #include "sandboxed_api/util/path.h"
20 #include "sandboxed_api/util/status_matchers.h"
21 #include "sandboxed_api/util/temp_file.h"
22
23 namespace {
24
25 using ::sapi::IsOk;
26
27 class ZipBase : public testing::Test {
28 protected:
29 std::string GetTestFilePath(const std::string& filename);
30 std::string GetTemporaryFile(const std::string& filename);
31 std::streamsize GetStreamSize(std::ifstream& stream);
32
33 absl::StatusOr<std::vector<uint8_t>> ReadFile(const std::string& filename);
34
35 void SetUp() override;
36
37 const char* test_files_dir_;
38 std::string test_path_zip_;
39
40 std::unique_ptr<ZipSapiSandbox> sandbox_;
41 };
42
43 class ZipMultiFiles
44 : public ZipBase,
45 public testing::WithParamInterface<std::pair<uint64_t, std::string>> {};
46
SetUp()47 void ZipBase::SetUp() {
48 test_files_dir_ = getenv("TEST_FILES_DIR");
49 ASSERT_NE(test_files_dir_, nullptr);
50
51 test_path_zip_ = GetTestFilePath("zip.zip");
52
53 sandbox_ = std::make_unique<ZipSapiSandbox>();
54 ASSERT_THAT(sandbox_->Init(), IsOk());
55 }
56
ReadFile(const std::string & filename)57 absl::StatusOr<std::vector<uint8_t>> ZipBase::ReadFile(
58 const std::string& filename) {
59 std::ifstream file(filename, std::ios::binary);
60 if (!file.is_open()) {
61 return absl::UnavailableError("Unable to open file");
62 }
63
64 std::streamsize size = GetStreamSize(file);
65 std::vector<uint8_t> buf(size);
66
67 file.read(reinterpret_cast<char*>(buf.data()), size);
68
69 if (file.gcount() != size) {
70 return absl::UnavailableError("Unable to read data");
71 }
72
73 return buf;
74 }
75
GetTestFilePath(const std::string & filename)76 std::string ZipBase::GetTestFilePath(const std::string& filename) {
77 return sapi::file::JoinPath(test_files_dir_, filename);
78 }
79
GetTemporaryFile(const std::string & filename)80 std::string ZipBase::GetTemporaryFile(const std::string& filename) {
81 absl::StatusOr<std::string> tmp_file =
82 sapi::CreateNamedTempFileAndClose(filename);
83 if (!tmp_file.ok()) {
84 return "";
85 }
86
87 return sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), *tmp_file);
88 }
89
GetStreamSize(std::ifstream & stream)90 std::streamsize ZipBase::GetStreamSize(std::ifstream& stream) {
91 stream.seekg(0, std::ios_base::end);
92 std::streamsize ssize = stream.tellg();
93 stream.seekg(0, std::ios_base::beg);
94
95 return ssize;
96 }
97
TEST_F(ZipBase,CheckInit)98 TEST_F(ZipBase, CheckInit) {
99 LibZip zip(sandbox_.get(), test_path_zip_, 0);
100 ASSERT_THAT(zip.IsOpen(), true);
101 }
102
TEST_F(ZipBase,CheckFileCount)103 TEST_F(ZipBase, CheckFileCount) {
104 LibZip zip(sandbox_.get(), test_path_zip_, 0);
105 ASSERT_THAT(zip.IsOpen(), true);
106
107 SAPI_ASSERT_OK_AND_ASSIGN(uint64_t count, zip.GetNumberEntries());
108 ASSERT_EQ(count, 2);
109 }
110
TEST_F(ZipBase,AddFileBuf)111 TEST_F(ZipBase, AddFileBuf) {
112 LibZip zip(sandbox_.get(), test_path_zip_, 0);
113 ASSERT_THAT(zip.IsOpen(), true);
114
115 SAPI_ASSERT_OK_AND_ASSIGN(auto newdata,
116 ReadFile(GetTestFilePath("notinzip")));
117
118 SAPI_ASSERT_OK_AND_ASSIGN(uint64_t index, zip.AddFile("test", newdata));
119 SAPI_ASSERT_OK_AND_ASSIGN(uint64_t count, zip.GetNumberEntries());
120 ASSERT_EQ(count, 3);
121
122 SAPI_ASSERT_OK_AND_ASSIGN(std::string newname, zip.GetName(index));
123 ASSERT_EQ(newname, "test");
124 }
125
TEST_F(ZipBase,AddFileFd)126 TEST_F(ZipBase, AddFileFd) {
127 LibZip zip(sandbox_.get(), test_path_zip_, 0);
128 ASSERT_THAT(zip.IsOpen(), true);
129
130 int fd = open(GetTestFilePath("notinzip").c_str(), O_RDONLY);
131 ASSERT_GE(fd, 0);
132
133 SAPI_ASSERT_OK_AND_ASSIGN(uint64_t index, zip.AddFile("test", fd));
134 SAPI_ASSERT_OK_AND_ASSIGN(uint64_t count, zip.GetNumberEntries());
135 ASSERT_EQ(count, 3);
136
137 SAPI_ASSERT_OK_AND_ASSIGN(std::string newname, zip.GetName(index));
138 ASSERT_EQ(newname, "test");
139 }
140
TEST_F(ZipMultiFiles,AddFileBufInplaceStore)141 TEST_F(ZipMultiFiles, AddFileBufInplaceStore) {
142 std::string new_path_zip = GetTemporaryFile("newzip.zip");
143
144 ASSERT_TRUE(
145 sapi::file_util::fileops::CopyFile(test_path_zip_, new_path_zip, 0644));
146 LibZip zip(sandbox_.get(), new_path_zip, 0);
147 ASSERT_THAT(zip.IsOpen(), true);
148
149 SAPI_ASSERT_OK_AND_ASSIGN(auto newdata,
150 ReadFile(GetTestFilePath("notinzip")));
151 SAPI_ASSERT_OK_AND_ASSIGN(uint64_t index, zip.AddFile("test", newdata));
152
153 ASSERT_THAT(zip.Finish(), IsOk());
154 ASSERT_THAT(zip.Save(), IsOk());
155
156 LibZip newzip(sandbox_.get(), new_path_zip, 0);
157 ASSERT_THAT(newzip.IsOpen(), true);
158
159 SAPI_ASSERT_OK_AND_ASSIGN(auto nameddata, newzip.ReadFile("test"));
160 ASSERT_EQ(newdata, newdata);
161
162 SAPI_ASSERT_OK_AND_ASSIGN(auto indexeddata, newzip.ReadFile(index));
163 ASSERT_EQ(indexeddata, newdata);
164 }
165
TEST_P(ZipMultiFiles,CheckFileNames)166 TEST_P(ZipMultiFiles, CheckFileNames) {
167 LibZip zip(sandbox_.get(), test_path_zip_, 0);
168 ASSERT_THAT(zip.IsOpen(), true);
169
170 uint64_t index = GetParam().first;
171 std::string origname = GetParam().second;
172
173 SAPI_ASSERT_OK_AND_ASSIGN(std::string name, zip.GetName(index));
174 ASSERT_EQ(name, origname);
175 }
176
TEST_P(ZipMultiFiles,DeleteFile)177 TEST_P(ZipMultiFiles, DeleteFile) {
178 LibZip zip(sandbox_.get(), test_path_zip_, 0);
179 ASSERT_THAT(zip.IsOpen(), true);
180
181 uint64_t index = GetParam().first;
182 std::string origname = GetParam().second;
183
184 SAPI_ASSERT_OK_AND_ASSIGN(uint64_t count, zip.GetNumberEntries());
185 ASSERT_THAT(zip.DeleteFile(index), IsOk());
186
187 for (uint64_t i = 0; i < count; i++) {
188 absl::StatusOr<std::string> name = zip.GetName(i);
189 if (i == index) {
190 ASSERT_FALSE(name.ok());
191 } else {
192 ASSERT_THAT(name, IsOk());
193 ASSERT_NE(*name, origname);
194 }
195 }
196 }
197
TEST_P(ZipMultiFiles,ReadFileName)198 TEST_P(ZipMultiFiles, ReadFileName) {
199 LibZip zip(sandbox_.get(), test_path_zip_, 0);
200 ASSERT_THAT(zip.IsOpen(), true);
201
202 std::string name = GetParam().second;
203
204 SAPI_ASSERT_OK_AND_ASSIGN(auto zipdata, zip.ReadFile(name));
205 SAPI_ASSERT_OK_AND_ASSIGN(auto origdata, ReadFile(GetTestFilePath(name)));
206
207 ASSERT_EQ(zipdata, origdata);
208 }
209
TEST_P(ZipMultiFiles,ReadFileIndex)210 TEST_P(ZipMultiFiles, ReadFileIndex) {
211 LibZip zip(sandbox_.get(), test_path_zip_, 0);
212 ASSERT_THAT(zip.IsOpen(), true);
213
214 uint64_t index = GetParam().first;
215 std::string name = GetParam().second;
216
217 SAPI_ASSERT_OK_AND_ASSIGN(auto zipdata, zip.ReadFile(index));
218 SAPI_ASSERT_OK_AND_ASSIGN(auto origdata, ReadFile(GetTestFilePath(name)));
219
220 ASSERT_EQ(zipdata, origdata);
221 }
222
TEST_P(ZipMultiFiles,AddFileBufNewStore)223 TEST_P(ZipMultiFiles, AddFileBufNewStore) {
224 LibZip zip(sandbox_.get(), test_path_zip_, 0);
225 ASSERT_THAT(zip.IsOpen(), true);
226
227 SAPI_ASSERT_OK_AND_ASSIGN(auto newdata,
228 ReadFile(GetTestFilePath("notinzip")));
229 SAPI_ASSERT_OK_AND_ASSIGN(uint64_t index, zip.AddFile("test", newdata));
230
231 std::string new_zip_file_name = GetTemporaryFile("newzip.zip");
232 int newfdzip = open(new_zip_file_name.c_str(), O_WRONLY);
233 ASSERT_GE(newfdzip, 0);
234 ASSERT_THAT(zip.Finish(), IsOk());
235 ASSERT_THAT(zip.Save(newfdzip), IsOk());
236 close(newfdzip);
237
238 LibZip newzip(sandbox_.get(), new_zip_file_name, 0);
239 ASSERT_THAT(newzip.IsOpen(), true);
240
241 SAPI_ASSERT_OK_AND_ASSIGN(auto nameddata, newzip.ReadFile("test"));
242 ASSERT_EQ(newdata, newdata);
243
244 SAPI_ASSERT_OK_AND_ASSIGN(auto indexeddata, newzip.ReadFile(index));
245 ASSERT_EQ(indexeddata, newdata);
246
247 // We also check if non other data was corrupted
248 uint64_t oldindex = GetParam().first;
249 std::string name = GetParam().second;
250
251 SAPI_ASSERT_OK_AND_ASSIGN(auto zipdata, newzip.ReadFile(oldindex));
252 SAPI_ASSERT_OK_AND_ASSIGN(auto origdata, ReadFile(GetTestFilePath(name)));
253
254 ASSERT_EQ(zipdata, origdata);
255 }
256
TEST_P(ZipMultiFiles,AddFileFdStore)257 TEST_P(ZipMultiFiles, AddFileFdStore) {
258 LibZip zip(sandbox_.get(), test_path_zip_, 0);
259 ASSERT_THAT(zip.IsOpen(), true);
260
261 SAPI_ASSERT_OK_AND_ASSIGN(auto newdata,
262 ReadFile(GetTestFilePath("notinzip")));
263 int fd = open(GetTestFilePath("notinzip").c_str(), O_RDONLY);
264 ASSERT_GE(fd, 0);
265 SAPI_ASSERT_OK_AND_ASSIGN(uint64_t index, zip.AddFile("test", fd));
266
267 std::string new_zip_file_name = GetTemporaryFile("newzip.zip");
268 int newfdzip = open(new_zip_file_name.c_str(), O_WRONLY);
269 ASSERT_GE(newfdzip, 0);
270 ASSERT_THAT(zip.Finish(), IsOk());
271 ASSERT_THAT(zip.Save(newfdzip), IsOk());
272 close(newfdzip);
273
274 LibZip newzip(sandbox_.get(), new_zip_file_name, 0);
275 ASSERT_THAT(newzip.IsOpen(), true);
276
277 SAPI_ASSERT_OK_AND_ASSIGN(auto nameddata, newzip.ReadFile("test"));
278 ASSERT_EQ(nameddata, newdata);
279
280 SAPI_ASSERT_OK_AND_ASSIGN(auto indexeddata, newzip.ReadFile(index));
281 ASSERT_EQ(indexeddata, newdata);
282
283 // We also check if non other data was corrupted
284 uint64_t oldindex = GetParam().first;
285 std::string name = GetParam().second;
286
287 SAPI_ASSERT_OK_AND_ASSIGN(auto zipdata, newzip.ReadFile(oldindex));
288 SAPI_ASSERT_OK_AND_ASSIGN(auto origdata, ReadFile(GetTestFilePath(name)));
289
290 ASSERT_EQ(zipdata, origdata);
291 }
292
TEST_P(ZipMultiFiles,ReplaceFileBufStore)293 TEST_P(ZipMultiFiles, ReplaceFileBufStore) {
294 LibZip zip(sandbox_.get(), test_path_zip_, 0);
295 ASSERT_THAT(zip.IsOpen(), true);
296
297 uint64_t index = GetParam().first;
298 std::string name = GetParam().second;
299
300 SAPI_ASSERT_OK_AND_ASSIGN(auto newdata,
301 ReadFile(GetTestFilePath("notinzip")));
302 SAPI_ASSERT_OK_AND_ASSIGN(auto zipdata, zip.ReadFile(index));
303 ASSERT_NE(zipdata, newdata);
304
305 ASSERT_THAT(zip.ReplaceFile(index, newdata), IsOk());
306
307 std::string new_zip_file_name = GetTemporaryFile("newzip.zip");
308 int newfdzip = open(new_zip_file_name.c_str(), O_WRONLY);
309 ASSERT_GE(newfdzip, 0);
310 ASSERT_THAT(zip.Finish(), IsOk());
311 ASSERT_THAT(zip.Save(newfdzip), IsOk());
312 close(newfdzip);
313
314 LibZip newzip(sandbox_.get(), new_zip_file_name, 0);
315 ASSERT_THAT(newzip.IsOpen(), true);
316
317 SAPI_ASSERT_OK_AND_ASSIGN(auto nameddata, newzip.ReadFile(name));
318 ASSERT_EQ(nameddata, newdata);
319
320 SAPI_ASSERT_OK_AND_ASSIGN(auto indexeddata, newzip.ReadFile(index));
321 ASSERT_EQ(indexeddata, newdata);
322 }
323
TEST_P(ZipMultiFiles,ReplaceFileFdStore)324 TEST_P(ZipMultiFiles, ReplaceFileFdStore) {
325 LibZip zip(sandbox_.get(), test_path_zip_, 0);
326 ASSERT_THAT(zip.IsOpen(), true);
327
328 uint64_t index = GetParam().first;
329 std::string name = GetParam().second;
330
331 SAPI_ASSERT_OK_AND_ASSIGN(auto newdata,
332 ReadFile(GetTestFilePath("notinzip")));
333 SAPI_ASSERT_OK_AND_ASSIGN(auto zipdata, zip.ReadFile(index));
334 ASSERT_NE(zipdata, newdata);
335
336 int fd = open(GetTestFilePath("notinzip").c_str(), O_RDONLY);
337 ASSERT_GE(fd, 0);
338
339 ASSERT_THAT(zip.ReplaceFile(index, fd), IsOk());
340
341 std::string new_zip_file_name = GetTemporaryFile("newzip.zip");
342 int newfdzip = open(new_zip_file_name.c_str(), O_WRONLY);
343 ASSERT_GE(newfdzip, 0);
344 ASSERT_THAT(zip.Finish(), IsOk());
345 ASSERT_THAT(zip.Save(newfdzip), IsOk());
346 close(newfdzip);
347
348 LibZip newzip(sandbox_.get(), new_zip_file_name, 0);
349 ASSERT_THAT(newzip.IsOpen(), true);
350
351 SAPI_ASSERT_OK_AND_ASSIGN(auto nameddata, newzip.ReadFile(name));
352 ASSERT_EQ(nameddata, newdata);
353
354 SAPI_ASSERT_OK_AND_ASSIGN(auto indexeddata, newzip.ReadFile(index));
355 ASSERT_EQ(indexeddata, newdata);
356 }
357
TEST_P(ZipMultiFiles,DeleteFileStore)358 TEST_P(ZipMultiFiles, DeleteFileStore) {
359 LibZip zip(sandbox_.get(), test_path_zip_, 0);
360 ASSERT_THAT(zip.IsOpen(), true);
361
362 uint64_t index = GetParam().first;
363 std::string origname = GetParam().second;
364
365 SAPI_ASSERT_OK_AND_ASSIGN(uint64_t oldcount, zip.GetNumberEntries());
366
367 ASSERT_THAT(zip.DeleteFile(index), IsOk());
368
369 std::string new_zip_file_name = GetTemporaryFile("newzip.zip");
370 int newfdzip = open(new_zip_file_name.c_str(), O_WRONLY);
371 ASSERT_GE(newfdzip, 0);
372 ASSERT_THAT(zip.Finish(), IsOk());
373 ASSERT_THAT(zip.Save(newfdzip), IsOk());
374 close(newfdzip);
375
376 LibZip newzip(sandbox_.get(), new_zip_file_name, 0);
377 ASSERT_THAT(newzip.IsOpen(), true);
378
379 SAPI_ASSERT_OK_AND_ASSIGN(uint64_t newcount, newzip.GetNumberEntries());
380 ASSERT_LT(newcount, oldcount);
381
382 for (uint64_t i = 0; i < newcount; i++) {
383 absl::StatusOr<std::string> name = newzip.GetName(i);
384 ASSERT_THAT(name, IsOk());
385 ASSERT_NE(*name, origname);
386 }
387 }
388
389 INSTANTIATE_TEST_SUITE_P(ZipBase, ZipMultiFiles,
390 testing::Values(std::make_pair(0, "binary"),
391 std::make_pair(1, "text")));
392 } // namespace
393