fix: avoid exception in console for null byte
Massimo Melina committed
Jan 19, 2026 at 11:38 UTC
e226ebe654b5caed9b126eafb83076f436b8c087
2 files changed
+7
-3
src/zip.ts
+5
-2
@@ -20,7 +20,10 @@ export async function zipStreamFromFolder(node: VfsNode, ctx: Koa.Context) {
20
ctx.mime = 'zip'
21
// ctx.query.list is undefined | string | string[]
22
const name = list?.length === 1 ? safeDecodeURIComponent(basename(list[0]!), '') : getNodeName(node)
23
- forceDownload(ctx, (isWindowsDrive(name) ? name[0] : (name || 'archive')) + '.zip')
23
+ try {
24
+ forceDownload(ctx, (isWindowsDrive(name) ? name[0] : (name || 'archive')) + '.zip')
25
+ }
26
+ catch { return ctx.status = 400 }
27
const { filterName, filterComment } = paramsToFilter(ctx.query)
28
const walker = !list ? walkNode(node, { ctx, requiredPerm: 'can_archive' })
29
: (async function*(): AsyncIterableIterator<VfsNode> {
@@ -29,7 +32,7 @@ export async function zipStreamFromFolder(node: VfsNode, ctx: Koa.Context) {
32
const subNode = await urlToNode(uri, ctx, node)
33
if (!subNode)
34
continue
32
- if (nodeIsFolder(subNode)) { // a directory needs to walked
35
+ if (nodeIsFolder(subNode)) { // a directory needs to be walked
36
if (hasPermission(subNode, 'can_list', ctx) && hasPermission(subNode, 'can_archive', ctx)) {
37
yield subNode // it could be empty
38
yield* walkNode(subNode, { ctx, prefixPath: decodeURI(uri) + '/', requiredPerm: 'can_archive' })
tests/test.ts
+2
-1
@@ -147,7 +147,8 @@ describe('basics', () => {
147
test('zip.partial', req('/f1/?get=zip', { re:/^page$/, length: zipLength }, { headers: { Range: `bytes=${zipOfs}-${zipOfs+zipLength-1}` } }) )
148
test('zip.partial.resume', req('/f1/?get=zip', { re:/^page/, length:zipSize-zipOfs }, { headers: { Range: `bytes=${zipOfs}-` } }) )
149
test('zip.partial.end', req('/f1/f2/?get=zip', { re:/^6/, length:10 }, { headers: { Range: 'bytes=-10' } }) )
150
- test('zip.list.bad encoding', req('/f1/?get=zip&list=%E0%A4%A', { status: 200, length: 22 })) // basically empty
150
+ test('zip.list.bad encoding', req('/f1/?get=zip&list=%E0%A4%A//%00', { status: 200, length: 22 })) // basically empty
151
+ test('zip.list.null filename', req('/f1/?get=zip&list=%00', 400)) // tries to name the output with null-byte
152
test('zip.alfa is forbidden', req('/protectFromAbove/child/?get=zip&list=alfa.txt//renamed', { empty: true, length:134 }, { method:'HEAD' }))
153
test('zip.cantReadPage', req('/cantReadPage/?get=zip', { length: 4832 }, { method:'HEAD' }))
154