fix: admin/fs: file/folder names weren't validated
Massimo Melina committed
Feb 3, 2024 at 14:43 UTC
3f48bf88cfd3d26b0a3ca90edd0881719d71a37a
4 files changed
+11
-6
src/api.vfs.ts
+7
-2
@@ -6,7 +6,8 @@ import _ from 'lodash'
6
import { mkdir, stat } from 'fs/promises'
7
import { ApiError, ApiHandlers } from './apiMiddleware'
8
import { dirname, extname, join, resolve } from 'path'
9
-import { dirStream, enforceFinal, isDirectory, isWindowsDrive, makeMatcher, PERM_KEYS, VfsNodeAdminSend } from './misc'
9
+import { dirStream, enforceFinal, isDirectory, isValidFileName, isWindowsDrive, makeMatcher, PERM_KEYS,
10
+ VfsNodeAdminSend } from './misc'
11
import { IS_WINDOWS, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR, HTTP_CONFLICT, HTTP_NOT_ACCEPTABLE } from './const'
12
import { getDrives } from './util-os'
13
import { getBaseUrlOrDefault, getServerStatus } from './listen'
@@ -87,6 +88,8 @@ const apis: ApiHandlers = {
88
return new ApiError(HTTP_NOT_FOUND, 'path not found')
89
props = pickProps(props, ALLOWED_KEYS) // sanitize
90
if (props.name && props.name !== getNodeName(n)) {
91
+ if (!isValidFileName(props.name))
92
+ return new ApiError(HTTP_BAD_REQUEST, 'bad name')
93
const parent = await urlToNodeOriginal(dirname(uri))
94
if (parent?.children?.find(x => getNodeName(x) === props.name))
95
return new ApiError(HTTP_CONFLICT, 'name already present')
@@ -102,6 +105,8 @@ const apis: ApiHandlers = {
105
async add_vfs({ parent, source, name, ...rest }) {
106
if (!source && !name)
107
return new ApiError(HTTP_BAD_REQUEST, 'name or source required')
108
+ if (!isValidFileName(name))
109
+ return new ApiError(HTTP_BAD_REQUEST, 'bad name')
110
const parentNode = parent ? await urlToNodeOriginal(parent) : vfs
111
if (!parentNode)
112
return new ApiError(HTTP_NOT_FOUND, 'parent not found')
@@ -132,7 +137,7 @@ const apis: ApiHandlers = {
137
138
async del_vfs({ uris }) {
139
if (!uris || !Array.isArray(uris))
135
- return new ApiError(HTTP_BAD_REQUEST, 'invalid uris')
140
+ return new ApiError(HTTP_BAD_REQUEST, 'bad uris')
141
return {
142
errors: await Promise.all(uris.map(async uri => {
143
if (typeof uri !== 'string')
src/frontEndApis.ts
+1
-1
@@ -49,7 +49,7 @@ export const frontEndApis: ApiHandlers = {
49
50
async create_folder({ uri, name }, ctx) {
51
apiAssertTypes({ string: { uri, name } })
52
- if (!isValidFileName(name) || dirTraversal(name))
52
+ if (!isValidFileName(name))
53
return new ApiError(HTTP_BAD_REQUEST, 'bad name')
54
const parentNode = await urlToNode(uri, ctx)
55
if (!parentNode)
src/util-files.ts
+1
-1
@@ -153,7 +153,7 @@ export function createFileWithPath(path: string, options?: Parameters<typeof cre
153
}
154
155
export function isValidFileName(name: string) {
156
- return !/^\.\.?$|[/:*?"<>|\\]/.test(name)
156
+ return !/^\.\.?$|[/:*?"<>|\\]/.test(name) && !dirTraversal(name)
157
}
158
159
const FILE_ATTR_PREFIX = 'user.hfs.' // user. prefix to be linux compatible
src/vfs.ts
+2
-2
@@ -2,7 +2,7 @@
2
3
import fs from 'fs/promises'
4
import { basename, dirname, join, resolve } from 'path'
5
-import { dirStream, dirTraversal, enforceFinal, getOrSet, isDirectory, makeMatcher, setHidden, onlyTruthy,
5
+import { dirStream, enforceFinal, getOrSet, isDirectory, makeMatcher, setHidden, onlyTruthy, isValidFileName,
6
throw_, VfsPerms, Who, isWhoObject, WHO_ANY_ACCOUNT, defaultPerms, PERM_KEYS, removeStarting } from './misc'
7
import Koa from 'koa'
8
import _ from 'lodash'
@@ -115,7 +115,7 @@ export async function urlToNode(url: string, ctx?: Koa.Context, parent: VfsNode=
115
}
116
117
export async function getNodeByName(name: string, parent: VfsNode) {
118
- if (dirTraversal(name) || /[\\/]/.test(name)) return
118
+ if (!isValidFileName(name)) return
119
// does the tree node have a child that goes by this name, otherwise attempt disk
120
const child = parent.children?.find(isSameFilenameAs(name)) || childFromDisk()
121
return child && applyParentToChild(child, parent, name)