diff --git a/src/lib/datasource/Local.ts b/src/lib/datasource/Local.ts index c0a9de41..ee2803f8 100644 --- a/src/lib/datasource/Local.ts +++ b/src/lib/datasource/Local.ts @@ -3,6 +3,7 @@ import { access, constants, copyFile, readdir, rename, rm, stat, writeFile } fro import { join, resolve, sep } from 'path'; import { Readable } from 'stream'; import { Datasource, ListOptions, PutOptions } from './Datasource'; +import { log } from '../logger'; async function existsAndCanRW(path: string): Promise { try { @@ -15,6 +16,7 @@ async function existsAndCanRW(path: string): Promise { export class LocalDatasource extends Datasource { name = 'local'; + logger = log('datasource').c('local'); constructor(public dir: string) { super(); @@ -40,9 +42,7 @@ export class LocalDatasource extends Datasource { public async put(file: string, data: Buffer | string, { noDelete }: PutOptions): Promise { const path = this.resolvePath(file); - if (!path) { - throw new Error('Invalid path provided'); - } + if (!path) throw new Error('Invalid path provided'); // handles if given a path to a file, it will just move it instead of doing unecessary writes if (typeof data === 'string' && data.startsWith('/')) { @@ -69,14 +69,17 @@ export class LocalDatasource extends Datasource { return; } - const path = join(this.dir, file); + const path = this.resolvePath(file); + if (!path) throw new Error('Invalid path provided'); + if (!existsSync(path)) return Promise.resolve(); return rm(path); } public async size(file: string): Promise { - const path = join(this.dir, file); + const path = this.resolvePath(file); + if (!path) throw new Error('Invalid path provided'); if (!existsSync(path)) return 0; const { size } = await stat(path); @@ -98,15 +101,18 @@ export class LocalDatasource extends Datasource { } public async range(file: string, start: number, end: number): Promise { - const path = join(this.dir, file); + const path = this.resolvePath(file); + if (!path) throw new Error('Invalid path provided'); + const readStream = createReadStream(path, { start, end }); return readStream; } public async rename(from: string, to: string): Promise { - const fromPath = join(this.dir, from); - const toPath = join(this.dir, to); + const fromPath = this.resolvePath(from); + const toPath = this.resolvePath(to); + if (!fromPath || !toPath) throw new Error('Invalid path provided'); if (!existsSync(fromPath)) throw new Error(`Something went very wrong! File ${from} does not exist in local datasource.`); @@ -120,3 +126,4 @@ export class LocalDatasource extends Datasource { return files.filter((f) => f.isFile() && f.name.startsWith(options.prefix || '')).map((f) => f.name); } } + diff --git a/src/server/routes/api/server/import/v3.ts b/src/server/routes/api/server/import/v3.ts index 09e9bc23..cb1ef6b0 100644 --- a/src/server/routes/api/server/import/v3.ts +++ b/src/server/routes/api/server/import/v3.ts @@ -1,8 +1,10 @@ import { ApiError } from '@/lib/api/errors'; import { createToken } from '@/lib/crypto'; import { prisma } from '@/lib/db'; +import { sanitizeFilename } from '@/lib/fs'; import { export3Schema } from '@/lib/import/version3/validateExport'; import { log } from '@/lib/logger'; +import { randomCharacters } from '@/lib/random'; import { secondlyRatelimit } from '@/lib/ratelimits'; import { administratorMiddleware } from '@/server/middleware/administrator'; import { userMiddleware } from '@/server/middleware/user'; @@ -182,10 +184,16 @@ export default typedPlugin( continue; } + let sanitizedFilename = sanitizeFilename(file.name); + if (!sanitizedFilename) { + sanitizedFilename = randomCharacters(12); + logger.warn('file has invalid name, using random name', { file: id, new: sanitizedFilename }); + } + const created = await prisma.file.create({ data: { userId: user, - name: file.name, + name: sanitizedFilename, originalName: file.original_name || null, type: file.type, size: file.size, @@ -303,3 +311,4 @@ export default typedPlugin( }, { name: PATH }, ); + diff --git a/src/server/routes/api/server/import/v4.ts b/src/server/routes/api/server/import/v4.ts index 41ab2749..582b8849 100644 --- a/src/server/routes/api/server/import/v4.ts +++ b/src/server/routes/api/server/import/v4.ts @@ -1,8 +1,10 @@ import { ApiError } from '@/lib/api/errors'; import { createToken } from '@/lib/crypto'; import { prisma } from '@/lib/db'; +import { sanitizeFilename } from '@/lib/fs'; import { export4Schema } from '@/lib/import/version4/validateExport'; import { log } from '@/lib/logger'; +import { randomCharacters } from '@/lib/random'; import { secondlyRatelimit } from '@/lib/ratelimits'; import { administratorMiddleware } from '@/server/middleware/administrator'; import { userMiddleware } from '@/server/middleware/user'; @@ -355,10 +357,19 @@ export default typedPlugin( const folderId = file.folderId ? importedFolders[file.folderId] : null; + let sanitizedFilename = sanitizeFilename(file.name); + if (!sanitizedFilename) { + sanitizedFilename = randomCharacters(12); + logger.warn('file has invalid name, using random name', { + file: file.id, + new: sanitizedFilename, + }); + } + const created = await prisma.file.create({ data: { userId, - name: file.name, + name: sanitizedFilename, size: file.size, type: file.type, folderId, @@ -549,3 +560,4 @@ export default typedPlugin( }, { name: PATH }, ); +