rcgit

/ karakeep

Commit a4b2fc7c

SHA a4b2fc7ca89c7916a32a3e279ab3880ebaa7a734
Author MohamedBassem <me at mbassem dot com>
Author Date 2025-08-23 16:03 +0300
Committer MohamedBassem <me at mbassem dot com>
Commit Date 2025-08-23 16:03 +0300
Parent(s) 096af7efffe1 (diff)
Tree 0697484b3881

patch snapshot

fix(security): Add CSP policies on asset serving path
File + - Graph
M apps/web/components/dashboard/preview/LinkContentSection.tsx +1 -0
M packages/api/utils/assets.ts +19 -2
2 file(s) changed, 20 insertions(+), 2 deletions(-)

apps/web/components/dashboard/preview/LinkContentSection.tsx

diff --git a/apps/web/components/dashboard/preview/LinkContentSection.tsx b/apps/web/components/dashboard/preview/LinkContentSection.tsx
index 67de4e75..a0a6f580 100644
--- a/apps/web/components/dashboard/preview/LinkContentSection.tsx
+++ b/apps/web/components/dashboard/preview/LinkContentSection.tsx
@@ -62,6 +62,7 @@ function FullPageArchiveSection({ link }: { link: ZBookmarkedLink }) {
     link.fullPageArchiveAssetId ?? link.precrawledArchiveAssetId;
   return (
     <iframe
+      sandbox=""
       title={link.url}
       src={`/api/assets/${archiveAssetId}`}
       className="relative h-full min-w-full"

packages/api/utils/assets.ts

diff --git a/packages/api/utils/assets.ts b/packages/api/utils/assets.ts
index 205e1a76..3da32ff2 100644
--- a/packages/api/utils/assets.ts
+++ b/packages/api/utils/assets.ts
@@ -22,6 +22,25 @@ export async function serveAsset(c: Context, assetId: string, userId: string) {
     }),
   ]);
 
+  // Default Headers
+  c.header("Content-type", metadata.contentType);
+  c.header("X-Content-Type-Options", "nosniff");
+  c.header(
+    "Content-Security-Policy",
+    [
+      "sandbox",
+      "default-src 'none'",
+      "base-uri 'none'",
+      "form-action 'none'",
+      "img-src https: data: blob:",
+      "style-src 'unsafe-inline' https:",
+      "connect-src 'none'",
+      "media-src https: data: blob:",
+      "object-src 'none'",
+      "frame-src 'none'",
+    ].join("; "),
+  );
+
   const range = c.req.header("Range");
   if (range) {
     const parts = range.replace(/bytes=/, "").split("-");
@@ -38,7 +57,6 @@ export async function serveAsset(c: Context, assetId: string, userId: string) {
     c.header("Content-Range", `bytes ${start}-${end}/${size}`);
     c.header("Accept-Ranges", "bytes");
     c.header("Content-Length", (end - start + 1).toString());
-    c.header("Content-type", metadata.contentType);
     return stream(c, async (stream) => {
       await stream.pipe(toWebReadableStream(fStream));
     });
@@ -49,7 +67,6 @@ export async function serveAsset(c: Context, assetId: string, userId: string) {
     });
     c.status(200);
     c.header("Content-Length", size.toString());
-    c.header("Content-type", metadata.contentType);
     return stream(c, async (stream) => {
       await stream.pipe(toWebReadableStream(fStream));
     });