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)