mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
fix: allow same origin for all necessary domains (#7889)
This commit is contained in:
parent
db4770ed83
commit
988f81911c
1 changed files with 78 additions and 27 deletions
|
@ -19,7 +19,7 @@ type EmbeddedLink =
|
|||
| ({
|
||||
aspectRatio: { w: number; h: number };
|
||||
warning?: string;
|
||||
sandbox?: { allowSameOrigin?: boolean };
|
||||
sandbox: { allowSameOrigin?: boolean };
|
||||
} & (
|
||||
| { type: "video" | "generic"; link: string }
|
||||
| { type: "document"; srcdoc: (theme: Theme) => string }
|
||||
|
@ -67,7 +67,18 @@ const ALLOWED_DOMAINS = new Set([
|
|||
"stackblitz.com",
|
||||
"val.town",
|
||||
"giphy.com",
|
||||
"dddice.com",
|
||||
]);
|
||||
|
||||
const ALLOW_SAME_ORIGIN = new Set([
|
||||
"youtube.com",
|
||||
"youtu.be",
|
||||
"vimeo.com",
|
||||
"player.vimeo.com",
|
||||
"figma.com",
|
||||
"twitter.com",
|
||||
"x.com",
|
||||
"*.simplepdf.eu",
|
||||
"stackblitz.com",
|
||||
]);
|
||||
|
||||
const createSrcDoc = (body: string) => {
|
||||
|
@ -85,6 +96,10 @@ export const getEmbedLink = (link: string | null | undefined): EmbeddedLink => {
|
|||
|
||||
const originalLink = link;
|
||||
|
||||
const allowSameOrigin = ALLOW_SAME_ORIGIN.has(
|
||||
matchHostname(link, ALLOW_SAME_ORIGIN) || "",
|
||||
);
|
||||
|
||||
let type: "video" | "generic" = "generic";
|
||||
let aspectRatio = { w: 560, h: 840 };
|
||||
const ytLink = link.match(RE_YOUTUBE);
|
||||
|
@ -107,8 +122,18 @@ export const getEmbedLink = (link: string | null | undefined): EmbeddedLink => {
|
|||
break;
|
||||
}
|
||||
aspectRatio = isPortrait ? { w: 315, h: 560 } : { w: 560, h: 315 };
|
||||
embeddedLinkCache.set(originalLink, { link, aspectRatio, type });
|
||||
return { link, aspectRatio, type };
|
||||
embeddedLinkCache.set(originalLink, {
|
||||
link,
|
||||
aspectRatio,
|
||||
type,
|
||||
sandbox: { allowSameOrigin },
|
||||
});
|
||||
return {
|
||||
link,
|
||||
aspectRatio,
|
||||
type,
|
||||
sandbox: { allowSameOrigin },
|
||||
};
|
||||
}
|
||||
|
||||
const vimeoLink = link.match(RE_VIMEO);
|
||||
|
@ -122,8 +147,13 @@ export const getEmbedLink = (link: string | null | undefined): EmbeddedLink => {
|
|||
aspectRatio = { w: 560, h: 315 };
|
||||
//warning deliberately ommited so it is displayed only once per link
|
||||
//same link next time will be served from cache
|
||||
embeddedLinkCache.set(originalLink, { link, aspectRatio, type });
|
||||
return { link, aspectRatio, type, warning };
|
||||
embeddedLinkCache.set(originalLink, {
|
||||
link,
|
||||
aspectRatio,
|
||||
type,
|
||||
sandbox: { allowSameOrigin },
|
||||
});
|
||||
return { link, aspectRatio, type, warning, sandbox: { allowSameOrigin } };
|
||||
}
|
||||
|
||||
const figmaLink = link.match(RE_FIGMA);
|
||||
|
@ -133,16 +163,26 @@ export const getEmbedLink = (link: string | null | undefined): EmbeddedLink => {
|
|||
link,
|
||||
)}`;
|
||||
aspectRatio = { w: 550, h: 550 };
|
||||
embeddedLinkCache.set(originalLink, { link, aspectRatio, type });
|
||||
return { link, aspectRatio, type };
|
||||
embeddedLinkCache.set(originalLink, {
|
||||
link,
|
||||
aspectRatio,
|
||||
type,
|
||||
sandbox: { allowSameOrigin },
|
||||
});
|
||||
return { link, aspectRatio, type, sandbox: { allowSameOrigin } };
|
||||
}
|
||||
|
||||
const valLink = link.match(RE_VALTOWN);
|
||||
if (valLink) {
|
||||
link =
|
||||
valLink[1] === "embed" ? valLink[0] : valLink[0].replace("/v", "/embed");
|
||||
embeddedLinkCache.set(originalLink, { link, aspectRatio, type });
|
||||
return { link, aspectRatio, type };
|
||||
embeddedLinkCache.set(originalLink, {
|
||||
link,
|
||||
aspectRatio,
|
||||
type,
|
||||
sandbox: { allowSameOrigin },
|
||||
});
|
||||
return { link, aspectRatio, type, sandbox: { allowSameOrigin } };
|
||||
}
|
||||
|
||||
if (RE_TWITTER.test(link)) {
|
||||
|
@ -162,7 +202,7 @@ export const getEmbedLink = (link: string | null | undefined): EmbeddedLink => {
|
|||
`<blockquote class="twitter-tweet" data-dnt="true" data-theme="${theme}"><a href="${safeURL}"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>`,
|
||||
),
|
||||
aspectRatio: { w: 480, h: 480 },
|
||||
sandbox: { allowSameOrigin: true },
|
||||
sandbox: { allowSameOrigin },
|
||||
};
|
||||
embeddedLinkCache.set(originalLink, ret);
|
||||
return ret;
|
||||
|
@ -185,13 +225,19 @@ export const getEmbedLink = (link: string | null | undefined): EmbeddedLink => {
|
|||
</style>
|
||||
`),
|
||||
aspectRatio: { w: 550, h: 720 },
|
||||
sandbox: { allowSameOrigin },
|
||||
};
|
||||
embeddedLinkCache.set(link, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
embeddedLinkCache.set(link, { link, aspectRatio, type });
|
||||
return { link, aspectRatio, type };
|
||||
embeddedLinkCache.set(link, {
|
||||
link,
|
||||
aspectRatio,
|
||||
type,
|
||||
sandbox: { allowSameOrigin },
|
||||
});
|
||||
return { link, aspectRatio, type, sandbox: { allowSameOrigin } };
|
||||
};
|
||||
|
||||
export const isEmbeddableOrLabel = (
|
||||
|
@ -266,34 +312,39 @@ export const actionSetEmbeddableAsActiveTool = register({
|
|||
},
|
||||
});
|
||||
|
||||
const validateHostname = (
|
||||
const matchHostname = (
|
||||
url: string,
|
||||
/** using a Set assumes it already contains normalized bare domains */
|
||||
allowedHostnames: Set<string> | string,
|
||||
): boolean => {
|
||||
): string | null => {
|
||||
try {
|
||||
const { hostname } = new URL(url);
|
||||
|
||||
const bareDomain = hostname.replace(/^www\./, "");
|
||||
|
||||
if (allowedHostnames instanceof Set) {
|
||||
if (ALLOWED_DOMAINS.has(bareDomain)) {
|
||||
return bareDomain;
|
||||
}
|
||||
|
||||
const bareDomainWithFirstSubdomainWildcarded = bareDomain.replace(
|
||||
/^([^.]+)/,
|
||||
"*",
|
||||
);
|
||||
|
||||
if (allowedHostnames instanceof Set) {
|
||||
return (
|
||||
ALLOWED_DOMAINS.has(bareDomain) ||
|
||||
ALLOWED_DOMAINS.has(bareDomainWithFirstSubdomainWildcarded)
|
||||
);
|
||||
if (ALLOWED_DOMAINS.has(bareDomainWithFirstSubdomainWildcarded)) {
|
||||
return bareDomainWithFirstSubdomainWildcarded;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (bareDomain === allowedHostnames.replace(/^www\./, "")) {
|
||||
return true;
|
||||
const bareAllowedHostname = allowedHostnames.replace(/^www\./, "");
|
||||
if (bareDomain === bareAllowedHostname) {
|
||||
return bareAllowedHostname;
|
||||
}
|
||||
} catch (error) {
|
||||
// ignore
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
};
|
||||
|
||||
export const extractSrc = (htmlString: string): string => {
|
||||
|
@ -342,7 +393,7 @@ export const embeddableURLValidator = (
|
|||
if (url.match(domain)) {
|
||||
return true;
|
||||
}
|
||||
} else if (validateHostname(url, domain)) {
|
||||
} else if (matchHostname(url, domain)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -350,5 +401,5 @@ export const embeddableURLValidator = (
|
|||
}
|
||||
}
|
||||
|
||||
return validateHostname(url, ALLOWED_DOMAINS);
|
||||
return !!matchHostname(url, ALLOWED_DOMAINS);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue