From 4fad7f04a9e73fa8064008601160b104fabb3a2f Mon Sep 17 00:00:00 2001 From: Maurice Date: Tue, 14 Jul 2026 09:57:57 +0200 Subject: [PATCH] fix(plugins): serve frame assets root-relative and cross-origin loadable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit res.sendFile(absolutePath) resolves against the rewritten req.url under the Nest ExpressAdapter and 404s spuriously (files-download already works around the same trap), which broke every plugin frame document. And helmet's CORP: same-origin made the browser drop the opaque-origin frame's own script/style subresources, so a multi-file plugin client could never boot. Serve root-relative and mark frame responses cross-origin — sandbox + per-plugin CSP stay the isolation boundary. --- .../nest/plugins/plugin-frame.controller.ts | 12 +++++++++- .../tests/unit/plugins/plugin-frame.test.ts | 24 ++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/server/src/nest/plugins/plugin-frame.controller.ts b/server/src/nest/plugins/plugin-frame.controller.ts index 08959bd06..0a932f8ff 100644 --- a/server/src/nest/plugins/plugin-frame.controller.ts +++ b/server/src/nest/plugins/plugin-frame.controller.ts @@ -61,8 +61,18 @@ export class PluginFrameController { const ext = path.extname(resolved).toLowerCase(); res.setHeader('Content-Type', MIME[ext] ?? 'application/octet-stream'); res.setHeader('X-Content-Type-Options', 'nosniff'); + // The frame document runs at an OPAQUE origin (sandbox without + // allow-same-origin), so its own