From 407ad8093f9a8ed28cdf88aebc967ba708de10fd Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Thu, 10 Sep 2026 23:18:22 +1000 Subject: [PATCH] Report malformed zip headers instead of raising A central directory record or local header cut short of its length fields made zip_entries and zip_data do arithmetic on nil, so the parse raised where every other failure returns nil, err. Bound both against the buffer first. A pax record claiming a zero length no longer spins forever. --- lua/arti-pack/archive.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lua/arti-pack/archive.lua b/lua/arti-pack/archive.lua index 4403602..88e83fc 100644 --- a/lua/arti-pack/archive.lua +++ b/lua/arti-pack/archive.lua @@ -140,6 +140,11 @@ local function zip_entries(buf) if u32(buf, pos) ~= CD_SIG then return nil, 'malformed central directory' end + -- The fixed 46-byte header has to be there in full, otherwise the fields + -- read below are nil and the parse blows up instead of reporting. + if pos + 45 > #buf then + return nil, 'truncated central directory' + end local namelen = u16(buf, pos + 28) local entry = { version_made_by = u16(buf, pos + 4), @@ -166,6 +171,9 @@ local function zip_data(buf, entry) if u32(buf, lho) ~= LOCAL_SIG then return reject(entry.name, 'invalid local header signature') end + if lho + 29 > #buf then + return reject(entry.name, 'truncated local header') + end local start = lho + 30 + u16(buf, lho + 26) + u16(buf, lho + 28) local data @@ -262,6 +270,10 @@ local function pax_records(block) break end len = tonumber(len) + -- A zero length would leave pos where it is and spin forever. + if len < 1 then + break + end local record = block:sub(rest, pos + len - 2) local key, value = record:match('^([^=]+)=(.*)$') if key then