fix: zip may fail for 4GB+ files

Massimo Melina committed May 25, 2022 at 23:18 UTC 1ed5d273c3002205b3a3ef18873d2c633e144fc6
2 files changed +8 -15
server/src/QuickZipStream.ts
+6 -13
@@ -47,21 +47,20 @@ export class QuickZipStream extends Readable {
47 if (!value) break
48 this.prewalk.push(value) // we keep same shape of the generator, so
49 }
50 - let size = 0
50 + let offset = 0
51 let centralDirSize = 0
52 for (const file of this.prewalk) {
53 const pathSize = Buffer.from(file.path, 'utf8').length
54 - const sizeSize = size > ZIP64_LIMIT ? 8 : 4
55 - const extraLength = (file.size > ZIP64_LIMIT ? 2 : 0) + (size > ZIP64_LIMIT ? 1 : 0)
54 + const extraLength = (file.size > ZIP64_LIMIT ? 2 : 0) + (offset > ZIP64_LIMIT ? 1 : 0)
55 const extraDataSize = extraLength && (2+2 + extraLength*8)
57 - size += 4+2+2+2+ 4+4+4+4+ 2+2+ pathSize + file.size +4+4 +sizeSize*2
56 + offset += 4+2+2+2+ 4+4+4+4+ 2+2+ pathSize + file.size
57 centralDirSize += 4+2+2+2+2+ 4+4+4+4+ 2+2+2+2+2+ 4+4 + pathSize + extraDataSize
58 }
60 - const centralOffset = size
59 + const centralOffset = offset
60 if (centralOffset > ZIP64_LIMIT)
61 centralDirSize += 4+8+2+2+4+4+8+8+8+8+4+4+8+4
62 centralDirSize += 4+4+2+2+4+4+2
64 - return size + centralDirSize
63 + return offset + centralDirSize
64 }
65
66 async _read(): Promise<void> {
@@ -105,13 +104,7 @@ export class QuickZipStream extends Readable {
104 this.workingFile = false
105 const extAttr = !mode ? 0 : (mode | 0x8000) * 0x10000 // it's like <<16 but doesn't overflow so easily
106 this.centralDir.push({ size, crc:crc!, pathAsBuffer, ts, offset, version, extAttr })
108 - const sizeSize = size > ZIP64_LIMIT ? 8 : 4
109 - this._push([
110 - 4, 0x08074b50,
111 - 4, crc,
112 - sizeSize, size,
113 - sizeSize, size,
114 - ])
107 + this.push('') // continue piping
108 })
109 }
110
tests/test.ts
+2 -2
@@ -65,8 +65,8 @@ describe('basics', () => {
65 it('protectFromAbove', req('/protectFromAbove/child/alfa.txt', 403))
66 it('protectFromAbove.list', reqList('/protectFromAbove/child/', { outList:['alfa.txt'] }))
67
68 - it('zip.head', req('/f1/?get=zip', { empty:true, length:13074 }, { method:'HEAD' }) )
69 - it('zip.alfa is forbidden', req('/protectFromAbove/child/?get=zip&list=alfa.txt*renamed', { empty: true, length:138 }, { method:'HEAD' }))
68 + it('zip.head', req('/f1/?get=zip', { empty:true, length:13010 }, { method:'HEAD' }) )
69 + it('zip.alfa is forbidden', req('/protectFromAbove/child/?get=zip&list=alfa.txt*renamed', { empty: true, length:118 }, { method:'HEAD' }))
70 it('login', reqApi('login', { username, password }, 406)) // by default, we don't support clear-text login
71
72 it('referer', req('/f1/page/gpl.png', 403, {