mirror of
https://github.com/excalidraw/excalidraw.git
synced 2024-11-10 11:35:52 +01:00
fix: parse embeddable srcdoc urls strictly (#7884)
This commit is contained in:
parent
4689a6b300
commit
afcde542f9
@ -1,11 +1,15 @@
|
|||||||
import { sanitizeUrl } from "@braintree/sanitize-url";
|
import { sanitizeUrl } from "@braintree/sanitize-url";
|
||||||
|
|
||||||
|
export const sanitizeHTMLAttribute = (html: string) => {
|
||||||
|
return html.replace(/"/g, """);
|
||||||
|
};
|
||||||
|
|
||||||
export const normalizeLink = (link: string) => {
|
export const normalizeLink = (link: string) => {
|
||||||
link = link.trim();
|
link = link.trim();
|
||||||
if (!link) {
|
if (!link) {
|
||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
return sanitizeUrl(link);
|
return sanitizeUrl(sanitizeHTMLAttribute(link));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isLocalLink = (link: string | null) => {
|
export const isLocalLink = (link: string | null) => {
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
ExcalidrawIframeLikeElement,
|
ExcalidrawIframeLikeElement,
|
||||||
IframeData,
|
IframeData,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
import { sanitizeHTMLAttribute } from "../data/url";
|
||||||
|
|
||||||
const embeddedLinkCache = new Map<string, IframeData>();
|
const embeddedLinkCache = new Map<string, IframeData>();
|
||||||
|
|
||||||
@ -21,12 +22,13 @@ const RE_VIMEO =
|
|||||||
/^(?:http(?:s)?:\/\/)?(?:(?:w){3}\.)?(?:player\.)?vimeo\.com\/(?:video\/)?([^?\s]+)(?:\?.*)?$/;
|
/^(?:http(?:s)?:\/\/)?(?:(?:w){3}\.)?(?:player\.)?vimeo\.com\/(?:video\/)?([^?\s]+)(?:\?.*)?$/;
|
||||||
const RE_FIGMA = /^https:\/\/(?:www\.)?figma\.com/;
|
const RE_FIGMA = /^https:\/\/(?:www\.)?figma\.com/;
|
||||||
|
|
||||||
const RE_GH_GIST = /^https:\/\/gist\.github\.com/;
|
const RE_GH_GIST = /^https:\/\/gist\.github\.com\/([\w_-]+)\/([\w_-]+)/;
|
||||||
const RE_GH_GIST_EMBED =
|
const RE_GH_GIST_EMBED =
|
||||||
/https?:\/\/gist\.github\.com\/([\w_-]+)\/([\w_-]+)\.js["']/i;
|
/^<script[\s\S]*?\ssrc=["'](https:\/\/gist\.github\.com\/.*?)\.js["']/i;
|
||||||
|
|
||||||
// not anchored to start to allow <blockquote> twitter embeds
|
// not anchored to start to allow <blockquote> twitter embeds
|
||||||
const RE_TWITTER = /(?:https?:\/\/)?(?:(?:w){3}\.)?(?:twitter|x)\.com/;
|
const RE_TWITTER =
|
||||||
|
/(?:https?:\/\/)?(?:(?:w){3}\.)?(?:twitter|x)\.com\/[^/]+\/status\/(\d+)/;
|
||||||
const RE_TWITTER_EMBED =
|
const RE_TWITTER_EMBED =
|
||||||
/^<blockquote[\s\S]*?\shref=["'](https?:\/\/(?:twitter|x)\.com\/[^"']*)/i;
|
/^<blockquote[\s\S]*?\shref=["'](https?:\/\/(?:twitter|x)\.com\/[^"']*)/i;
|
||||||
|
|
||||||
@ -150,14 +152,20 @@ export const getEmbedLink = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (RE_TWITTER.test(link)) {
|
if (RE_TWITTER.test(link)) {
|
||||||
// the embed srcdoc still supports twitter.com domain only
|
const postId = link.match(RE_TWITTER)![1];
|
||||||
link = link.replace(/\bx.com\b/, "twitter.com");
|
// the embed srcdoc still supports twitter.com domain only.
|
||||||
|
// Note that we don't attempt to parse the username as it can consist of
|
||||||
|
// non-latin1 characters, and the username in the url can be set to anything
|
||||||
|
// without affecting the embed.
|
||||||
|
const safeURL = sanitizeHTMLAttribute(
|
||||||
|
`https://twitter.com/x/status/${postId}`,
|
||||||
|
);
|
||||||
|
|
||||||
const ret: IframeData = {
|
const ret: IframeData = {
|
||||||
type: "document",
|
type: "document",
|
||||||
srcdoc: (theme: string) =>
|
srcdoc: (theme: string) =>
|
||||||
createSrcDoc(
|
createSrcDoc(
|
||||||
`<blockquote class="twitter-tweet" data-dnt="true" data-theme="${theme}"><a href="${link}"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>`,
|
`<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>`,
|
||||||
),
|
),
|
||||||
intrinsicSize: { w: 480, h: 480 },
|
intrinsicSize: { w: 480, h: 480 },
|
||||||
sandbox: { allowSameOrigin: true },
|
sandbox: { allowSameOrigin: true },
|
||||||
@ -167,11 +175,15 @@ export const getEmbedLink = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (RE_GH_GIST.test(link)) {
|
if (RE_GH_GIST.test(link)) {
|
||||||
|
const [, user, gistId] = link.match(RE_GH_GIST)!;
|
||||||
|
const safeURL = sanitizeHTMLAttribute(
|
||||||
|
`https://gist.github.com/${user}/${gistId}`,
|
||||||
|
);
|
||||||
const ret: IframeData = {
|
const ret: IframeData = {
|
||||||
type: "document",
|
type: "document",
|
||||||
srcdoc: () =>
|
srcdoc: () =>
|
||||||
createSrcDoc(`
|
createSrcDoc(`
|
||||||
<script src="${link}.js"></script>
|
<script src="${safeURL}.js"></script>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
* { margin: 0px; }
|
* { margin: 0px; }
|
||||||
table, .gist { height: 100%; }
|
table, .gist { height: 100%; }
|
||||||
@ -290,8 +302,8 @@ export const maybeParseEmbedSrc = (str: string): string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const gistMatch = str.match(RE_GH_GIST_EMBED);
|
const gistMatch = str.match(RE_GH_GIST_EMBED);
|
||||||
if (gistMatch && gistMatch.length === 3) {
|
if (gistMatch && gistMatch.length === 2) {
|
||||||
return `https://gist.github.com/${gistMatch[1]}/${gistMatch[2]}`;
|
return gistMatch[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RE_GIPHY.test(str)) {
|
if (RE_GIPHY.test(str)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user