@samitouri / QOSami-HFS / commits / 3f48bf88

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)