fix: files with "*" in the name couldn't be selected as part of a zip

Massimo Melina committed Jan 21, 2025 at 18:43 UTC dea9d476a63d49d2e169cd85c31d023fba7d72fa
3 files changed +4 -4
frontend/src/menu.ts
+2 -2
@@ -42,7 +42,7 @@ export function MenuPanel() {
42
43 // passing files as string in the url should allow 1-2000 items before hitting the url limit of 64KB. Shouldn't be a problem, right?
44 const ofs = location.pathname.length
45 - const list = useMemo(() => Object.keys(selected).map(s => s.slice(ofs, s.endsWith('/') ? -1 : Infinity)).join('*'), [selected])
45 + const list = useMemo(() => Object.keys(selected).map(s => s.slice(ofs, s.endsWith('/') ? -1 : Infinity)).join('//'), [selected])
46
47 // avoid useless dom changes while we are still waiting for necessary data
48 const [changingButton, setChangingButton] = useState<'' | 'upload' | 'delete'>('')
@@ -68,7 +68,7 @@ export function MenuPanel() {
68 icon: 'delete',
69 label: t`Delete`,
70 className: 'show-sliding',
71 - disabled: !list.length,
71 + disabled: !list,
72 tooltip: t('delete_select', "Select something to delete"),
73 onClick: () => deleteFiles(Object.keys(selected))
74 } : {
src/zip.ts
+1 -1
@@ -15,7 +15,7 @@ import { getCommentFor } from './comments'
15
16 // expects 'node' to have had permissions checked by caller
17 export async function zipStreamFromFolder(node: VfsNode, ctx: Koa.Context) {
18 - const list = wantArray(ctx.query.list)[0]?.split('*') // we are using * as separator because it cannot be used in a file name and doesn't need url encoding
18 + const list = wantArray(ctx.query.list)[0]?.split('//') // slash is the only char not allowed in file names both for windows and unix, but still we need to encode whole paths, so the only safe choice to separate the entries is the double slash
19 if (!list && statusCodeForMissingPerm(node, 'can_archive', ctx)) return
20 ctx.status = HTTP_OK
21 ctx.mime = 'zip'
tests/test.ts
+1 -1
@@ -102,7 +102,7 @@ describe('basics', () => {
102 it('zip.partial', req('/f1/?get=zip', { re:/^C3$/, length: 2 }, { headers: { Range: `bytes=${zipOfs}-${zipOfs+1}` } }) )
103 it('zip.partial.resume', req('/f1/?get=zip', { re:/^C3/, length:zipSize-zipOfs }, { headers: { Range: `bytes=${zipOfs}-` } }) )
104 it('zip.partial.end', req('/f1/f2/?get=zip', { re:/^6/, length:10 }, { headers: { Range: 'bytes=-10' } }) )
105 - it('zip.alfa is forbidden', req('/protectFromAbove/child/?get=zip&list=alfa.txt*renamed', { empty: true, length:118 }, { method:'HEAD' }))
105 + it('zip.alfa is forbidden', req('/protectFromAbove/child/?get=zip&list=alfa.txt//renamed', { empty: true, length:118 }, { method:'HEAD' }))
106 it('zip.cantReadPage', req('/cantReadPage/?get=zip', { length: 120 }, { method:'HEAD' }))
107
108 it('referer', req('/f1/page/gpl.png', 403, {