From 8bf12e476eb896ef46b5a1426db4c6028ce36cb3 Mon Sep 17 00:00:00 2001 From: Jaret Burkett Date: Tue, 15 Sep 2026 10:13:53 -0600 Subject: [PATCH] Fixed issue that could allow deleting and uploading items outside the restricted directories. --- ui/src/app/api/datasets/delete/route.tsx | 12 ++++++++++-- ui/src/app/api/datasets/listImages/route.ts | 9 ++++++++- ui/src/app/api/datasets/upload/route.ts | 14 +++++++++++--- version.py | 2 +- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/ui/src/app/api/datasets/delete/route.tsx b/ui/src/app/api/datasets/delete/route.tsx index 3ec3e42..971c570 100644 --- a/ui/src/app/api/datasets/delete/route.tsx +++ b/ui/src/app/api/datasets/delete/route.tsx @@ -7,8 +7,16 @@ export async function POST(request: Request) { try { const body = await request.json(); const { name } = body; - let datasetsPath = await getDatasetsRoot(); - let datasetPath = path.join(datasetsPath, name); + if (typeof name !== 'string' || name.trim() === '') { + return NextResponse.json({ error: 'Invalid dataset name' }, { status: 400 }); + } + const datasetsPath = await getDatasetsRoot(); + const datasetPath = path.resolve(datasetsPath, name); + + // Must resolve to a direct child of the datasets root; rejects "..", absolute paths, and the root itself. + if (path.dirname(datasetPath) !== datasetsPath || datasetPath === datasetsPath) { + return NextResponse.json({ error: 'Invalid dataset name' }, { status: 400 }); + } // if folder doesnt exist, ignore if (!fs.existsSync(datasetPath)) { diff --git a/ui/src/app/api/datasets/listImages/route.ts b/ui/src/app/api/datasets/listImages/route.ts index 61594f3..26e3d46 100644 --- a/ui/src/app/api/datasets/listImages/route.ts +++ b/ui/src/app/api/datasets/listImages/route.ts @@ -12,7 +12,14 @@ export async function POST(request: Request) { const datasetsPath = await getDatasetsRoot(); const body = await request.json(); const { datasetName } = body; - const datasetFolder = path.join(datasetsPath, datasetName); + if (typeof datasetName !== 'string' || datasetName.trim() === '') { + return NextResponse.json({ error: 'Invalid dataset name' }, { status: 400 }); + } + const datasetFolder = path.resolve(datasetsPath, datasetName); + // Must resolve to a direct child of the datasets root; rejects "..", absolute paths, and the root itself. + if (path.dirname(datasetFolder) !== datasetsPath || datasetFolder === datasetsPath) { + return NextResponse.json({ error: 'Invalid dataset name' }, { status: 400 }); + } try { // Check if folder exists diff --git a/ui/src/app/api/datasets/upload/route.ts b/ui/src/app/api/datasets/upload/route.ts index 51aff81..3d63453 100644 --- a/ui/src/app/api/datasets/upload/route.ts +++ b/ui/src/app/api/datasets/upload/route.ts @@ -1,7 +1,7 @@ // src/app/api/datasets/upload/route.ts import { NextRequest, NextResponse } from 'next/server'; import { writeFile, mkdir } from 'fs/promises'; -import { join } from 'path'; +import path, { join } from 'path'; import { getDatasetsRoot } from '@/server/settings'; export async function POST(request: NextRequest) { @@ -18,12 +18,20 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: 'No files provided' }, { status: 400 }); } + if (typeof datasetName !== 'string' || datasetName.trim() === '') { + return NextResponse.json({ error: 'Invalid dataset name' }, { status: 400 }); + } + const uploadDir = path.resolve(datasetsPath, datasetName); + // Must resolve to a direct child of the datasets root; rejects "..", absolute paths, and the root itself. + if (path.dirname(uploadDir) !== datasetsPath || uploadDir === datasetsPath) { + return NextResponse.json({ error: 'Invalid dataset name' }, { status: 400 }); + } + // Create upload directory if it doesn't exist - const uploadDir = join(datasetsPath, datasetName); await mkdir(uploadDir, { recursive: true }); const savedFiles: string[] = []; - + // Process files sequentially to avoid overwhelming the system for (let i = 0; i < files.length; i++) { const file = files[i] as any; diff --git a/version.py b/version.py index 3fa5b69..f5d698e 100644 --- a/version.py +++ b/version.py @@ -1 +1 @@ -VERSION = "0.13.11" +VERSION = "0.13.12"