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:
parent
0f5a6d035c
commit
98c23ece3e
3 changed files with 392 additions and 358 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
17
tests/misaligned_comment.luau
Normal file
17
tests/misaligned_comment.luau
Normal 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
|
Loading…
Add table
Reference in a new issue