blob: fc0b957a073e182ba9db710a11b3d1adbb674591 [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
#include <string>
#ifdef __WINDOWS__
#include <stout/windows.hpp>
#endif
#include <stout/archiver.hpp>
#include <stout/base64.hpp>
#include <stout/gtest.hpp>
#include <stout/os.hpp>
#include <stout/uuid.hpp>
#include <stout/os/write.hpp>
#include <stout/tests/utils.hpp>
using std::string;
class ArchiverTest : public TemporaryDirectoryTest {};
// No input file should return some error, not read from stdin.
TEST_F(ArchiverTest, ExtractEmptyInputFile)
{
EXPECT_ERROR(archiver::extract("", ""));
}
// File that does not exist should return some error.
TEST_F(ArchiverTest, ExtractInputFileNotFound)
{
// Construct a temporary file path that is guarenteed unique.
Try<string> dir = os::mkdtemp(path::join(sandbox.get(), "XXXXXX"));
ASSERT_SOME(dir);
string path = path::join(dir.get(), "ThisFileDoesNotExist");
EXPECT_ERROR(archiver::extract(path, ""));
}
TEST_F(ArchiverTest, ExtractTarGzFile)
{
// Construct a hello.tar.gz file that can be extracted.
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> path = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(path);
// Length Size Date Time Name Content
// -------- ------- ---------- ----- ---- ------
// 22 22 2018-02-21 10:06 hello Howdy there, partner!\n
// -------- ------- ------- ------
// 22 22 1 file
ASSERT_SOME(os::write(path.get(), base64::decode(
"H4sICE61jVoAA2hlbGxvLnRhcgDtzjEOwjAQRNGtOcXSU9hx7FyBa0RgK0IR"
"RsYIcfsEaGhQqggh/VfsFLPFDHEcs6zLzEJon2k7bz7zrQliXdM6Nx/vxVgb"
"uiBqVt71crvWvqjKKaZ0yOnr31L/p/b5fnxoHWKJO730pZ5j2W5+vQoAAAAA"
"AAAAAAAAAAAAsGQC2DPIjgAoAAA=").get()));
// Note: The file does NOT have a .tar.gz extension. We could rename
// it, but libarchive doesn't care about extensions. It determines
// the format from the contents of the file. So this is tested here
// as well.
EXPECT_SOME(archiver::extract(path.get(), ""));
string extractedFile = path::join(sandbox.get(), "hello");
ASSERT_TRUE(os::exists(extractedFile));
ASSERT_SOME_EQ("Howdy there, partner!\n", os::read(extractedFile));
}
TEST_F(ArchiverTest, ExtractTarFile)
{
// Construct a hello.tar file that can be extracted.
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> path = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(path);
// The .tar file, since not compressed, is long. So go through some
// pains to construct the contents to the file programmatically.
//
// We could skip the .tar file test, but it's worth having it.
//
// Length Size Date Time Name Content
// -------- ------- ---------- ----- ---- ------
// 10240 10240 2018-02-21 10:06 hello Howdy there, partner (.tar)!\n
// -------- ------- ------- ------
// 10240 10240 1 file
string tarContents =
"aGVsbG8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAADAwMDA2NjQAMDAwMTc1MAAwMDAxNzUwADAwMDAwMDAwMDM1"
"ADEzMjQ1MTA2NTE1ADAxMTY3NAAgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1c3RhciAgAGplZmZj"
"b2YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAamVmZmNvZgAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAABIb3dkeSB0aGVyZSwgcGFydG5lciEgKC50YXIp"
"CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
for (int i = 0; i < 214; i++)
{
tarContents +=
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
}
tarContents += "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
// Now write out the .tar file, extract contents, and verify results
ASSERT_SOME(os::write(path.get(), base64::decode(tarContents).get()));
EXPECT_SOME(archiver::extract(path.get(), ""));
string extractedFile = path::join(sandbox.get(), "hello");
ASSERT_TRUE(os::exists(extractedFile));
ASSERT_SOME_EQ("Howdy there, partner! (.tar)\n", os::read(extractedFile));
}
TEST_F(ArchiverTest, ExtractZipFile)
{
// Construct a hello.zip file that can be extracted.
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> path = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(path);
// Length Size Date Time Name Content
// -------- ------- ---------- ----- ---- ------
// 189 189 2018-02-26 15:06 hello Howdy there, partner! (.zip)\n
// -------- ------- ------- ------
// 189 189 1 file
ASSERT_SOME(os::write(path.get(), base64::decode(
"UEsDBAoAAAAAAMZ4WkxFOXeVHQAAAB0AAAAFABwAaGVsbG9VVAkAA+SSlFrk"
"kpRadXgLAAEE6AMAAAToAwAASG93ZHkgdGhlcmUsIHBhcnRuZXIhICguemlw"
"KQpQSwECHgMKAAAAAADGeFpMRTl3lR0AAAAdAAAABQAYAAAAAAABAAAAtIEA"
"AAAAaGVsbG9VVAUAA+SSlFp1eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBL"
"AAAAXAAAAAAA").get()));
EXPECT_SOME(archiver::extract(path.get(), ""));
string extractedFile = path::join(sandbox.get(), "hello");
ASSERT_TRUE(os::exists(extractedFile));
ASSERT_SOME_EQ("Howdy there, partner! (.zip)\n", os::read(extractedFile));
}
TEST_F(ArchiverTest, ExtractInvalidZipFile)
{
// Construct a hello.zip file that can be extracted.
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> path = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(path);
// Write broken zip to file [bad CRC 440a6aa5 (should be af083b2d)].
//
// Length Date Time CRC expected CRC actual Name Content
// -------- ---------- ----- ------------ ---------- ---- ------
// 12 2016-03-19 10:08 af083b2d 440a6aa5 world hello hello\n
// -------- ------- ------
// 12 1 file
ASSERT_SOME(os::write(path.get(), base64::decode(
"UEsDBAoAAAAAABBRc0gtOwivDAAAAAwAAAAFABwAd29ybG9VVAkAAxAX7VYQ"
"F+1WdXgLAAEE6AMAAARkAAAAaGVsbG8gaGVsbG8KUEsBAh4DCgAAAAAAEFFz"
"SC07CK8MAAAADAAAAAUAGAAAAAAAAQAAAKSBAAAAAHdvcmxkVVQFAAMQF+1W"
"dXgLAAEE6AMAAARkAAAAUEsFBgAAAAABAAEASwAAAEsAAAAAAA==").get()));
EXPECT_ERROR(archiver::extract(path.get(), ""));
}
TEST_F(ArchiverTest, ExtractZipFileWithDuplicatedEntries)
{
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> path = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(path);
// Create zip file with duplicates.
//
// Length Method Size Cmpr Date Time CRC-32 Name Content
// -------- ------ ------- ---- ---------- ----- -------- ---- -------
// 1 Stored 1 0% 2016-03-18 22:49 83dcefb7 A 1
// 1 Stored 1 0% 2016-03-18 22:49 1ad5be0d A 2
// -------- ------- --- ------- -------
// 2 2 0% 2 files
ASSERT_SOME(os::write(path.get(), base64::decode(
"UEsDBBQAAAAAADC2cki379yDAQAAAAEAAAABAAAAQTFQSwMEFAAAAAAAMrZy"
"SA2+1RoBAAAAAQAAAAEAAABBMlBLAQIUAxQAAAAAADC2cki379yDAQAAAAEA"
"AAABAAAAAAAAAAAAAACAAQAAAABBUEsBAhQDFAAAAAAAMrZySA2+1RoBAAAA"
"AQAAAAEAAAAAAAAAAAAAAIABIAAAAEFQSwUGAAAAAAIAAgBeAAAAQAAAAAAA").get()));
EXPECT_SOME(archiver::extract(path.get(), ""));
string extractedFile = path::join(sandbox.get(), "A");
ASSERT_TRUE(os::exists(extractedFile));
ASSERT_SOME_EQ("2", os::read(extractedFile));
}
TEST_F(ArchiverTest, ExtractTarXZFile)
{
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> path = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(path);
// Create a tar.xz compressed file.
//
// Length Size Date Time Name Content
// -------- ------- ---------- ----- ---- ------
// 192 192 2018-02-27 15:34 hello Hello world (xz)\n
// -------- ------- ------- ------
// 192 192 1 file
ASSERT_SOME(os::write(path.get(), base64::decode(
"/Td6WFoAAATm1rRGAgAhARYAAAB0L+Wj4Cf/AH5dADQZSe6N1/i4P8k3jGgA"
"rB4mJjQrf8ka7ajHWIxeYZoS+eGuA0Br4ooXZVdW4dnh8GpgDlbdfMQrOOPA"
"aJE3B9L56mP0ThtjwNuMhhc8/xiXsFOVeUf/xbgcqognut0NZNetr0p+FA/O"
"K6NqFHAjzSaANcbNj+iFfqY3sC/mAAAAADpda78LIiMIAAGaAYBQAADDUC3D"
"scRn+wIAAAAABFla").get()));
EXPECT_SOME(archiver::extract(path.get(), ""));
string extractedFile = path::join(sandbox.get(), "hello");
ASSERT_TRUE(os::exists(extractedFile));
ASSERT_SOME_EQ("Hello world (xz)\n", os::read(extractedFile));
}
TEST_F(ArchiverTest, ExtractTarBZ2File)
{
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> path = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(path);
// Create an tar.bz2 compressed file.
//
// Length Size Date Time Name Content
// -------- ------- ---------- ----- ---- ------
// 148 148 2018-02-27 15:34 hello Hello world (bzip2)\n
// -------- ------- ------- ------
// 148 148 1 file
ASSERT_SOME(os::write(path.get(), base64::decode(
"QlpoOTFBWSZTWZo+haYAAH//hMIRAgBAYH+AAEAACH903pAABAAIIAB0EpEa"
"IeiMJtAIeRP1BlNCA00AAAA+x2lRZBAgaACRM0TvUjA5RJAR6BfGS3MjVUIh"
"IUI0Yww9tmran651Du0Hk5ZN4pbSxgs5xlAlIjtgOImyv+auHhIXnipV/xXy"
"iIHQu5IpwoSE0fQtMA==").get()));
EXPECT_SOME(archiver::extract(path.get(), ""));
string extractedFile = path::join(sandbox.get(), "hello");
ASSERT_TRUE(os::exists(extractedFile));
ASSERT_SOME_EQ("Hello world (bzip2)\n", os::read(extractedFile));
}
TEST_F(ArchiverTest, ExtractTarBz2GzFile)
{
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> path = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(path);
// Create an tar.bz2.gz compressed file.
//
// Verify that archives compressed twice (in this case, .bzip2.gz)
// work. Libarchive will keep processing until fully extracted.
//
// Length Size Date Time Name Content
// -------- ------- ---------- ----- ---- ------
// 185 185 2018-02-27 15:46 hello Hello world (bzip2)\n
// -------- ------- ------- ------
// 185 185 1 file
ASSERT_SOME(os::write(path.get(), base64::decode(
"H4sICOPtlVoAA2hlbGxvLnRhci5iejIAAZQAa/9CWmg5MUFZJlNZmj6FpgAA"
"f/+EwhECAEBgf4AAQAAIf3TekAAEAAggAHQSkRoh6Iwm0Ah5E/UGU0IDTQAA"
"AD7HaVFkECBoAJEzRO9SMDlEkBHoF8ZLcyNVQiEhQjRjDD22atqfrnUO7QeT"
"lk3iltLGCznGUCUiO2A4ibK/5q4eEheeKlX/FfKIgdC7kinChITR9C0wSQeY"
"TJQAAAA=").get()));
EXPECT_SOME(archiver::extract(path.get(), ""));
string extractedFile = path::join(sandbox.get(), "hello");
ASSERT_TRUE(os::exists(extractedFile));
ASSERT_SOME_EQ("Hello world (bzip2)\n", os::read(extractedFile));
}
TEST_F(ArchiverTest, ExtractBz2FileFails)
{
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> path = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(path);
// Create an .bz2 compressed file.
//
// Libarchive does not appear to work without some sort of container
// (tar or zip or whatever). Verify that a regular file, compressed,
// will fail.
//
// Length Size Date Time Name Content
// -------- ------- ---------- ----- ---- ------
// 63 63 2018-02-27 17:00 hello Hello world (bzip2)\n
// -------- ------- ------- ------
// 63 63 1 file
ASSERT_SOME(os::write(path.get(), base64::decode(
"QlpoOTFBWSZTWTMaBKkAAANdgAAQQGAQAABAFiTQkCAAIhGCD1HoUwAE0auv"
"Imhs/86EgGxdyRThQkDMaBKk").get()));
EXPECT_ERROR(archiver::extract(path.get(), ""));
}
TEST_F(ArchiverTest, ExtractGzFileFails)
{
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> path = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(path);
// Create an .gz compressed file.
//
// Libarchive does not appear to work without some sort of container
// (tar or zip or whatever). Verify that a regular file, compressed,
// will fail.
//
// Length Size Date Time Name Content
// -------- ------- ---------- ----- ---- ------
// 43 43 2018-03-21 16:59 hello Hello world (gz)\n
// -------- ------- ------- ------
// 43 43 1 file
ASSERT_SOME(os::write(path.get(), base64::decode(
"H4sICNjxsloAA2hlbGxvAPNIzcnJVyjPL8pJUdBIr9LkAgAwtvTdEQAAAA==").get()));
EXPECT_ERROR(archiver::extract(path.get(), ""));
}
// TODO(josephw): Libarchive currently does not support creating symlinks
// on Windows (hardlinks are fine).
TEST_F_TEMP_DISABLED_ON_WINDOWS(
ArchiverTest, SYMLINK_ExtractTarGzFileWithLinks)
{
// Construct a tarball containing a hardlink and symlink.
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> path = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(path);
// This file was generated with the following commands:
// echo "I'm a link target" > target
// ln target hardlink
// ln -s target symlink
// tar -czf foo.tar.gz target hardlink symlink
// cat foo.tar.gz | base64
ASSERT_SOME(os::write(path.get(), base64::decode(
"H4sIADbiKlsAA+3UTQ6CMBCG4a49xezctqWlZ/AYJIL4gxqoMd5efhITF4obM"
"Mb32UxIJmXIxzRm9SaPalJa69Q56WpIfV+1HZ4H1opJEmN9ql1wok1ig1eipx"
"1rcGliVrej7E5Nfi6vl1d9bVtRvDln+BJ51B+xWlaSyWF73Evsf4XFtyfCnMq"
"sXnfpT/mO0f1v9+V5/33bqcTEGS6nP9//5lZNHX+fu/ef5e9tl78L2iix5A8A"
"AAAAAAAAAAAAADDqDhr0y40AKAAA").get()));
EXPECT_SOME(archiver::extract(path.get(), dir));
string extractedFile1 = path::join(dir, "target");
string extractedFile2 = path::join(dir, "hardlink");
string extractedFile3 = path::join(dir, "symlink");
ASSERT_TRUE(os::exists(extractedFile1));
ASSERT_TRUE(os::exists(extractedFile2));
ASSERT_TRUE(os::exists(extractedFile3));
ASSERT_SOME_EQ("I'm a link target\n", os::read(extractedFile1));
ASSERT_SOME_EQ("I'm a link target\n", os::read(extractedFile2));
ASSERT_SOME_EQ("I'm a link target\n", os::read(extractedFile3));
}
TEST_F(ArchiverTest, ExtractTarGzFileWithDestinationDir)
{
// Construct a hello.tar.gz file that can be extracted.
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> sourcePath = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(sourcePath);
// Length Size Date Time Name Content
// -------- ------- ---------- ----- ---- ------
// 22 22 2018-02-21 10:06 hello Howdy there, partner!\n
// -------- ------- ------- ------
// 22 22 1 file
ASSERT_SOME(os::write(sourcePath.get(), base64::decode(
"H4sICE61jVoAA2hlbGxvLnRhcgDtzjEOwjAQRNGtOcXSU9hx7FyBa0RgK0IR"
"RsYIcfsEaGhQqggh/VfsFLPFDHEcs6zLzEJon2k7bz7zrQliXdM6Nx/vxVgb"
"uiBqVt71crvWvqjKKaZ0yOnr31L/p/b5fnxoHWKJO730pZ5j2W5+vQoAAAAA"
"AAAAAAAAAAAAsGQC2DPIjgAoAAA=").get()));
// Make a destination directory to extract the archive to.
string destDir = path::join(dir, "somedestination");
ASSERT_SOME(os::mkdir(destDir));
// Note: The file does NOT have a .tar.gz extension. We could rename
// it, but libarchive doesn't care about extensions. It determines
// the format from the contents of the file. So this is tested here
// as well.
//
// Note: In this test, we extract the file to a destination directory
// and expect to find it there.
EXPECT_SOME(archiver::extract(sourcePath.get(), destDir));
string extractedFile = path::join(destDir, "hello");
ASSERT_TRUE(os::exists(extractedFile));
ASSERT_SOME_EQ("Howdy there, partner!\n", os::read(extractedFile));
}
TEST_F(ArchiverTest, ExtractZipFileWithDestinationDir)
{
// Construct a hello.zip file that can be extracted.
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> sourcePath = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(sourcePath);
// Length Size Date Time Name Content
// -------- ------- ---------- ----- ---- ------
// 189 189 2018-02-26 15:06 hello Howdy there, partner! (.zip)\n
// -------- ------- ------- ------
// 189 189 1 file
ASSERT_SOME(os::write(sourcePath.get(), base64::decode(
"UEsDBAoAAAAAAMZ4WkxFOXeVHQAAAB0AAAAFABwAaGVsbG9VVAkAA+SSlFrk"
"kpRadXgLAAEE6AMAAAToAwAASG93ZHkgdGhlcmUsIHBhcnRuZXIhICguemlw"
"KQpQSwECHgMKAAAAAADGeFpMRTl3lR0AAAAdAAAABQAYAAAAAAABAAAAtIEA"
"AAAAaGVsbG9VVAUAA+SSlFp1eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBL"
"AAAAXAAAAAAA").get()));
// Make a destination directory to extract the archive to.
string destDir = path::join(dir, "somedestination");
ASSERT_SOME(os::mkdir(destDir));
EXPECT_SOME(archiver::extract(sourcePath.get(), destDir));
string extractedFile = path::join(destDir, "hello");
ASSERT_TRUE(os::exists(extractedFile));
ASSERT_SOME_EQ("Howdy there, partner! (.zip)\n", os::read(extractedFile));
}
TEST_F(ArchiverTest, ExtractZipFileWithLongDestinationDir)
{
// Construct a hello.zip file that can be extracted.
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> sourcePath = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(sourcePath);
// Length Size Date Time Name Content
// -------- ------- ---------- ----- ---- ------
// 189 189 2018-02-26 15:06 hello Howdy there, partner! (.zip)\n
// -------- ------- ------- ------
// 189 189 1 file
ASSERT_SOME(os::write(sourcePath.get(), base64::decode(
"UEsDBAoAAAAAAMZ4WkxFOXeVHQAAAB0AAAAFABwAaGVsbG9VVAkAA+SSlFrk"
"kpRadXgLAAEE6AMAAAToAwAASG93ZHkgdGhlcmUsIHBhcnRuZXIhICguemlw"
"KQpQSwECHgMKAAAAAADGeFpMRTl3lR0AAAAdAAAABQAYAAAAAAABAAAAtIEA"
"AAAAaGVsbG9VVAUAA+SSlFp1eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBL"
"AAAAXAAAAAAA").get()));
// Make a destination directory to extract the archive to.
const size_t max_path_length = 260;
while (dir.length() <= max_path_length) {
dir = path::join(dir, id::UUID::random().toString());
}
EXPECT_TRUE(dir.length() > max_path_length);
ASSERT_SOME(os::mkdir(dir));
EXPECT_SOME(archiver::extract(sourcePath.get(), dir));
string extractedFile = path::join(dir, "hello");
ASSERT_TRUE(os::exists(extractedFile));
ASSERT_SOME_EQ("Howdy there, partner! (.zip)\n", os::read(extractedFile));
}
TEST_F(ArchiverTest, ExtractZipWithDotDot)
{
// Construct a exploit.zip file that should not be extracted.
string dir = path::join(sandbox.get(), "somedir");
ASSERT_SOME(os::mkdir(dir));
Try<string> path = os::mktemp(path::join(dir, "XXXXXX"));
ASSERT_SOME(path);
// Extract from the same directory as the zip file.
ASSERT_SOME(os::chdir(dir));
// This is a file contructed with the following Python code:
// import zipfile
// zip = zipfile.ZipFile("exploit.zip", "w")
// zip.writestr("../unsecure_file.txt", "content")
// zip.close()
ASSERT_SOME(os::write(path.get(), base64::decode(
"UEsDBBQAAAAAACZbZk6pMMX+BwAAAAcAAAAUAAAALi4vdW5zZWN1cmVfZmls"
"ZS50eHRjb250ZW50UEsBAhQDFAAAAAAAJltmTqkwxf4HAAAABwAAABQAAAAA"
"AAAAAAAAAIABAAAAAC4uL3Vuc2VjdXJlX2ZpbGUudHh0UEsFBgAAAAABAAEA"
"QgAAADkAAAAAAA==").get()));
string extractedFile = path::join(sandbox.get(), "unsecure_file.txt");
EXPECT_ERROR(archiver::extract(path.get(), ""));
ASSERT_FALSE(os::exists(extractedFile));
// Just to sanity check, extract again, with the secure flag disabled.
EXPECT_SOME(archiver::extract(path.get(), "", 0));
ASSERT_TRUE(os::exists(extractedFile));
}