@samitouri / QOSami-HFS / commits / abe15df9

fix: escape filenames in basic listing CVE-2026-61504

Massimo Melina committed Jul 11, 2026 at 13:16 UTC abe15df901c65562a3be5f5e068bf8fef976f9f3
2 files changed +15 -2
src/basicWeb.ts
+2 -2
@@ -3,7 +3,7 @@ import { BASIC_AUTHENTICATE_HEADER, HTTP_UNAUTHORIZED } from './cross-const'
3 import Koa from 'koa'
4 import { defineConfig } from './config'
5 import { getNodeName, getDefaultFile, nodeIsFolder, VfsNode, walkNode } from './vfs'
6 -import { asyncGeneratorToReadable, Dict, filterMapGenerator, pathEncode } from './misc'
6 +import { asyncGeneratorToReadable, Dict, escapeHTML, filterMapGenerator, pathEncode } from './misc'
7 import _ from 'lodash'
8 import { title } from './adminApis'
9 import { getSection } from './customHtml'
@@ -53,7 +53,7 @@ export function basicWeb(ctx: Koa.Context, node: VfsNode) {
53 return true
54
55 function a(href: string, label: string) {
56 - return `<a href='${href}'>${label}</a>`
56 + return `<a href='${href}'>${escapeHTML(label)}</a>`
57 }
58
59 }
tests/test.ts
+13
@@ -166,6 +166,19 @@ describe('basics', () => {
166 test('name encoding', req(FUNNY_NAME_ENCODED, 200))
167 test('name encoding list', reqList('/', { inList: [FUNNY_NAME] }))
168 test('name encoding search', reqList('/', { inList: [FUNNY_NAME] }, { search: FUNNY_NAME }))
169 + test('basic listing escapes', async () => {
170 + const name = '<img src=x onerror=alert(1)>.png'
171 + const path = resolve(__dirname, name)
172 + await writeFile(path, '')
173 + try {
174 + await req('/tests/?get=basic', { status: 200, cb: data => !String(data).includes(name) }, {
175 + headers: { 'user-agent': 'Mozilla/5.0' },
176 + })()
177 + }
178 + finally {
179 + await rm(path, { force: true })
180 + }
181 + })
182 test('folder list preserves encoded colon in prepend', req('/tests/C%3A/?get=list&folders=*', data => {
183 if (!String(data).includes('/tests/C%3A/gpl.png'))
184 throw Error('missing correctly encoded path in list: ' + data)