Add filter hook to forbid embed access

pull/3888/head
Chocobozzz 2021-03-23 17:18:18 +01:00
parent 4bc45da342
commit eebd9838f0
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
6 changed files with 107 additions and 5 deletions

View File

@ -2,7 +2,9 @@ import * as express from 'express'
import { constants, promises as fs } from 'fs'
import { readFile } from 'fs-extra'
import { join } from 'path'
import { logger } from '@server/helpers/logger'
import { CONFIG } from '@server/initializers/config'
import { Hooks } from '@server/lib/plugins/hooks'
import { HttpStatusCode } from '@shared/core-utils'
import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n'
import { root } from '../helpers/core-utils'
@ -27,6 +29,7 @@ const embedMiddlewares = [
? embedCSP
: (req: express.Request, res: express.Response, next: express.NextFunction) => next(),
// Set headers
(req: express.Request, res: express.Response, next: express.NextFunction) => {
res.removeHeader('X-Frame-Options')
@ -105,6 +108,24 @@ function serveServerTranslations (req: express.Request, res: express.Response) {
}
async function generateEmbedHtmlPage (req: express.Request, res: express.Response) {
const hookName = req.originalUrl.startsWith('/video-playlists/')
? 'filter:html.embed.video-playlist.allowed.result'
: 'filter:html.embed.video.allowed.result'
const allowParameters = { req }
const allowedResult = await Hooks.wrapFun(
isEmbedAllowed,
allowParameters,
hookName
)
if (!allowedResult || allowedResult.allowed !== true) {
logger.info('Embed is not allowed.', { allowedResult })
return sendHTML(allowedResult?.html || '', res)
}
const html = await ClientHtml.getEmbedHTML()
return sendHTML(html, res)
@ -158,3 +179,10 @@ function serveClientOverride (path: string) {
}
}
}
type AllowedResult = { allowed: boolean, html?: string }
function isEmbedAllowed (_object: {
req: express.Request
}): AllowedResult {
return { allowed: true }
}

View File

@ -132,7 +132,7 @@ function checkAllowResult (res: express.Response, allowParameters: any, result?:
if (!result || result.allowed !== true) {
logger.info('Download is not allowed.', { result, allowParameters })
res.status(HttpStatusCode.FORBIDDEN_403)
.json({ error: result.errorMessage || 'Refused download' })
.json({ error: result?.errorMessage || 'Refused download' })
return false
}

View File

@ -216,7 +216,7 @@ async function isVideoCommentAccepted (req: express.Request, res: express.Respon
if (!acceptedResult || acceptedResult.accepted !== true) {
logger.info('Refused local comment.', { acceptedResult, acceptParameters })
res.status(HttpStatusCode.FORBIDDEN_403)
.json({ error: acceptedResult.errorMessage || 'Refused local comment' })
.json({ error: acceptedResult?.errorMessage || 'Refused local comment' })
return false
}

View File

@ -210,6 +210,26 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
return result
}
})
registerHook({
target: 'filter:html.embed.video.allowed.result',
handler: (result, params) => {
return {
allowed: false,
html: 'Lu Bu'
}
}
})
registerHook({
target: 'filter:html.embed.video-playlist.allowed.result',
handler: (result, params) => {
return {
allowed: false,
html: 'Diao Chan'
}
}
})
}
async function unregister () {

View File

@ -3,10 +3,12 @@
import 'mocha'
import * as chai from 'chai'
import { ServerConfig } from '@shared/models'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
import {
addVideoCommentReply,
addVideoCommentThread,
createLive,
createVideoPlaylist,
doubleFollow,
getAccountVideos,
getConfig,
@ -15,6 +17,7 @@ import {
getVideo,
getVideoChannelVideos,
getVideoCommentThreads,
getVideoPlaylist,
getVideosList,
getVideosListPagination,
getVideoThreadComments,
@ -32,9 +35,15 @@ import {
} from '../../../shared/extra-utils'
import { cleanupTests, flushAndRunMultipleServers, ServerInfo } from '../../../shared/extra-utils/server/servers'
import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports'
import { VideoDetails, VideoImport, VideoImportState, VideoPrivacy } from '../../../shared/models/videos'
import {
VideoDetails,
VideoImport,
VideoImportState,
VideoPlaylist,
VideoPlaylistPrivacy,
VideoPrivacy
} from '../../../shared/models/videos'
import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
const expect = chai.expect
@ -418,6 +427,47 @@ describe('Test plugin filter hooks', function () {
})
})
describe('Embed filters', function () {
const embedVideos: VideoDetails[] = []
const embedPlaylists: VideoPlaylist[] = []
before(async function () {
this.timeout(60000)
await updateCustomSubConfig(servers[0].url, servers[0].accessToken, {
transcoding: {
enabled: false
}
})
for (const name of [ 'bad embed', 'good embed' ]) {
{
const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: name })).uuid
const res = await getVideo(servers[0].url, uuid)
embedVideos.push(res.body)
}
{
const playlistAttrs = { displayName: name, videoChannelId: servers[0].videoChannel.id, privacy: VideoPlaylistPrivacy.PUBLIC }
const res = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs })
const resPlaylist = await getVideoPlaylist(servers[0].url, res.body.videoPlaylist.id)
embedPlaylists.push(resPlaylist.body)
}
}
})
it('Should run filter:html.embed.video.allowed.result', async function () {
const res = await makeRawRequest(servers[0].url + embedVideos[0].embedPath, 200)
expect(res.text).to.equal('Lu Bu')
})
it('Should run filter:html.embed.video-playlist.allowed.result', async function () {
const res = await makeRawRequest(servers[0].url + embedPlaylists[0].embedPath, 200)
expect(res.text).to.equal('Diao Chan')
})
})
after(async function () {
await cleanupTests(servers)
})

View File

@ -54,7 +54,11 @@ export const serverFilterHookObject = {
// Filter result used to check if video/torrent download is allowed
'filter:api.download.video.allowed.result': true,
'filter:api.download.torrent.allowed.result': true
'filter:api.download.torrent.allowed.result': true,
// Filter result to check if the embed is allowed for a particular request
'filter:html.embed.video.allowed.result': true,
'filter:html.embed.video-playlist.allowed.result': true
}
export type ServerFilterHookName = keyof typeof serverFilterHookObject