fix: validate plugin/theme id in marketplace install to block path traversal

This commit is contained in:
Linus Rath
2026-05-18 13:03:49 +02:00
parent f275fbe2e4
commit b299a0b602
2 changed files with 22 additions and 2 deletions
+14 -2
View File
@@ -164,6 +164,18 @@ export async function POST(request: NextRequest) {
const now = new Date().toISOString();
// Resolve and strictly validate the id used as a filename. Marketplace
// bundles are authored by a third-party publisher; without this an id
// like "../../foo" causes savePlugin/saveTheme to write outside the
// plugins/themes dir via path.join.
const resolvedId = typeof manifest.id === 'string' && manifest.id ? manifest.id : slug;
if (typeof resolvedId !== 'string' || !/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(resolvedId)) {
return NextResponse.json(
{ error: 'Invalid id: must be lowercase alphanumeric with hyphens, min 2 chars' },
{ status: 400 },
);
}
if (type === 'theme') {
// Read theme.css
const cssFile = zip.file(root + 'theme.css');
@@ -183,7 +195,7 @@ export async function POST(request: NextRequest) {
}
const theme: ServerTheme = {
id: (manifest.id as string) || slug,
id: resolvedId,
name: (manifest.name as string) || slug,
version: (manifest.version as string) || version,
author: (manifest.author as string) || 'Unknown',
@@ -280,7 +292,7 @@ export async function POST(request: NextRequest) {
}
const plugin: ServerPlugin = {
id: (manifest.id as string) || slug,
id: resolvedId,
name: (manifest.name as string) || slug,
version: (manifest.version as string) || version,
author: (manifest.author as string) || 'Unknown',