1
1
Fork 0
mirror of https://github.com/0x5eal/luau-unzip.git synced 2025-04-16 03:43:44 +01:00

fix: correctly parse EoCD for misaligned comment sizes

Also adds a test case for the same.
This commit is contained in:
Erica Marigold 2025-01-07 13:29:39 +00:00
parent 0f5a6d035c
commit 98c23ece3e
Signed by: DevComp
GPG key ID: 429EF1C337871656
3 changed files with 392 additions and 358 deletions

View file

@ -115,6 +115,7 @@ export type ZipReader = typeof(setmetatable({} :: ZipReaderInner, { __index = Zi
-- stylua: ignore
type ZipReaderInner = {
data: buffer, -- The buffer containing the raw bytes of the ZIP
comment: string, -- Comment associated with the ZIP
entries: { ZipEntry }, -- The decoded entries present
directories: { [string]: ZipEntry }, -- The directories and their respective entries
root: ZipEntry, -- The entry of the root directory
@ -143,50 +144,68 @@ function ZipReader.parseCentralDirectory(self: ZipReader): ()
-- ZIP files are read from the end, starting with the End of Central Directory record
-- The EoCD is at least 22 bytes and contains pointers to the rest of the ZIP structure
local bufSize = buffer.len(self.data)
-- Start from the minimum possible position of EoCD (22 bytes from end)
local minPos = math.max(0, bufSize - (22 + 65535) --[[ max comment size: 64 KiB ]])
local pos = bufSize - 22
-- Search backwards for the EoCD signature
while pos > 0 do
-- Read 4 bytes as uint32 in little-endian format
while pos >= minPos do
if buffer.readu32(self.data, pos) == SIGNATURES.END_OF_CENTRAL_DIR then
break
end
pos -= 1
end
-- Central Directory offset is stored 16 bytes into the EoCD record
-- Verify we found the signature
if pos < minPos then
error("Could not find End of Central Directory signature")
end
-- End of Central Directory format:
-- Offset Bytes Description
-- 0 4 End of central directory signature
-- 4 2 Number of this disk
-- 6 2 Disk where central directory starts
-- 8 2 Number of central directory records on this disk
-- 10 2 Total number of central directory records
-- 12 4 Size of central directory (bytes)
-- 16 4 Offset of start of central directory
-- 20 2 Comment length (n)
-- 22 n Comment
local cdOffset = buffer.readu32(self.data, pos + 16)
-- Number of entries is stored 10 bytes into the EoCD record
local cdEntries = buffer.readu16(self.data, pos + 10)
local cdCommentLength = buffer.readu16(self.data, pos + 20)
self.comment = buffer.readstring(self.data, pos + 22, cdCommentLength)
-- Process each entry in the Central Directory
pos = cdOffset
for i = 1, cdEntries do
-- Central Directory Entry format:
-- Offset Bytes Description
-- ------------------------------------------------
-- 0 4 Central directory entry signature
-- 8 2 General purpose bitflags
-- 10 2 Compression method (8 = DEFLATE)
-- 12 4 Last mod time/date
-- 16 4 CRC-32
-- 24 4 Uncompressed size
-- 28 2 File name length (n)
-- 30 2 Extra field length (m)
-- 32 2 Comment length (k)
-- 16 4 CRC-32
-- 24 4 Uncompressed size
-- 42 4 Local header offset
-- 46 n File name
-- 46+n m Extra field
-- 46+n+m k Comment
local _bitflags = buffer.readu16(self.data, pos + 8)
local timestamp = buffer.readu32(self.data, pos + 12)
local compressionMethod = buffer.readu16(self.data, pos + 10)
local crc = buffer.readu32(self.data, pos + 16)
local size = buffer.readu32(self.data, pos + 24)
local nameLength = buffer.readu16(self.data, pos + 28)
local extraLength = buffer.readu16(self.data, pos + 30)
local commentLength = buffer.readu16(self.data, pos + 32)
local timestamp = buffer.readu32(self.data, pos + 12)
local crc = buffer.readu32(self.data, pos + 16)
local size = buffer.readu32(self.data, pos + 24)
local offset = buffer.readu32(self.data, pos + 42)
local name = buffer.readstring(self.data, pos + 46, nameLength)

View file

@ -19,8 +19,6 @@ local FALLIBLES = {
"chinese.zip",
"non_utf8.zip", -- FIXME: Lune breaks for non utf8 data in process stdout
"pandoc_soft_links.zip", -- FIXME: Soft links are not handled correctly
-- FIXME: Files with a misaligned comments are not correctly located
-- "misaligned_comment.zip",
}
return function(test: typeof(frktest.test))

View file

@ -0,0 +1,17 @@
local fs = require("@lune/fs")
local frktest = require("../lune_packages/frktest")
local check = frktest.assert.check
local ZipReader = require("../lib")
return function(test: typeof(frktest.test))
test.suite("ZIP extraction tests", function()
test.case("Handles misaligned comment properly", function()
local data = fs.readFile("tests/data/misaligned_comment.zip")
local zip = ZipReader.load(buffer.fromstring(data))
check.equal(zip.comment, "short.")
end)
end)
end