| 297 | }; |
| 298 | |
| 299 | void ZipResourceProvider::Internal::Index() { |
| 300 | const unsigned char* data = archive->data; |
| 301 | const size_t size = archive->size; |
| 302 | if (size < 22) { |
| 303 | throw InvalidFormat("Invalid zip archive: " + archive->fileName); |
| 304 | } |
| 305 | |
| 306 | const size_t maxCommentSize = 65535; |
| 307 | const size_t searchStart = |
| 308 | size > maxCommentSize + 22 ? size - maxCommentSize - 22 : 0; |
| 309 | size_t eocdOffset = std::string::npos; |
| 310 | for (size_t pos = size - 22;; pos--) { |
| 311 | if (data[pos] == 0x50 && data[pos + 1] == 0x4b && data[pos + 2] == 0x05 && |
| 312 | data[pos + 3] == 0x06) { |
| 313 | eocdOffset = pos; |
| 314 | break; |
| 315 | } |
| 316 | if (pos == searchStart) { |
| 317 | break; |
| 318 | } |
| 319 | } |
| 320 | if (eocdOffset == std::string::npos) { |
| 321 | throw InvalidFormat("Invalid zip archive: " + archive->fileName); |
| 322 | } |
| 323 | |
| 324 | const uint16_t entryCount = ReadLe16(&data[eocdOffset + 10]); |
| 325 | const uint32_t centralDirectorySize = ReadLe32(&data[eocdOffset + 12]); |
| 326 | const uint32_t centralDirectoryOffset = ReadLe32(&data[eocdOffset + 16]); |
| 327 | if (centralDirectoryOffset > size || |
| 328 | centralDirectorySize > size - centralDirectoryOffset) { |
| 329 | throw InvalidFormat("Invalid zip central directory: " + archive->fileName); |
| 330 | } |
| 331 | |
| 332 | size_t pos = centralDirectoryOffset; |
| 333 | for (uint16_t i = 0; i < entryCount; i++) { |
| 334 | if (pos + 46 > size || ReadLe32(&data[pos]) != 0x02014b50) { |
| 335 | throw InvalidFormat("Invalid zip central directory: " + archive->fileName); |
| 336 | } |
| 337 | const uint16_t method = ReadLe16(&data[pos + 10]); |
| 338 | const uint32_t compressedSize = ReadLe32(&data[pos + 20]); |
| 339 | const uint32_t uncompressedSize = ReadLe32(&data[pos + 24]); |
| 340 | const uint16_t fileNameLength = ReadLe16(&data[pos + 28]); |
| 341 | const uint16_t extraLength = ReadLe16(&data[pos + 30]); |
| 342 | const uint16_t commentLength = ReadLe16(&data[pos + 32]); |
| 343 | const uint32_t localHeaderOffset = ReadLe32(&data[pos + 42]); |
| 344 | const size_t next = pos + 46 + fileNameLength + extraLength + commentLength; |
| 345 | if (next > size) { |
| 346 | throw InvalidFormat("Invalid zip central directory: " + archive->fileName); |
| 347 | } |
| 348 | const std::string name( |
| 349 | reinterpret_cast<const char*>(&data[pos + 46]), fileNameLength); |
| 350 | const std::string normalized = NormalizeResourceName(name); |
| 351 | if (!IsAbsolutePath(name) && !normalized.empty() && |
| 352 | normalized.back() != '/' && IsSafeZipResourceName(normalized)) { |
| 353 | if (localHeaderOffset + 30 > size || |
| 354 | ReadLe32(&data[localHeaderOffset]) != 0x04034b50) { |
| 355 | throw InvalidFormat("Invalid zip local header: " + archive->fileName); |
| 356 | } |
nothing calls this directly
no test coverage detected