Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions apps/mesh/src/api/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
21 changes: 20 additions & 1 deletion apps/mesh/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,23 @@ const handleAssets = createAssetHandler({
isServerPath,
});

const SECURITY_HEADERS: Record<string, string> = {
"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();

Expand All @@ -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",
});
Expand Down
1 change: 1 addition & 0 deletions packages/runtime/src/asset-server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { resolve, dirname, join, extname, basename, sep } from "path";
* hash in the filename changes on every build.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Shared asset server stopped sending anti-clickjacking headers on HTML responses, weakening default protection for createAssetHandler() consumers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/runtime/src/asset-server/index.ts, line 19:

<comment>Shared asset server stopped sending anti-clickjacking headers on HTML responses, weakening default protection for `createAssetHandler()` consumers.</comment>

<file context>
@@ -10,17 +10,13 @@ import { resolve, dirname, join, extname, basename, sep } from "path";
 ): Record<string, string> {
   if (filePath === indexPath || basename(filePath) === "index.html") {
-    return { "Cache-Control": "no-cache", ...SECURITY_HEADERS };
+    return { "Cache-Control": "no-cache" };
   }
 
</file context>
Fix with Cubic

* - Everything else: no explicit caching directive (browser defaults apply).
*/

function getAssetCacheHeaders(
filePath: string,
indexPath: string,
Expand Down
Loading