From 9f1ecd3931668b0cb78d43debbf9e4699e9011df Mon Sep 17 00:00:00 2001 From: KingKDot Date: Thu, 7 May 2026 20:37:29 -0500 Subject: [PATCH] fix plugin loading with bad names --- .../src/server/plugin-state-bundle.ts | 23 +++++++++++++++++++ .../src/server/routes/plugin-routes.ts | 22 ++++++++++-------- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/Overlord-Server/src/server/plugin-state-bundle.ts b/Overlord-Server/src/server/plugin-state-bundle.ts index 091cbeb..0843348 100644 --- a/Overlord-Server/src/server/plugin-state-bundle.ts +++ b/Overlord-Server/src/server/plugin-state-bundle.ts @@ -398,6 +398,29 @@ export async function dispatchAutoLoadPlugins( } } +export function detectPluginIdFromZip(zip: AdmZip): string | null { + const entries = zip.getEntries(); + for (const entry of entries) { + if (entry.isDirectory) continue; + const base = path.basename(entry.entryName); + if (base.toLowerCase().endsWith(".html")) { + return base.slice(0, -5); + } + } + for (const entry of entries) { + if (entry.isDirectory) continue; + const base = path.basename(entry.entryName); + if (base.toLowerCase() === "config.json") { + try { + const cfg = JSON.parse(entry.getData().toString("utf-8")); + if (typeof cfg?.id === "string" && cfg.id.length > 0) return cfg.id; + } catch {} + break; + } + } + return null; +} + function derivePlatformKey(filename: string): string { const match = filename.match(/-(linux|darwin|windows|freebsd)-(amd64|arm64|arm|386)\.(so|dll|dylib)$/i); if (match) { diff --git a/Overlord-Server/src/server/routes/plugin-routes.ts b/Overlord-Server/src/server/routes/plugin-routes.ts index 3208f63..987ff64 100644 --- a/Overlord-Server/src/server/routes/plugin-routes.ts +++ b/Overlord-Server/src/server/routes/plugin-routes.ts @@ -12,7 +12,7 @@ import { encodeMessage, type PluginSignatureInfo } from "../../protocol"; import { getConfig, updatePluginsConfig } from "../../config"; import { getOrVerifySignature, BUILTIN_TRUSTED_KEYS } from "../plugin-signature"; import type { PluginRuntime } from "../plugin-runtime/runtime"; -import { getPluginPull, deletePluginPull } from "../plugin-state-bundle"; +import { getPluginPull, deletePluginPull, detectPluginIdFromZip } from "../plugin-state-bundle"; import { isAuthorizedAgentRequest } from "../agent-auth"; import { logger } from "../../logger"; @@ -253,21 +253,14 @@ export async function handlePluginRoutes( return new Response("Plugin zip exceeds size limit", { status: 413 }); } - const base = path.basename(filename, path.extname(filename)); - let pluginId = ""; - try { - pluginId = deps.sanitizePluginId(base); - } catch { - return new Response("Invalid plugin name", { status: 400 }); - } - const data = new Uint8Array(await file.arrayBuffer()); if (data.byteLength > MAX_PLUGIN_ZIP_BYTES) { return new Response("Plugin zip exceeds size limit", { status: 413 }); } + let probe: AdmZip; try { - const probe = new AdmZip(Buffer.from(data)); + probe = new AdmZip(Buffer.from(data)); let totalUncompressed = 0; for (const entry of probe.getEntries() as any[]) { const sz = Number(entry?.header?.size ?? 0); @@ -283,6 +276,15 @@ export async function handlePluginRoutes( return new Response("Invalid plugin zip", { status: 400 }); } + const internalId = detectPluginIdFromZip(probe); + const idCandidate = internalId || path.basename(filename, path.extname(filename)); + let pluginId = ""; + try { + pluginId = deps.sanitizePluginId(idCandidate); + } catch { + return new Response("Invalid plugin name", { status: 400 }); + } + await fs.mkdir(deps.PLUGIN_ROOT, { recursive: true }); const zipPath = path.join(deps.PLUGIN_ROOT, `${pluginId}.zip`); await fs.writeFile(zipPath, data);