enzostvs HF Staff commited on
Commit
b8dd7a1
·
1 Parent(s): 1bf7173

try to avoid iframe on other website

Browse files
app/layout.tsx CHANGED
@@ -10,6 +10,7 @@ import MY_TOKEN_KEY from "@/lib/get-cookie-name";
10
  import { apiServer } from "@/lib/api";
11
  import AppContext from "@/components/contexts/app-context";
12
  import Script from "next/script";
 
13
 
14
  const inter = Inter({
15
  variable: "--font-inter-sans",
@@ -100,6 +101,7 @@ export default async function RootLayout({
100
  <body
101
  className={`${inter.variable} ${ptSans.variable} antialiased bg-black dark h-[100dvh] overflow-hidden`}
102
  >
 
103
  <Toaster richColors position="bottom-center" />
104
  <TanstackProvider>
105
  <AppContext me={data}>{children}</AppContext>
 
10
  import { apiServer } from "@/lib/api";
11
  import AppContext from "@/components/contexts/app-context";
12
  import Script from "next/script";
13
+ import IframeDetector from "@/components/iframe-detector";
14
 
15
  const inter = Inter({
16
  variable: "--font-inter-sans",
 
101
  <body
102
  className={`${inter.variable} ${ptSans.variable} antialiased bg-black dark h-[100dvh] overflow-hidden`}
103
  >
104
+ <IframeDetector />
105
  <Toaster richColors position="bottom-center" />
106
  <TanstackProvider>
107
  <AppContext me={data}>{children}</AppContext>
components/editor/pages/index.tsx CHANGED
@@ -1,12 +1,10 @@
1
  import { Page } from "@/types";
2
- // import { PlusIcon } from "lucide-react";
3
  import { ListPagesItem } from "./page";
4
 
5
  export function ListPages({
6
  pages,
7
  currentPage,
8
  onSelectPage,
9
- // onNewPage,
10
  onDeletePage,
11
  }: {
12
  pages: Array<Page>;
@@ -27,13 +25,6 @@ export function ListPages({
27
  index={i}
28
  />
29
  ))}
30
- {/* <button
31
- className="max-h-14 min-h-14 pl-2 pr-4 py-4 text-neutral-400 cursor-pointer text-sm hover:bg-neutral-900 flex items-center justify-center gap-1 text-nowrap"
32
- onClick={onNewPage}
33
- >
34
- <PlusIcon className="h-3" />
35
- New Page
36
- </button> */}
37
  </div>
38
  );
39
  }
 
1
  import { Page } from "@/types";
 
2
  import { ListPagesItem } from "./page";
3
 
4
  export function ListPages({
5
  pages,
6
  currentPage,
7
  onSelectPage,
 
8
  onDeletePage,
9
  }: {
10
  pages: Array<Page>;
 
25
  index={i}
26
  />
27
  ))}
 
 
 
 
 
 
 
28
  </div>
29
  );
30
  }
components/iframe-detector.tsx ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import IframeWarningModal from "./iframe-warning-modal";
5
+
6
+ export default function IframeDetector() {
7
+ const [showWarning, setShowWarning] = useState(false);
8
+
9
+ useEffect(() => {
10
+ // Helper function to check if a hostname is from allowed domains
11
+ const isAllowedDomain = (hostname: string) => {
12
+ const host = hostname.toLowerCase();
13
+ return (
14
+ host.endsWith(".huggingface.co") ||
15
+ host.endsWith(".hf.co") ||
16
+ host === "huggingface.co" ||
17
+ host === "hf.co"
18
+ );
19
+ };
20
+
21
+ // Check if the current window is in an iframe
22
+ const isInIframe = () => {
23
+ try {
24
+ return window.self !== window.top;
25
+ } catch {
26
+ // If we can't access window.top due to cross-origin restrictions,
27
+ // we're likely in an iframe
28
+ return true;
29
+ }
30
+ };
31
+
32
+ // Additional check: compare window location with parent location
33
+ const isEmbedded = () => {
34
+ try {
35
+ return window.location !== window.parent.location;
36
+ } catch {
37
+ // Cross-origin iframe
38
+ return true;
39
+ }
40
+ };
41
+
42
+ // Check if we're in an iframe from a non-allowed domain
43
+ const shouldShowWarning = () => {
44
+ if (!isInIframe() && !isEmbedded()) {
45
+ return false; // Not in an iframe
46
+ }
47
+
48
+ try {
49
+ // Try to get the parent's hostname
50
+ const parentHostname = window.parent.location.hostname;
51
+ return !isAllowedDomain(parentHostname);
52
+ } catch {
53
+ // Cross-origin iframe - try to get referrer instead
54
+ try {
55
+ if (document.referrer) {
56
+ const referrerUrl = new URL(document.referrer);
57
+ return !isAllowedDomain(referrerUrl.hostname);
58
+ }
59
+ } catch {
60
+ // If we can't determine the parent domain, assume it's not allowed
61
+ }
62
+ return true;
63
+ }
64
+ };
65
+
66
+ if (shouldShowWarning()) {
67
+ // Show warning modal instead of redirecting immediately
68
+ setShowWarning(true);
69
+ }
70
+ }, []);
71
+
72
+ return (
73
+ <IframeWarningModal isOpen={showWarning} onOpenChange={setShowWarning} />
74
+ );
75
+ }
components/iframe-warning-modal.tsx ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import {
4
+ Dialog,
5
+ DialogContent,
6
+ DialogDescription,
7
+ DialogFooter,
8
+ DialogHeader,
9
+ DialogTitle,
10
+ } from "@/components/ui/dialog";
11
+ import { Button } from "@/components/ui/button";
12
+ import { ExternalLink, AlertTriangle } from "lucide-react";
13
+
14
+ interface IframeWarningModalProps {
15
+ isOpen: boolean;
16
+ onOpenChange: (open: boolean) => void;
17
+ }
18
+
19
+ export default function IframeWarningModal({
20
+ isOpen,
21
+ onOpenChange,
22
+ }: IframeWarningModalProps) {
23
+ const handleVisitSite = () => {
24
+ window.top!.location.href = "https://deepsite.hf.co";
25
+ };
26
+
27
+ return (
28
+ <Dialog open={isOpen} onOpenChange={onOpenChange}>
29
+ <DialogContent className="sm:max-w-md">
30
+ <DialogHeader>
31
+ <div className="flex items-center gap-2">
32
+ <AlertTriangle className="h-5 w-5 text-red-500" />
33
+ <DialogTitle>Unauthorized Embedding</DialogTitle>
34
+ </div>
35
+ <DialogDescription className="text-left">
36
+ You&apos;re viewing DeepSite through an unauthorized iframe. For the
37
+ best experience and security, please visit the official website
38
+ directly.
39
+ </DialogDescription>
40
+ </DialogHeader>
41
+
42
+ <div className="bg-muted/50 rounded-lg p-4 space-y-2">
43
+ <p className="text-sm font-medium">Why visit the official site?</p>
44
+ <ul className="text-sm text-muted-foreground space-y-1">
45
+ <li>• Better performance and security</li>
46
+ <li>• Full functionality access</li>
47
+ <li>• Latest features and updates</li>
48
+ <li>• Proper authentication support</li>
49
+ </ul>
50
+ </div>
51
+
52
+ <DialogFooter className="flex-col sm:flex-row gap-2">
53
+ <Button onClick={handleVisitSite} className="w-full sm:w-auto">
54
+ <ExternalLink className="mr-2 h-4 w-4" />
55
+ Visit Deepsite.hf.co
56
+ </Button>
57
+ </DialogFooter>
58
+ </DialogContent>
59
+ </Dialog>
60
+ );
61
+ }
middleware.ts CHANGED
@@ -4,7 +4,54 @@ import type { NextRequest } from "next/server";
4
  export function middleware(request: NextRequest) {
5
  const headers = new Headers(request.headers);
6
  headers.set("x-current-host", request.nextUrl.host);
7
- return NextResponse.next({ headers });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  }
9
 
10
  export const config = {
 
4
  export function middleware(request: NextRequest) {
5
  const headers = new Headers(request.headers);
6
  headers.set("x-current-host", request.nextUrl.host);
7
+
8
+ // Check if the request is coming from an iframe
9
+ const referer = request.headers.get("referer");
10
+ const currentHost = request.nextUrl.host;
11
+ const currentOrigin = `${request.nextUrl.protocol}//${currentHost}`;
12
+
13
+ // Helper function to check if a URL is from allowed domains
14
+ const isAllowedDomain = (url: string) => {
15
+ try {
16
+ const urlObj = new URL(url);
17
+ const hostname = urlObj.hostname.toLowerCase();
18
+ return hostname.endsWith('.huggingface.co') ||
19
+ hostname.endsWith('.hf.co') ||
20
+ hostname === 'huggingface.co' ||
21
+ hostname === 'hf.co';
22
+ } catch {
23
+ return false;
24
+ }
25
+ };
26
+
27
+ // If there's a referer and it's not from the same origin, check if it's allowed
28
+ if (referer && !referer.startsWith(currentOrigin)) {
29
+ // Additional check: look for iframe-specific headers or indicators
30
+ const secFetchDest = request.headers.get("sec-fetch-dest");
31
+ const secFetchMode = request.headers.get("sec-fetch-mode");
32
+
33
+ // If the request is for a document within an iframe context
34
+ if (secFetchDest === "iframe" ||
35
+ (secFetchDest === "document" && secFetchMode === "navigate" && referer)) {
36
+
37
+ // Check if the referer is from an allowed domain
38
+ if (!isAllowedDomain(referer)) {
39
+ return NextResponse.redirect("https://deepsite.hf.co");
40
+ }
41
+ }
42
+ }
43
+
44
+ // Set headers to prevent framing
45
+ const response = NextResponse.next({ headers });
46
+
47
+ // Allow embedding only from Hugging Face domains
48
+ response.headers.set("X-Frame-Options", "SAMEORIGIN");
49
+ response.headers.set(
50
+ "Content-Security-Policy",
51
+ "frame-ancestors 'self' *.huggingface.co *.hf.co huggingface.co hf.co;"
52
+ );
53
+
54
+ return response;
55
  }
56
 
57
  export const config = {