mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: allowed_iframes site setting for allowing iframes
This allows you to whitelist custom iframes if needed in posts
This commit is contained in:
parent
b3b6eeff97
commit
e283e6aea0
@ -62,6 +62,7 @@ export function buildOptions(state) {
|
|||||||
lookupImageUrls,
|
lookupImageUrls,
|
||||||
censoredWords,
|
censoredWords,
|
||||||
allowedHrefSchemes: siteSettings.allowed_href_schemes ? siteSettings.allowed_href_schemes.split('|') : null,
|
allowedHrefSchemes: siteSettings.allowed_href_schemes ? siteSettings.allowed_href_schemes.split('|') : null,
|
||||||
|
allowedIframes: (siteSettings.allowed_iframes || '').split('|'),
|
||||||
markdownIt: true,
|
markdownIt: true,
|
||||||
previewing
|
previewing
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import xss from 'pretty-text/xss';
|
import xss from 'pretty-text/xss';
|
||||||
|
|
||||||
const _validIframes = [];
|
|
||||||
|
|
||||||
function attr(name, value) {
|
function attr(name, value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
return `${name}="${xss.escapeAttrValue(value)}"`;
|
return `${name}="${xss.escapeAttrValue(value)}"`;
|
||||||
@ -69,7 +67,8 @@ export function sanitize(text, whiteLister) {
|
|||||||
text = text.replace(/<([^A-Za-z\/\!]|$)/g, "<$1");
|
text = text.replace(/<([^A-Za-z\/\!]|$)/g, "<$1");
|
||||||
|
|
||||||
const whiteList = whiteLister.getWhiteList(),
|
const whiteList = whiteLister.getWhiteList(),
|
||||||
allowedHrefSchemes = whiteLister.getAllowedHrefSchemes();
|
allowedHrefSchemes = whiteLister.getAllowedHrefSchemes(),
|
||||||
|
allowedIframes = whiteLister.getAllowedIframes();
|
||||||
let extraHrefMatchers = null;
|
let extraHrefMatchers = null;
|
||||||
|
|
||||||
if (allowedHrefSchemes && allowedHrefSchemes.length > 0) {
|
if (allowedHrefSchemes && allowedHrefSchemes.length > 0) {
|
||||||
@ -85,11 +84,13 @@ export function sanitize(text, whiteLister) {
|
|||||||
const forTag = whiteList.attrList[tag];
|
const forTag = whiteList.attrList[tag];
|
||||||
if (forTag) {
|
if (forTag) {
|
||||||
const forAttr = forTag[name];
|
const forAttr = forTag[name];
|
||||||
if ((forAttr && (forAttr.indexOf('*') !== -1 || forAttr.indexOf(value) !== -1)) ||
|
if (
|
||||||
|
(forAttr && (forAttr.indexOf('*') !== -1 || forAttr.indexOf(value) !== -1)) ||
|
||||||
(name.indexOf('data-') === 0 && forTag['data-*']) ||
|
(name.indexOf('data-') === 0 && forTag['data-*']) ||
|
||||||
((tag === 'a' && name === 'href') && hrefAllowed(value, extraHrefMatchers)) ||
|
((tag === 'a' && name === 'href') && hrefAllowed(value, extraHrefMatchers)) ||
|
||||||
(tag === 'img' && name === 'src' && (/^data:image.*$/i.test(value) || hrefAllowed(value, extraHrefMatchers))) ||
|
(tag === 'img' && name === 'src' && (/^data:image.*$/i.test(value) || hrefAllowed(value, extraHrefMatchers))) ||
|
||||||
(tag === 'iframe' && name === 'src' && _validIframes.some(i => i.test(value)))) {
|
(tag === 'iframe' && name === 'src' && allowedIframes.some(i => { return value.toLowerCase().indexOf((i || '').toLowerCase()) === 0;}))
|
||||||
|
) {
|
||||||
return attr(name, value);
|
return attr(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,10 +115,3 @@ export function sanitize(text, whiteLister) {
|
|||||||
.replace(/'/g, "'")
|
.replace(/'/g, "'")
|
||||||
.replace(/ \/>/g, '>');
|
.replace(/ \/>/g, '>');
|
||||||
};
|
};
|
||||||
|
|
||||||
export function whiteListIframe(regexp) {
|
|
||||||
_validIframes.push(regexp);
|
|
||||||
}
|
|
||||||
|
|
||||||
whiteListIframe(/^(https?:)?\/\/www\.google\.com\/maps\/embed\?.+/i);
|
|
||||||
whiteListIframe(/^(https?:)?\/\/www\.openstreetmap\.org\/export\/embed.html\?.+/i);
|
|
||||||
|
@ -9,6 +9,7 @@ export default class WhiteLister {
|
|||||||
|
|
||||||
this._enabled = { "default": true };
|
this._enabled = { "default": true };
|
||||||
this._allowedHrefSchemes = (options && options.allowedHrefSchemes) || [];
|
this._allowedHrefSchemes = (options && options.allowedHrefSchemes) || [];
|
||||||
|
this._allowedIframes = (options && options.allowedIframes) || [];
|
||||||
this._rawFeatures = [["default", DEFAULT_LIST]];
|
this._rawFeatures = [["default", DEFAULT_LIST]];
|
||||||
|
|
||||||
this._cache = null;
|
this._cache = null;
|
||||||
@ -102,6 +103,10 @@ export default class WhiteLister {
|
|||||||
getAllowedHrefSchemes() {
|
getAllowedHrefSchemes() {
|
||||||
return this._allowedHrefSchemes;
|
return this._allowedHrefSchemes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAllowedIframes() {
|
||||||
|
return this._allowedIframes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only add to `default` when you always want your whitelist to occur. In other words,
|
// Only add to `default` when you always want your whitelist to occur. In other words,
|
||||||
|
@ -1058,6 +1058,7 @@ en:
|
|||||||
use_admin_ip_whitelist: "Admins can only log in if they are at an IP address defined in the Screened IPs list (Admin > Logs > Screened Ips)."
|
use_admin_ip_whitelist: "Admins can only log in if they are at an IP address defined in the Screened IPs list (Admin > Logs > Screened Ips)."
|
||||||
blacklist_ip_blocks: "A list of private IP blocks that should never be crawled by Discourse"
|
blacklist_ip_blocks: "A list of private IP blocks that should never be crawled by Discourse"
|
||||||
whitelist_internal_hosts: "A list of internal hosts that discourse can safely crawl for oneboxing and other purposes"
|
whitelist_internal_hosts: "A list of internal hosts that discourse can safely crawl for oneboxing and other purposes"
|
||||||
|
allowed_iframes: "A list of iframe src domain prefixes that discourse can safely allow in posts"
|
||||||
top_menu: "Determine which items appear in the homepage navigation, and in what order. Example latest|new|unread|categories|top|read|posted|bookmarks"
|
top_menu: "Determine which items appear in the homepage navigation, and in what order. Example latest|new|unread|categories|top|read|posted|bookmarks"
|
||||||
post_menu: "Determine which items appear on the post menu, and in what order. Example like|edit|flag|delete|share|bookmark|reply"
|
post_menu: "Determine which items appear on the post menu, and in what order. Example like|edit|flag|delete|share|bookmark|reply"
|
||||||
post_menu_hidden_items: "The menu items to hide by default in the post menu unless an expansion ellipsis is clicked on."
|
post_menu_hidden_items: "The menu items to hide by default in the post menu unless an expansion ellipsis is clicked on."
|
||||||
|
@ -940,6 +940,10 @@ security:
|
|||||||
whitelist_internal_hosts:
|
whitelist_internal_hosts:
|
||||||
default: ''
|
default: ''
|
||||||
type: list
|
type: list
|
||||||
|
allowed_iframes:
|
||||||
|
default: 'https://www.google.com/maps/embed?|https://www.openstreetmap.org/export/embed.html?'
|
||||||
|
type: list
|
||||||
|
client: true
|
||||||
|
|
||||||
onebox:
|
onebox:
|
||||||
enable_flash_video_onebox: false
|
enable_flash_video_onebox: false
|
||||||
|
@ -1104,4 +1104,24 @@ HTML
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "can properly whitelist iframes" do
|
||||||
|
SiteSetting.allowed_iframes = "https://bob.com/a|http://silly.com?EMBED="
|
||||||
|
raw = <<~IFRAMES
|
||||||
|
<iframe src='https://www.google.com/maps/Embed?testing'></iframe>
|
||||||
|
<iframe src='https://bob.com/a?testing'></iframe>
|
||||||
|
<iframe src='HTTP://SILLY.COM?EMBED=111'></iframe>
|
||||||
|
IFRAMES
|
||||||
|
|
||||||
|
# we require explicit HTTPS here
|
||||||
|
html = <<~IFRAMES
|
||||||
|
<iframe src="https://bob.com/a?testing"></iframe>
|
||||||
|
<iframe src="HTTP://SILLY.COM?EMBED=111"></iframe>
|
||||||
|
IFRAMES
|
||||||
|
|
||||||
|
cooked = PrettyText.cook(raw).strip
|
||||||
|
|
||||||
|
expect(cooked).to eq(html.strip)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user