Commit 9dd66bf4 authored by Wojciech Barcik's avatar Wojciech Barcik
Browse files

Organize iframe sandboxing in a better way

parent e39f5a46
import React from 'react';
/**
* Regular expressions for detecting and validating provider URLs
* @type {{htmlReplacement: RegExp, main: RegExp, sanitize: RegExp}}
......@@ -7,9 +5,17 @@ import React from 'react';
const regex = {
sanitize: /^(https?:)?\/\/[a-z]*\.dapplr.in\/file\/dapplr-videos\/.*/i,
};
export default regex;
/**
* Configuration for HTML iframe's `sandbox` attribute
* @type {useSandbox: boolean, sandboxAttributes: string[]}
*/
export const sandboxConfig = {
useSandbox: true,
sandboxAttributes: [],
};
/**
* Check if the iframe code in the post editor is to an allowed URL
* <iframe src="https://*.dapplr.in/file/dapplr-videos/" frameborder="0" allowfullscreen="true" scrolling="no" height="378" width="620"></iframe>
......
......@@ -5,13 +5,24 @@ import React from 'react';
* @type {{htmlReplacement: RegExp, main: RegExp, sanitize: RegExp}}
*/
const regex = {
sanitize: /^https:\/\/emb.d.tube\/\#\!\/([a-zA-Z0-9\-\.\/]+)$/,
main: /https:\/\/(?:emb\.)?(?:d.tube\/\#\!\/(?:v\/)?)([a-zA-Z0-9\-\.\/]*)/,
contentId: /(?:d\.tube\/#!\/(?:v\/)?([a-zA-Z0-9\-\.\/]*))+/,
// eslint-disable-next-line no-useless-escape
sanitize: /^https:\/\/emb\.d\.tube\/#!\/([a-zA-Z0-9-.\/]+)$/,
// eslint-disable-next-line no-useless-escape
main: /https:\/\/(?:emb\.)?(?:d\.tube\/#!\/(?:v\/)?)([a-zA-Z0-9\-.\/]*)/,
// eslint-disable-next-line no-useless-escape
contentId: /(?:d\.tube\/#!\/(?:v\/)?([a-zA-Z0-9\-.\/]*))+/,
};
export default regex;
/**
* Configuration for HTML iframe's `sandbox` attribute
* @type {useSandbox: boolean, sandboxAttributes: string[]}
*/
export const sandboxConfig = {
useSandbox: true,
sandboxAttributes: ['allow-scripts', 'allow-same-origin'],
};
/**
* Generates the Markdown/HTML code to override the detected URL with an iFrame
* @param idx
......@@ -32,6 +43,13 @@ export function genIframeMd(idx, dtubeId, w, h) {
height={h}
frameBorder="0"
allowFullScreen
sandbox={
sandboxConfig.useSandbox
? sandboxConfig.sandboxAttributes
? sandboxConfig.sandboxAttributes.join(' ')
: true
: ''
}
/>
</div>
);
......@@ -39,7 +57,7 @@ export function genIframeMd(idx, dtubeId, w, h) {
/**
* Check if the iframe code in the post editor is to an allowed URL
* <iframe title="DTube embedded player" src="https://emb.d.tube/#!/lemwong/QmQqxBCkoVusMRwP6D9oBMRQdASFzABdKQxE7xLysfmsR6" width="640" height="360" frameborder="0" allowfullscreen=""></iframe>
* <iframe title="DTube embedded player" src="https://emb.d.tube/#!/cyberspacegod/QmfHTqZWQkJ6uqLsca4wgZffGE3To6YVSzazFD3ReS1NcA" width="640" height="360" frameborder="0" allowfullscreen=""></iframe>
* @param url
* @returns {boolean|*}
*/
......
# Example Media Players 2020-07-23
## dapplr direct html iframe markup
<iframe src="https://cdn.dapplr.in/file/dapplr-videos/cryptoanalysis/yuHDBQG8XY3IBMwD0f4je9b1P7u7PAsT.mp4"></iframe>
## dapplr iframe embedded from link
Not supported
## dtube direct html iframe markup
<iframe title="DTube embedded player" src="https://emb.d.tube/#!/cyberspacegod/QmfHTqZWQkJ6uqLsca4wgZffGE3To6YVSzazFD3ReS1NcA"></iframe>
## dtube iframe embedded from link
https://emb.d.tube/#!/cyberspacegod/QmfHTqZWQkJ6uqLsca4wgZffGE3To6YVSzazFD3ReS1NcA
## soundcloud direct html iframe markup
<iframe width="100%" height="450" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/257659076&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false&amp;visual=true"></iframe>
## soundcloud iframe embedded from link
Not supported
## threespeak direct html iframe markup
<iframe src="https://3speak.online/embed?v=threespeak/iaarkpvf"></iframe>
## threespeak iframe embedded from link
It's broken
https://3speak.online/watch?v=threespeak/iaarkpvf
## twitch direct html iframe markup
It's broken, because we don't set parameter `parent` in url query. Iframe's attribute `src` should be something like `https://player.twitch.tv/?<channel, video, or collection>&parent=streamernews.example.com`. From their docs: "parent string (required) Domain(s) that will be embedding Twitch. You must have one parent key for each domain your site uses."
<iframe src="https://player.twitch.tv/?channel=tfue" frameborder="0" allowfullscreen="true" scrolling="no" height="378" width="620"></iframe>
## twitter
https://twitter.com/missybahia/status/1281295770298318849
## vimeo direct html iframe markup
<iframe src="https://player.vimeo.com/video/179213493"></iframe>
## vimeo iframe embedded from link
Looks broken…
https://player.vimeo.com/video/179213493
## youtube direct html iframe markup
<iframe src="https://www.youtube.com/embed/KOnk7Nbqkhs" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
## youtube iframe embedded from link
https://www.youtube.com/embed/KOnk7Nbqkhs
......@@ -3,6 +3,7 @@ import {
validateIframeUrl as validateDtubeIframeUrl,
normalizeEmbedUrl as normalizeDtubeEmbedUrl,
embedNode as embedDtubeNode,
sandboxConfig as sandboxConfigDtube,
} from 'app/components/elements/EmbeddedPlayers/dtube';
import {
......@@ -10,15 +11,20 @@ import {
validateIframeUrl as validateTwitchIframeUrl,
normalizeEmbedUrl as normalizeTwitchEmbedUrl,
embedNode as embedTwitchNode,
sandboxConfig as sandboxConfigTwitch,
} from 'app/components/elements/EmbeddedPlayers/twitch';
import { validateIframeUrl as validateSoundcloudIframeUrl } from 'app/components/elements/EmbeddedPlayers/soundcloud';
import {
validateIframeUrl as validateSoundcloudIframeUrl,
sandboxConfig as sandboxConfigSoundcloud,
} from 'app/components/elements/EmbeddedPlayers/soundcloud';
import {
genIframeMd as genYoutubeIframeMd,
validateIframeUrl as validateYoutubeIframeUrl,
normalizeEmbedUrl as normalizeYoutubeEmbedUrl,
embedNode as embedYoutubeNode,
sandboxConfig as sandboxConfigYoutube,
} from 'app/components/elements/EmbeddedPlayers/youtube';
import {
......@@ -26,6 +32,7 @@ import {
validateIframeUrl as validateVimeoIframeUrl,
normalizeEmbedUrl as normalizeVimeoEmbedUrl,
embedNode as embedVimeoNode,
sandboxConfig as sandboxConfigVimeo,
} from 'app/components/elements/EmbeddedPlayers/vimeo';
import {
......@@ -34,6 +41,7 @@ import {
normalizeEmbedUrl as normalizeThreespeakEmbedUrl,
embedNode as embedThreeSpeakNode,
preprocessHtml as preprocess3SpeakHtml,
sandboxConfig as sandboxConfigThreespeak,
} from 'app/components/elements/EmbeddedPlayers/threespeak';
import {
......@@ -42,9 +50,23 @@ import {
normalizeEmbedUrl as normalizeTwitterEmbedUrl,
embedNode as embedTwitterNode,
preprocessHtml as preprocessTwitterHtml,
sandboxConfig as sandboxConfigTwitter,
} from 'app/components/elements/EmbeddedPlayers/twitter';
import { validateIframeUrl as validateDapplrVideoUrl } from 'app/components/elements/EmbeddedPlayers/dapplr';
import {
validateIframeUrl as validateDapplrVideoUrl,
sandboxConfig as sandboxConfigDapplr,
} from 'app/components/elements/EmbeddedPlayers/dapplr';
// Set only those attributes in `sandboxAttributes`, that are minimally
// required for a given provider.
// When the embedded document has the same origin as the embedding page,
// it is strongly discouraged to use both allow-scripts
// and allow-same-origin, as that lets the embedded document remove
// the sandbox attribute — making it no more secure than not using
// the sandbox attribute at all. Also note that the sandbox attribute
// is unsupported in Internet Explorer 9 and earlier.
// See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe.
const supportedProviders = [
{
......@@ -53,7 +75,7 @@ const supportedProviders = [
normalizeEmbedUrlFn: normalizeDtubeEmbedUrl,
embedNodeFn: embedDtubeNode,
genIframeMdFn: genDtubeIframeMd,
useSandbox: false,
...sandboxConfigDtube,
},
{
id: 'twitch',
......@@ -61,7 +83,7 @@ const supportedProviders = [
normalizeEmbedUrlFn: normalizeTwitchEmbedUrl,
embedNodeFn: embedTwitchNode,
genIframeMdFn: genTwitchIframeMd,
useSandbox: false,
...sandboxConfigTwitch,
},
{
id: 'soundcloud',
......@@ -69,7 +91,7 @@ const supportedProviders = [
normalizeEmbedUrlFn: null,
embedNodeFn: null,
genIframeMdFn: null,
useSandbox: false,
...sandboxConfigSoundcloud,
},
{
id: 'youtube',
......@@ -77,7 +99,7 @@ const supportedProviders = [
normalizeEmbedUrlFn: normalizeYoutubeEmbedUrl,
embedNodeFn: embedYoutubeNode,
genIframeMdFn: genYoutubeIframeMd,
useSandbox: false,
...sandboxConfigYoutube,
},
{
id: 'vimeo',
......@@ -85,7 +107,7 @@ const supportedProviders = [
normalizeEmbedUrlFn: normalizeVimeoEmbedUrl,
embedNodeFn: embedVimeoNode,
genIframeMdFn: genVimeoIframeMd,
useSandbox: false,
...sandboxConfigVimeo,
},
{
id: 'threespeak',
......@@ -93,7 +115,7 @@ const supportedProviders = [
normalizeEmbedUrlFn: normalizeThreespeakEmbedUrl,
embedNodeFn: embedThreeSpeakNode,
genIframeMdFn: genThreespeakIframeMd,
useSandbox: false,
...sandboxConfigThreespeak,
},
{
id: 'twitter',
......@@ -101,7 +123,7 @@ const supportedProviders = [
normalizeEmbedUrlFn: normalizeTwitterEmbedUrl,
embedNodeFn: embedTwitterNode,
genIframeMdFn: genTwitterIframeMd,
useSandbox: false,
...sandboxConfigTwitter,
},
{
id: 'dapplr',
......@@ -109,7 +131,7 @@ const supportedProviders = [
normalizeEmbedUrlFn: null,
embedNodeFn: null,
genIframeMdFn: null,
useSandbox: true,
...sandboxConfigDapplr,
},
];
......@@ -118,7 +140,7 @@ export default supportedProviders;
/**
* Allow iFrame in the Markdown if the source URL is allowed
* @param url
* @returns { boolean | { providerId: string, useSandbox: boolean, validUrl: string }}
* @returns { boolean | { providerId: string, sandboxAttributes: string[], useSandbox: boolean, validUrl: string }}
*/
export function validateIframeUrl(url) {
for (let pi = 0; pi < supportedProviders.length; pi += 1) {
......@@ -130,6 +152,7 @@ export function validateIframeUrl(url) {
console.log(`Found a valid ${provider.id} iframe URL`);
return {
providerId: provider.id,
sandboxAttributes: provider.sandboxAttributes || [],
useSandbox: provider.useSandbox,
validUrl,
};
......
......@@ -3,11 +3,20 @@
* @type {{htmlReplacement: RegExp, main: RegExp, sanitize: RegExp}}
*/
const regex = {
sanitize: /^https:\/\/w.soundcloud.com\/player\/.*?url=(.+?)&.*/i,
sanitize: /^https:\/\/w\.soundcloud\.com\/player\/.*?url=(.+?)&.*/i,
};
export default regex;
/**
* Configuration for HTML iframe's `sandbox` attribute
* @type {useSandbox: boolean, sandboxAttributes: string[]}
*/
export const sandboxConfig = {
useSandbox: true,
sandboxAttributes: ['allow-scripts', 'allow-same-origin', 'allow-popups'],
};
/**
* Check if the iframe code in the post editor is to an allowed URL
* <iframe width="100%" height="450" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/257659076&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false&amp;visual=true"></iframe>
......
......@@ -5,16 +5,28 @@ import React from 'react';
* @type {{htmlReplacement: RegExp, main: RegExp, sanitize: RegExp}}
*/
const regex = {
sanitize: /^https:\/\/3speak.online\/embed\?v=([A-Za-z0-9\_\-\/]+)(&.*)?$/,
main: /(?:https?:\/\/(?:(?:3speak.online\/watch\?v=)|(?:3speak.online\/embed\?v=)))([A-Za-z0-9\_\-\/]+)(&.*)?/i,
htmlReplacement: /<a href="(https?:\/\/3speak.online\/watch\?v=([A-Za-z0-9\_\-\/]+))".*<img.*?><\/a>/i,
// eslint-disable-next-line no-useless-escape
sanitize: /^https:\/\/3speak\.online\/embed\?v=([A-Za-z0-9_\-\/]+)(&.*)?$/,
// eslint-disable-next-line no-useless-escape
main: /(?:https?:\/\/(?:(?:3speak\.online\/watch\?v=)|(?:3speak\.online\/embed\?v=)))([A-Za-z0-9_\-\/]+)(&.*)?/i,
// eslint-disable-next-line no-useless-escape
htmlReplacement: /<a href="(https?:\/\/3speak\.online\/watch\?v=([A-Za-z0-9_\-\/]+))".*<img.*?><\/a>/i,
embedShorthand: /~~~ embed:(.*?)\/(.*?) threespeak ~~~/,
};
export default regex;
/**
* Configuration for HTML iframe's `sandbox` attribute
* @type {useSandbox: boolean, sandboxAttributes: string[]}
*/
export const sandboxConfig = {
useSandbox: true,
sandboxAttributes: ['allow-scripts', 'allow-same-origin', 'allow-popups'],
};
/**
* Generates the Markdown/HTML code to override the detected URL with an iFrame
*
* @param idx
* @param threespeakId
* @param w
......@@ -33,6 +45,13 @@ export function genIframeMd(idx, threespeakId, w, h) {
height={h}
frameBorder="0"
allowFullScreen
sandbox={
sandboxConfig.useSandbox
? sandboxConfig.sandboxAttributes
? sandboxConfig.sandboxAttributes.join(' ')
: true
: ''
}
/>
</div>
);
......@@ -40,6 +59,7 @@ export function genIframeMd(idx, threespeakId, w, h) {
/**
* Check if the iframe code in the post editor is to an allowed URL
* <iframe src="https://3speak.online/embed?v=threespeak/iaarkpvf"></iframe>
* @param url
* @returns {boolean|*}
*/
......
......@@ -5,12 +5,20 @@ import React from 'react';
* @type {{htmlReplacement: RegExp, main: RegExp, sanitize: RegExp}}
*/
const regex = {
sanitize: /^(https?:)?\/\/player.twitch.tv\/.*/i,
main: /https?:\/\/(?:www.)?twitch.tv\/(?:(videos)\/)?([a-zA-Z0-9][\w]{3,24})/i,
sanitize: /^(https?:)?\/\/player\.twitch.tv\/.*/i,
main: /https?:\/\/(?:www.)?twitch\.tv\/(?:(videos)\/)?([a-zA-Z0-9][\w]{3,24})/i,
};
export default regex;
/**
* Configuration for HTML iframe's `sandbox` attribute
* @type {useSandbox: boolean, sandboxAttributes: string[]}
*/
export const sandboxConfig = {
useSandbox: false,
sandboxAttributes: [],
};
/**
* Check if the iframe code in the post editor is to an allowed URL
* <iframe src="https://player.twitch.tv/?channel=tfue" frameborder="0" allowfullscreen="true" scrolling="no" height="378" width="620"></iframe>
......@@ -112,6 +120,13 @@ export function genIframeMd(idx, id, w, h) {
height={h}
frameBorder="0"
allowFullScreen
sandbox={
sandboxConfig.useSandbox
? sandboxConfig.sandboxAttributes
? sandboxConfig.sandboxAttributes.join(' ')
: true
: ''
}
/>
</div>
);
......
......@@ -9,11 +9,19 @@ import _ from 'lodash';
const regex = {
main: /(?:https?:\/\/(?:(?:twitter\.com\/(.*?)\/status\/(.*))))/i,
sanitize: /(?:https?:\/\/(?:(?:twitter\.com\/(.*?)\/status\/(.*))))/i,
htmlReplacement: /<blockquote[^>]*?><p[^>]*?>(.*?)<\/p>.*?mdash; (.*)<a href="(https:\/\/twitter.com\/.*?(.*?\/status\/(.*?))\?.*?)">(.*?)<\/a><\/blockquote>/i,
htmlReplacement: /<blockquote[^>]*?><p[^>]*?>(.*?)<\/p>.*?mdash; (.*)<a href="(https:\/\/twitter\.com\/.*?(.*?\/status\/(.*?))\?.*?)">(.*?)<\/a><\/blockquote>/i,
};
export default regex;
/**
* Configuration for HTML iframe's `sandbox` attribute
* @type {useSandbox: boolean, sandboxAttributes: string[]}
*/
export const sandboxConfig = {
useSandbox: false,
sandboxAttributes: [],
};
/**
* Extract the content ID and other metadata from the URL
* @param data
......
......@@ -5,13 +5,21 @@ import React from 'react';
* @type {{htmlReplacement: RegExp, main: RegExp, sanitize: RegExp}}
*/
const regex = {
sanitize: /^(https?:)?\/\/player.vimeo.com\/video\/([0-9]*)/i,
main: /https?:\/\/(?:vimeo.com\/|player.vimeo.com\/video\/)([0-9]+)\/?(#t=((\d+)s?))?\/?/,
contentId: /(?:vimeo.com\/|player.vimeo.com\/video\/)([0-9]+)/,
sanitize: /^(https?:)?\/\/player\.vimeo\.com\/video\/([0-9]*)/i,
main: /https?:\/\/(?:vimeo\.com\/|player\.vimeo\.com\/video\/)([0-9]+)\/?(#t=((\d+)s?))?\/?/,
contentId: /(?:vimeo\.com\/|player\.vimeo\.com\/video\/)([0-9]+)/,
};
export default regex;
/**
* Configuration for HTML iframe's `sandbox` attribute
* @type {useSandbox: boolean, sandboxAttributes: string[]}
*/
export const sandboxConfig = {
useSandbox: false,
sandboxAttributes: [],
};
/**
* Check if the iframe code in the post editor is to an allowed URL
* <iframe src="https://player.vimeo.com/video/179213493" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
......@@ -118,6 +126,13 @@ export function genIframeMd(idx, id, w, h, startTime) {
webkitallowfullscreen
mozallowfullscreen
allowFullScreen
sandbox={
sandboxConfig.useSandbox
? sandboxConfig.sandboxAttributes
? sandboxConfig.sandboxAttributes.join(' ')
: true
: ''
}
/>
</div>
);
......
......@@ -6,14 +6,22 @@ import YoutubePreview from 'app/components/elements/YoutubePreview';
* @type {{htmlReplacement: RegExp, main: RegExp, sanitize: RegExp}}
*/
const regex = {
sanitize: /^(https?:)?\/\/www.youtube.com\/embed\/.*/i,
sanitize: /^(https?:)?\/\/www\.youtube\.com\/embed\/.*/i,
//main: new RegExp(urlSet({ domain: '(?:(?:.*.)?youtube.com|youtu.be)' }), flags),
main: /(?:https?:\/\/)(?:www\.)?(?:(?:youtube.com\/watch\?v=)|(?:youtu.be\/)|(?:youtube.com\/embed\/))([A-Za-z0-9\_\-]+)[^ ]*/i,
contentId: /(?:(?:youtube.com\/watch\?v=)|(?:youtu.be\/)|(?:youtube.com\/embed\/))([A-Za-z0-9\_\-]+)/i,
main: /(?:https?:\/\/)(?:www\.)?(?:(?:youtube\.com\/watch\?v=)|(?:youtu.be\/)|(?:youtube\.com\/embed\/))([A-Za-z0-9_-]+)[^ ]*/i,
contentId: /(?:(?:youtube\.com\/watch\?v=)|(?:youtu.be\/)|(?:youtube\.com\/embed\/))([A-Za-z0-9_-]+)/i,
};
export default regex;
/**
* Configuration for HTML iframe's `sandbox` attribute
* @type {useSandbox: boolean, sandboxAttributes: string[]}
*/
export const sandboxConfig = {
useSandbox: false,
sandboxAttributes: [],
};
/**
* Check if the iframe code in the post editor is to an allowed URL
* <iframe width="560" height="315" src="https://www.youtube.com/embed/KOnk7Nbqkhs" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
......
......@@ -59,9 +59,11 @@ export default ({
transformTags: {
iframe: (tagName, attribs) => {
const srcAtty = attribs.src;
const { validUrl, useSandbox } = validateEmbbeddedPlayerIframeUrl(
srcAtty
);
const {
validUrl,
useSandbox,
sandboxAttributes,
} = validateEmbbeddedPlayerIframeUrl(srcAtty);
if (validUrl !== false) {
const iframe = {
......@@ -77,7 +79,11 @@ export default ({
},
};
if (useSandbox) {
iframe.attribs.sandbox = true;
if (sandboxAttributes.length > 0) {
iframe.attribs.sandbox = sandboxAttributes.join(' ');
} else {
iframe.attribs.sandbox = true;
}
}
return iframe;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment