diff --git a/deps/zlib/google/zip_reader.cc b/deps/zlib/google/zip_reader.cc index 002a1e3ed01d35..7b5b28967cfbb9 100644 --- a/deps/zlib/google/zip_reader.cc +++ b/deps/zlib/google/zip_reader.cc @@ -16,6 +16,7 @@ #include "base/files/file.h" #include "base/files/file_util.h" #include "base/functional/bind.h" +#include "base/i18n/i18n_constants.h" #include "base/i18n/icu_string_conversions.h" #include "base/logging.h" #include "base/numerics/safe_conversions.h" @@ -307,25 +308,63 @@ bool ZipReader::OpenEntry() { DCHECK(path_in_zip[info.size_filename] == '\0'); entry_.path_in_original_encoding = path_in_zip.data(); + const char* const configured_encoding = + encoding_.empty() ? base::kCodepageUTF8 : encoding_.c_str(); + const char* entry_path_encoding = configured_encoding; + bool physical_path_is_directory = false; + bool physical_path_is_unsafe = false; + + // If an Info-ZIP Unicode Path Extra Field is present, the physical Central + // Directory path is about to be overridden. Decode and normalize it now into + // `entry_.physical_path` so consumers (e.g. Safe Browsing) can still see the + // name that other tools (e.g. Windows Explorer) would use for extraction. if (info.size_utf8_filename > 0) { + std::u16string physical_path_in_utf16; + if (!base::CodepageToUTF16(entry_.path_in_original_encoding, + configured_encoding, + base::OnStringConversionError::SUBSTITUTE, + &physical_path_in_utf16)) { + LOG(ERROR) << "Cannot convert path from encoding " << configured_encoding; + return false; + } + // Normalize() stores the normalized result in entry_.path; copy it before + // applying the Unicode Path Extra Field below. + Normalize(physical_path_in_utf16); + entry_.physical_path = entry_.path; + physical_path_is_directory = entry_.is_directory; + physical_path_is_unsafe = entry_.is_unsafe; + // Use the Info-ZIP Unicode Path Extra Field if present. DCHECK(info.utf8_filename[info.size_utf8_filename] == '\0'); entry_.path_in_original_encoding = info.utf8_filename; + entry_path_encoding = base::kCodepageUTF8; } // Convert path from original encoding to Unicode. std::u16string path_in_utf16; - const char* const encoding = encoding_.empty() ? "UTF-8" : encoding_.c_str(); - if (!base::CodepageToUTF16(entry_.path_in_original_encoding, encoding, + if (!base::CodepageToUTF16(entry_.path_in_original_encoding, + entry_path_encoding, base::OnStringConversionError::SUBSTITUTE, &path_in_utf16)) { - LOG(ERROR) << "Cannot convert path from encoding " << encoding; + LOG(ERROR) << "Cannot convert path from encoding " << entry_path_encoding; return false; } // Normalize path. Normalize(path_in_utf16); + if (info.size_utf8_filename > 0) { + // Treat an entry as a directory only if both names are directories; + // otherwise callers that analyze file entries should inspect it as a file. + entry_.is_directory = entry_.is_directory && physical_path_is_directory; + // Treat an entry as unsafe if either name is unsafe. + entry_.is_unsafe = entry_.is_unsafe || physical_path_is_unsafe; + } else { + // In the common case (no Unicode Path Extra Field) the physical path + // matches the effective path. + entry_.physical_path = entry_.path; + } + entry_.original_size = info.uncompressed_size; // The file content of this entry is encrypted if flag bit 0 is set. diff --git a/deps/zlib/google/zip_reader.h b/deps/zlib/google/zip_reader.h index 8bc4a21d27c344..af7536149f6fd4 100644 --- a/deps/zlib/google/zip_reader.h +++ b/deps/zlib/google/zip_reader.h @@ -130,6 +130,11 @@ class ZipReader { // ./a -> DOT/a base::FilePath path; + // Physical path from the ZIP Central Directory, before applying the + // Info-ZIP Unicode Path Extra Field. This is converted and normalized in + // the same way as `path`. + base::FilePath physical_path; + // Size of the original uncompressed file, or 0 if the entry is a directory. // This value should not be trusted, because it is stored as metadata in the // ZIP archive and can be different from the real uncompressed size. diff --git a/deps/zlib/google/zip_reader_unittest.cc b/deps/zlib/google/zip_reader_unittest.cc index 3de65688b7e18e..578539ffbf7616 100644 --- a/deps/zlib/google/zip_reader_unittest.cc +++ b/deps/zlib/google/zip_reader_unittest.cc @@ -998,6 +998,123 @@ TEST_F(ZipReaderTest, WrongFilenameLength) { reader.Next(); } +TEST_F(ZipReaderTest, UnicodePathExtraFieldPreservesPhysicalPath) { + static const char test_data[] = { + 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x03, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x71, + 0x91, 0x4e, 0x11, 0x2c, 0xf9, 0x51, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x6c, 0x77, 0x61, 0x72, + 0x65, 0x2e, 0x65, 0x78, 0x65, 0x54, 0x65, 0x73, 0x74, 0x20, 0x64, 0x61, + 0x74, 0x61, 0x50, 0x4b, 0x01, 0x02, 0x3f, 0x03, 0x0a, 0x03, 0x00, 0x00, + 0x00, 0x00, 0xd0, 0x71, 0x91, 0x4e, 0x11, 0x2c, 0xf9, 0x51, 0x09, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0xc9, 0x81, 0x00, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x6c, 0x77, 0x61, 0x72, 0x65, 0x2e, 0x65, 0x78, 0x65, 0x75, + 0x70, 0x11, 0x00, 0x01, 0xed, 0x4b, 0x16, 0x3c, 0x64, 0x6f, 0x77, 0x6e, + 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x74, 0x78, 0x74, 0x50, 0x4b, 0x05, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00}; + + std::string test_string(test_data, sizeof(test_data)); + ZipReader reader; + ASSERT_TRUE(reader.OpenFromString(test_string)); + const ZipReader::Entry* entry = reader.Next(); + ASSERT_TRUE(entry); + // The Unicode Path Extra Field overrides the Central Directory filename, + // but the original physical path is preserved separately. `is_unsafe` tracks + // path traversal safety, not whether the filename looks executable. + EXPECT_EQ(base::FilePath::FromUTF8Unsafe("download.txt"), entry->path); + EXPECT_EQ(base::FilePath::FromUTF8Unsafe("malware.exe"), + entry->physical_path); + EXPECT_FALSE(entry->is_directory); + EXPECT_FALSE(entry->is_unsafe); +} + +TEST_F(ZipReaderTest, UnicodePathExtraFieldUsesUtf8WithConfiguredEncoding) { + static constexpr uint8_t test_data[] = { + 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x03, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x71, + 0x91, 0x4e, 0x11, 0x2c, 0xf9, 0x51, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x6c, 0x77, 0x61, 0x72, + 0x65, 0x2e, 0x65, 0x78, 0x65, 0x54, 0x65, 0x73, 0x74, 0x20, 0x64, 0x61, + 0x74, 0x61, 0x50, 0x4b, 0x01, 0x02, 0x3f, 0x03, 0x0a, 0x03, 0x00, 0x00, + 0x00, 0x00, 0xd0, 0x71, 0x91, 0x4e, 0x11, 0x2c, 0xf9, 0x51, 0x09, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0xc9, 0x81, 0x00, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x6c, 0x77, 0x61, 0x72, 0x65, 0x2e, 0x65, 0x78, 0x65, 0x75, + 0x70, 0x11, 0x00, 0x01, 0xed, 0x4b, 0x16, 0x3c, 0x72, 0xc3, 0xa9, 0x73, + 0x75, 0x6d, 0xc3, 0xa9, 0x2e, 0x74, 0x78, 0x74, 0x50, 0x4b, 0x05, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00}; + + std::string test_string(reinterpret_cast(test_data), + sizeof(test_data)); + ZipReader reader; + ASSERT_TRUE(reader.OpenFromString(test_string)); + reader.SetEncoding("windows-1252"); + const ZipReader::Entry* entry = reader.Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(base::FilePath::FromUTF8Unsafe("résumé.txt"), entry->path); + EXPECT_EQ(base::FilePath::FromUTF8Unsafe("malware.exe"), + entry->physical_path); +} + +TEST_F(ZipReaderTest, UnicodePathExtraFieldFileIfEitherPathIsFile) { + static const char test_data[] = { + 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x03, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x71, + 0x91, 0x4e, 0x11, 0x2c, 0xf9, 0x51, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x6c, 0x77, 0x61, 0x72, + 0x65, 0x2e, 0x65, 0x78, 0x65, 0x2f, 0x54, 0x65, 0x73, 0x74, 0x20, 0x64, + 0x61, 0x74, 0x61, 0x50, 0x4b, 0x01, 0x02, 0x3f, 0x03, 0x0a, 0x03, 0x00, + 0x00, 0x00, 0x00, 0xd0, 0x71, 0x91, 0x4e, 0x11, 0x2c, 0xf9, 0x51, 0x09, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0xc9, 0x81, 0x00, 0x00, 0x00, + 0x00, 0x6d, 0x61, 0x6c, 0x77, 0x61, 0x72, 0x65, 0x2e, 0x65, 0x78, 0x65, + 0x2f, 0x75, 0x70, 0x10, 0x00, 0x01, 0x5a, 0x5a, 0x54, 0xa7, 0x6d, 0x61, + 0x6c, 0x77, 0x61, 0x72, 0x65, 0x2e, 0x65, 0x78, 0x65, 0x50, 0x4b, 0x05, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4e, 0x00, 0x00, + 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00}; + + std::string test_string(test_data, sizeof(test_data)); + ZipReader reader; + ASSERT_TRUE(reader.OpenFromString(test_string)); + const ZipReader::Entry* entry = reader.Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(base::FilePath::FromUTF8Unsafe("malware.exe"), entry->path); + EXPECT_EQ(base::FilePath::FromUTF8Unsafe("malware.exe/"), + entry->physical_path); + EXPECT_FALSE(entry->is_directory); + EXPECT_FALSE(entry->is_unsafe); +} + +TEST_F(ZipReaderTest, UnicodePathExtraFieldPreservesUnsafePhysicalPath) { + static const char test_data[] = { + 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x03, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x71, + 0x91, 0x4e, 0x11, 0x2c, 0xf9, 0x51, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x6c, + 0x77, 0x61, 0x72, 0x65, 0x2e, 0x65, 0x78, 0x65, 0x2f, + 0x2e, 0x2e, 0x54, 0x65, 0x73, 0x74, + 0x20, 0x64, 0x61, 0x74, 0x61, 0x50, 0x4b, 0x01, 0x02, 0x3f, 0x03, 0x0a, + 0x03, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x71, 0x91, 0x4e, 0x11, 0x2c, 0xf9, + 0x51, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x15, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0xc9, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x6d, 0x61, 0x6c, 0x77, 0x61, 0x72, + 0x65, 0x2e, 0x65, 0x78, 0x65, 0x2f, 0x2e, 0x2e, 0x75, + 0x70, 0x11, 0x00, 0x01, 0x7c, 0xbc, 0xe2, 0x5d, 0x64, + 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x74, + 0x78, 0x74, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x51, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00}; + + std::string test_string(test_data, sizeof(test_data)); + ZipReader reader; + ASSERT_TRUE(reader.OpenFromString(test_string)); + const ZipReader::Entry* entry = reader.Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(base::FilePath::FromUTF8Unsafe("download.txt"), entry->path); + EXPECT_EQ(base::FilePath::FromUTF8Unsafe("malware.exe/UP"), + entry->physical_path); + EXPECT_FALSE(entry->is_directory); + EXPECT_TRUE(entry->is_unsafe); +} + class FileWriterDelegateTest : public ::testing::Test { protected: void SetUp() override { diff --git a/src/zlib_version.h b/src/zlib_version.h index a258bd21a55dc8..302282a2027e04 100644 --- a/src/zlib_version.h +++ b/src/zlib_version.h @@ -2,5 +2,5 @@ // Refer to tools/dep_updaters/update-zlib.sh #ifndef SRC_ZLIB_VERSION_H_ #define SRC_ZLIB_VERSION_H_ -#define ZLIB_VERSION "1.3.2.1-motley-8b3aa8a" +#define ZLIB_VERSION "1.3.2.1-motley-42c2f19" #endif // SRC_ZLIB_VERSION_H_