refactor(embeds): drop slideshare, use vimeo api, tighten CSP

The slideshare integration was broken for quite a while already,
as slideshare doesn't seem to have a good replacement, we're
dropping it in the same manner as speakerdeck was dropped some
time ago. This means the special syntax now just renders a plain
link. This commit gets rid of the vimdo oembed API too which
allowed JSONP injection. Instead we're using the normal vimeo
video metadata API.

Co-authored-by: Philip Molares <philip.molares@udo.edu>
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
Erik Michelson 2025-04-09 21:06:07 +02:00
parent 0636b5c20b
commit 3f520ea59a
3 changed files with 16 additions and 38 deletions

View file

@ -7,17 +7,15 @@ const CspStrategy = {}
const defaultDirectives = { const defaultDirectives = {
defaultSrc: ['\'none\''], defaultSrc: ['\'none\''],
baseUri: ['\'self\''], baseUri: ['\'self\''],
connectSrc: ['\'self\'', buildDomainOriginWithProtocol(config, 'ws')], connectSrc: ['\'self\'', buildDomainOriginWithProtocol(config, 'ws'), 'https://vimeo.com/api/v2/video/'],
fontSrc: ['\'self\''], fontSrc: ['\'self\''],
manifestSrc: ['\'self\''], manifestSrc: ['\'self\''],
frameSrc: ['\'self\'', 'https://player.vimeo.com', 'https://www.slideshare.net/slideshow/embed_code/key/', 'https://www.youtube.com', 'https://gist.github.com'], frameSrc: ['\'self\'', 'https://player.vimeo.com', 'https://www.youtube.com', 'https://gist.github.com'],
imgSrc: ['*', 'data:'], // we allow using arbitrary images & explicit data for mermaid imgSrc: ['*', 'data:'], // we allow using arbitrary images & explicit data for mermaid
scriptSrc: [ scriptSrc: [
config.serverURL + '/build/', config.serverURL + '/build/',
config.serverURL + '/js/', config.serverURL + '/js/',
config.serverURL + '/config', config.serverURL + '/config',
'https://vimeo.com/api/oembed.json',
'https://www.slideshare.net/api/oembed/2',
'\'unsafe-inline\'' // this is ignored by browsers supporting nonces/hashes '\'unsafe-inline\'' // this is ignored by browsers supporting nonces/hashes
], ],
styleSrc: [config.serverURL + '/build/', config.serverURL + '/css/', '\'unsafe-inline\''], // unsafe-inline is required for some libs, plus used in views styleSrc: [config.serverURL + '/build/', config.serverURL + '/css/', '\'unsafe-inline\''], // unsafe-inline is required for some libs, plus used in views

View file

@ -238,10 +238,6 @@ When youre a carpenter making a beautiful chest of drawers, youre not goin
{%gist schacon/4277%} {%gist schacon/4277%}
#### SlideShare
{%slideshare briansolis/26-disruptive-technology-trends-2016-2018-56796196 %}
#### PDF #### PDF
**Caution: this might be blocked by your browser if not using an `https` URL.** **Caution: this might be blocked by your browser if not using an `https` URL.**

View file

@ -289,18 +289,14 @@ export function finishView (view) {
imgPlayiframe(this, 'https://player.vimeo.com/video/') imgPlayiframe(this, 'https://player.vimeo.com/video/')
}) })
.each((key, value) => { .each((key, value) => {
const vimeoLink = `https://vimeo.com/${$(value).attr('data-videoid')}` fetch(`https://vimeo.com/api/v2/video/${$(value).attr('data-videoid')}.json`)
$.ajax({ .then(response => response.json())
type: 'GET', .then(data => {
url: `https://vimeo.com/api/oembed.json?url=${encodeURIComponent(vimeoLink)}`, const image = `<img src="${data[0].thumbnail_large}" />`
jsonp: 'callback',
dataType: 'jsonp',
success (data) {
const image = `<img src="${data.thumbnail_url}" />`
$(value).prepend(image) $(value).prepend(image)
if (window.viewAjaxCallback) window.viewAjaxCallback() if (window.viewAjaxCallback) window.viewAjaxCallback()
} })
}) .catch(console.error)
}) })
// sequence diagram // sequence diagram
const sequences = view.find('div.sequence-diagram.raw').removeClass('raw') const sequences = view.find('div.sequence-diagram.raw').removeClass('raw')
@ -444,26 +440,14 @@ export function finishView (view) {
// slideshare // slideshare
view.find('div.slideshare.raw').removeClass('raw') view.find('div.slideshare.raw').removeClass('raw')
.each((key, value) => { .each((key, value) => {
$.ajax({ const url = `https://slideshare.com/${$(value).attr('data-slideshareid')}`
type: 'GET', const inner = $('<a>Slideshare</a>')
url: `https://www.slideshare.net/api/oembed/2?url=https://www.slideshare.net/${$(value).attr('data-slideshareid')}&format=json`, inner.attr('href', url)
jsonp: 'callback', inner.attr('rel', 'noopener noreferrer')
dataType: 'jsonp', inner.attr('target', '_blank')
success (data) { $(value).append(inner)
const $html = $(data.html)
const iframe = $html.closest('iframe')
const caption = $html.closest('div')
const inner = $('<div class="inner"></div>').append(iframe)
const height = iframe.attr('height')
const width = iframe.attr('width')
const ratio = (height / width) * 100
inner.css('padding-bottom', `${ratio}%`)
$(value).html(inner).append(caption)
if (window.viewAjaxCallback) window.viewAjaxCallback()
}
})
}) })
// speakerdeck // speakerdeck
view.find('div.speakerdeck.raw').removeClass('raw') view.find('div.speakerdeck.raw').removeClass('raw')
.each((key, value) => { .each((key, value) => {
const url = `https://speakerdeck.com/${$(value).attr('data-speakerdeckid')}` const url = `https://speakerdeck.com/${$(value).attr('data-speakerdeckid')}`
@ -828,7 +812,7 @@ export function smoothHashScroll () {
function imgPlayiframe (element, src) { function imgPlayiframe (element, src) {
if (!$(element).attr('data-videoid')) return if (!$(element).attr('data-videoid')) return
const iframe = $("<iframe frameborder='0' webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>") const iframe = $("<iframe style='border: none' allowfullscreen></iframe>")
$(iframe).attr('src', `${src + $(element).attr('data-videoid')}?autoplay=1`) $(iframe).attr('src', `${src + $(element).attr('data-videoid')}?autoplay=1`)
$(element).find('img').css('visibility', 'hidden') $(element).find('img').css('visibility', 'hidden')
$(element).append(iframe) $(element).append(iframe)