Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
**Vulnerability:** Raw error objects from FFmpeg and browser APIs (`error.message` and `console.error`) were being rendered directly in the user interface and browser console.
**Learning:** Returning unhandled exception messages to the client risks exposing internal stack traces, system paths, and unexpected framework vulnerabilities to potential attackers.
**Prevention:** Catch error blocks should log a sanitized version of the error or omit sensitive details, while the state presented to the user should be a generic, friendly, and secure fallback message (e.g. "Processing failed securely").
## 2024-05-30 - Fix Path Traversal Risk in Server API Uploads
**Vulnerability:** User-uploaded file names were concatenated directly into temporary storage file paths in `src/app/api/video/[tool]/route.ts` and `src/app/api/plugins/install/route.ts` (e.g., `const inputPath = ${tmpDir}/${crypto.randomUUID()}_${file.name};`).
**Learning:** While using a UUID prefix makes brute-forcing paths more difficult, it does not guarantee protection if the user input `file.name` contains directory traversal sequences (like `../` or newline characters). Trusting unvalidated inputs inside path strings is a systemic risk for path traversal vulnerabilities when passing these paths to file system commands (like `fs.writeFile`).
**Prevention:** Always sanitize the user-provided filename using `path.basename()` before combining it into server-side file paths to strip off any directory structures provided by the client.
6 changes: 4 additions & 2 deletions src/app/api/plugins/install/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from 'next/server';
import { pluginDb } from '@/lib/db';
import AdmZip from 'adm-zip';
import { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs';
import { join, resolve } from 'path';
import { join, resolve, basename } from 'path';
import { validateManifest } from '@/lib/validators/plugin';
import { PLUGINS_DIR } from '@/modules/plugin-engine/system/health-monitor';

Expand All @@ -20,8 +20,10 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: 'File harus berformat ZIP' }, { status: 400 });
}

// Sanitize user-provided filename to prevent path traversal vulnerability
const safeFileName = basename(file.name);
const buffer = Buffer.from(await file.arrayBuffer());
tempZipPath = join(process.cwd(), '.next', 'tmp', `${Date.now()}-${file.name}`);
tempZipPath = join(process.cwd(), '.next', 'tmp', `${Date.now()}-${safeFileName}`);

if (!existsSync(join(process.cwd(), '.next', 'tmp'))) {
mkdirSync(join(process.cwd(), '.next', 'tmp'), { recursive: true });
Expand Down
6 changes: 4 additions & 2 deletions src/app/api/video/[tool]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ export async function POST(

const tmpDir = `/tmp/omni/video`;
await fs.mkdir(tmpDir, { recursive: true });
const inputPath = `${tmpDir}/${crypto.randomUUID()}_${file.name}`;
// Sanitize user-provided filename to prevent path traversal vulnerability
const safeFileName = path.basename(file.name);
const inputPath = `${tmpDir}/${crypto.randomUUID()}_${safeFileName}`;

// Write file to disk
const arrayBuffer = await file.arrayBuffer();
Expand All @@ -138,7 +140,7 @@ export async function POST(
success: true,
jobId,
tool,
fileName: file.name,
fileName: safeFileName,
fileSize: file.size,
status: "queued",
message: `Job queued.`,
Expand Down
Loading