fix: admin/fs: file/folder names weren't validated
Massimo Melina committed
Feb 3, 2024 at 14:43 UTC
5c471ef55b2b8ca4ddf95fa566037ad7632a947e
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 { Stats } from 'fs'
@@ -88,6 +89,8 @@ const apis: ApiHandlers = {
89
return new ApiError(HTTP_NOT_FOUND, 'path not found')
90
props = pickProps(props, ALLOWED_KEYS) // sanitize
91
if (props.name && props.name !== getNodeName(n)) {
92
+ if (!isValidFileName(props.name))
93
+ return new ApiError(HTTP_BAD_REQUEST, 'bad name')
94
const parent = await urlToNodeOriginal(dirname(uri))
95
if (parent?.children?.find(x => getNodeName(x) === props.name))
96
return new ApiError(HTTP_CONFLICT, 'name already present')
@@ -103,6 +106,8 @@ const apis: ApiHandlers = {
106
async add_vfs({ parent, source, name, ...rest }) {
107
if (!source && !name)
108
return new ApiError(HTTP_BAD_REQUEST, 'name or source required')
109
+ if (!isValidFileName(name))
110
+ return new ApiError(HTTP_BAD_REQUEST, 'bad name')
111
const parentNode = parent ? await urlToNodeOriginal(parent) : vfs
112
if (!parentNode)
113
return new ApiError(HTTP_NOT_FOUND, 'parent not found')
@@ -133,7 +138,7 @@ const apis: ApiHandlers = {
138
139
async del_vfs({ uris }) {
140
if (!uris || !Array.isArray(uris))
136
- return new ApiError(HTTP_BAD_REQUEST, 'invalid uris')
141
+ return new ApiError(HTTP_BAD_REQUEST, 'bad uris')
142
return {
143
errors: await Promise.all(uris.map(async uri => {
144
if (typeof uri !== 'string')
src/frontEndApis.ts
+1
-1
@@ -51,7 +51,7 @@ export const frontEndApis: ApiHandlers = {
51
52
async create_folder({ uri, name }, ctx) {
53
apiAssertTypes({ string: { uri, name } })
54
- if (!isValidFileName(name) || dirTraversal(name))
54
+ if (!isValidFileName(name))
55
return new ApiError(HTTP_BAD_REQUEST, 'bad name')
56
const parentNode = await urlToNode(uri, ctx)
57
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'
@@ -117,7 +117,7 @@ export async function urlToNode(url: string, ctx?: Koa.Context, parent: VfsNode=
117
}
118
119
export async function getNodeByName(name: string, parent: VfsNode) {
120
- if (dirTraversal(name) || /[\\/]/.test(name)) return
120
+ if (!isValidFileName(name)) return
121
// does the tree node have a child that goes by this name, otherwise attempt disk
122
const child = parent.children?.find(isSameFilenameAs(name)) || childFromDisk()
123
return child && applyParentToChild(child, parent, name)