From 282fb85959ddfaf8d07f137daf3de744d7c2a9d1 Mon Sep 17 00:00:00 2001 From: decobot Date: Fri, 13 Mar 2026 21:09:12 +0800 Subject: [PATCH 1/3] fix clickjacking vulnerability --- apps/mesh/src/api/app.ts | 7 +++++++ apps/mesh/src/index.ts | 21 ++++++++++++++++++++- packages/runtime/src/asset-server/index.ts | 7 ++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/apps/mesh/src/api/app.ts b/apps/mesh/src/api/app.ts index a5a82e4932..0f9b0a6397 100644 --- a/apps/mesh/src/api/app.ts +++ b/apps/mesh/src/api/app.ts @@ -369,6 +369,13 @@ export async function createApp(options: CreateAppOptions = {}) { }), ); + // Security headers middleware - prevents UI redressing / clickjacking + app.use("*", async (c, next) => { + await next(); + c.header("X-Frame-Options", "DENY"); + c.header("Content-Security-Policy", "frame-ancestors 'none'"); + }); + if (env.NODE_ENV === "production") { app.use("*", logger()); } else { diff --git a/apps/mesh/src/index.ts b/apps/mesh/src/index.ts index 73c8ae1044..149a8ee939 100644 --- a/apps/mesh/src/index.ts +++ b/apps/mesh/src/index.ts @@ -60,6 +60,23 @@ const handleAssets = createAssetHandler({ isServerPath, }); +const SECURITY_HEADERS: Record = { + "X-Frame-Options": "DENY", + "Content-Security-Policy": "frame-ancestors 'none'", +}; + +function withSecurityHeaders(res: Response): Response { + const headers = new Headers(res.headers); + for (const [key, value] of Object.entries(SECURITY_HEADERS)) { + headers.set(key, value); + } + return new Response(res.body, { + status: res.status, + statusText: res.statusText, + headers, + }); +} + // Create the Hono app const app = await createApp(); @@ -81,7 +98,9 @@ Bun.serve({ fetch: async (request, server) => { // Try assets first (static files or dev proxy), then API // Pass server as env so Hono's getConnInfo can access requestIP - return (await handleAssets(request)) ?? app.fetch(request, { server }); + const assetRes = await handleAssets(request); + if (assetRes) return withSecurityHeaders(assetRes); + return app.fetch(request, { server }); }, development: env.NODE_ENV !== "production", }); diff --git a/packages/runtime/src/asset-server/index.ts b/packages/runtime/src/asset-server/index.ts index 810be5b0a7..b000b7f0de 100644 --- a/packages/runtime/src/asset-server/index.ts +++ b/packages/runtime/src/asset-server/index.ts @@ -10,12 +10,17 @@ import { resolve, dirname, join, extname, basename, sep } from "path"; * hash in the filename changes on every build. * - Everything else: no explicit caching directive (browser defaults apply). */ +const SECURITY_HEADERS = { + "X-Frame-Options": "DENY", + "Content-Security-Policy": "frame-ancestors 'none'", +}; + function getAssetCacheHeaders( filePath: string, indexPath: string, ): Record { if (filePath === indexPath || basename(filePath) === "index.html") { - return { "Cache-Control": "no-cache" }; + return { "Cache-Control": "no-cache", ...SECURITY_HEADERS }; } if (filePath.includes(`${sep}assets${sep}`)) { From c94d2145b5c540a8c1fedf75ccf9b51b8556c62a Mon Sep 17 00:00:00 2001 From: decobot Date: Fri, 13 Mar 2026 21:58:52 +0800 Subject: [PATCH 2/3] remove runtime asset-server changes --- packages/runtime/src/asset-server/index.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/runtime/src/asset-server/index.ts b/packages/runtime/src/asset-server/index.ts index b000b7f0de..a901677e01 100644 --- a/packages/runtime/src/asset-server/index.ts +++ b/packages/runtime/src/asset-server/index.ts @@ -10,17 +10,13 @@ import { resolve, dirname, join, extname, basename, sep } from "path"; * hash in the filename changes on every build. * - Everything else: no explicit caching directive (browser defaults apply). */ -const SECURITY_HEADERS = { - "X-Frame-Options": "DENY", - "Content-Security-Policy": "frame-ancestors 'none'", -}; function getAssetCacheHeaders( filePath: string, indexPath: string, ): Record { if (filePath === indexPath || basename(filePath) === "index.html") { - return { "Cache-Control": "no-cache", ...SECURITY_HEADERS }; + return { "Cache-Control": "no-cache" }; } if (filePath.includes(`${sep}assets${sep}`)) { From c102febc276b9c25a13f6abc218bcac02c57959b Mon Sep 17 00:00:00 2001 From: decobot Date: Tue, 17 Mar 2026 02:50:34 +0800 Subject: [PATCH 3/3] remove whitespace --- packages/runtime/src/asset-server/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/runtime/src/asset-server/index.ts b/packages/runtime/src/asset-server/index.ts index a901677e01..810be5b0a7 100644 --- a/packages/runtime/src/asset-server/index.ts +++ b/packages/runtime/src/asset-server/index.ts @@ -10,7 +10,6 @@ import { resolve, dirname, join, extname, basename, sep } from "path"; * hash in the filename changes on every build. * - Everything else: no explicit caching directive (browser defaults apply). */ - function getAssetCacheHeaders( filePath: string, indexPath: string,