fix: address all final review findings for Phase 3

- CRITICAL: Fix command injection in discoveryService (execFile instead
  of exec, path validation regex)
- CRITICAL: Add Zod validation on discover API endpoint
- HIGH: Add Zod validation on discover/approve endpoint
- HIGH: Add array length limits to import schema (1000/100/100)
- HIGH: Fix theme broadcast echo loop (setTimeout vs queueMicrotask)
- MEDIUM: Singleton BroadcastChannel instead of create-per-send
- MEDIUM: Exclude sensitive APIs from service worker cache
- MEDIUM: Fix TypeScript cast errors in exportService tests
This commit is contained in:
2026-03-25 01:28:24 +03:00
parent 7d8a8fb0fc
commit 395ed821b7
9 changed files with 72 additions and 50 deletions
+15 -4
View File
@@ -1,9 +1,15 @@
import { json } from '@sveltejs/kit';
import { z } from 'zod';
import type { RequestHandler } from './$types';
import { requireAdmin } from '$lib/server/middleware/authorize.js';
import { discoverAll, type DiscoveryConfig } from '$lib/server/services/discoveryService.js';
import { success, error } from '$lib/server/utils/response.js';
const discoverConfigSchema = z.object({
dockerSocketPath: z.string().regex(/^[\w/.:-]+$/).optional(),
traefikApiUrl: z.string().url().optional()
});
/**
* POST /api/admin/discover — Scan Docker and Traefik for services. Admin only.
*
@@ -12,16 +18,21 @@ import { success, error } from '$lib/server/utils/response.js';
export const POST: RequestHandler = async (event) => {
requireAdmin(event);
let body: DiscoveryConfig;
let rawBody: unknown;
try {
body = await event.request.json();
rawBody = await event.request.json();
} catch {
return json(error('Invalid JSON body'), { status: 400 });
}
const parsed = discoverConfigSchema.safeParse(rawBody);
if (!parsed.success) {
return json(error(`Validation failed: ${parsed.error.issues.map((i) => i.message).join(', ')}`), { status: 400 });
}
const config: DiscoveryConfig = {
dockerSocketPath: body.dockerSocketPath || undefined,
traefikApiUrl: body.traefikApiUrl || undefined
dockerSocketPath: parsed.data.dockerSocketPath || undefined,
traefikApiUrl: parsed.data.traefikApiUrl || undefined
};
if (!config.dockerSocketPath && !config.traefikApiUrl) {