Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
12 changes: 9 additions & 3 deletions .github/workflows/audio-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,17 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
node-version: '22'
cache: 'pnpm'
cache-dependency-path: pnpm-lock.yaml

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: '9'

- name: Install Dependencies
run: npm ci
run: pnpm install --frozen-lockfile

- name: Run Audio Unit Tests
run: npx vitest run src/__tests__/audio-*
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
cache-dependency-path: pnpm-lock.yaml

- run: pnpm install --frozen-lockfile
- run: pnpm lint
Expand All @@ -51,6 +52,7 @@ jobs:
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
cache-dependency-path: pnpm-lock.yaml

- run: pnpm install --frozen-lockfile
- run: pnpm vitest run --reporter=verbose
Expand All @@ -73,6 +75,7 @@ jobs:
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
cache-dependency-path: pnpm-lock.yaml

- run: pnpm install --frozen-lockfile
- run: pnpm build
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/video-backend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: 9

Expand Down
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