From 1642c5b9e774f3b53a070c8a02fd02b86e24be37 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 26 Apr 2024 15:20:47 +0200 Subject: [PATCH] Protect all video related AP endpoints --- .../core/middlewares/validators/redundancy.ts | 3 +++ .../middlewares/validators/shared/videos.ts | 4 ++-- server/core/middlewares/validators/static.ts | 23 +++++++++---------- .../validators/videos/video-comments.ts | 8 +++++-- .../validators/videos/video-shares.ts | 17 ++++---------- server/core/models/video/video.ts | 6 +++++ server/core/types/express.d.ts | 8 +++---- 7 files changed, 37 insertions(+), 32 deletions(-) diff --git a/server/core/middlewares/validators/redundancy.ts b/server/core/middlewares/validators/redundancy.ts index 27c0591da..ea9c7439e 100644 --- a/server/core/middlewares/validators/redundancy.ts +++ b/server/core/middlewares/validators/redundancy.ts @@ -16,6 +16,7 @@ import { isHostValid } from '../../helpers/custom-validators/servers.js' import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy.js' import { ServerModel } from '../../models/server/server.js' import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from './shared/index.js' +import { canVideoBeFederated } from '@server/lib/activitypub/videos/federate.js' const videoFileRedundancyGetValidator = [ isValidVideoIdParam('videoId'), @@ -31,6 +32,7 @@ const videoFileRedundancyGetValidator = [ async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return + if (!canVideoBeFederated(res.locals.onlyVideo)) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) const video = res.locals.videoAll @@ -72,6 +74,7 @@ const videoPlaylistRedundancyGetValidator = [ async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return + if (!canVideoBeFederated(res.locals.onlyVideo)) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) const video = res.locals.videoAll diff --git a/server/core/middlewares/validators/shared/videos.ts b/server/core/middlewares/validators/shared/videos.ts index 261fd15b0..65b632cfa 100644 --- a/server/core/middlewares/validators/shared/videos.ts +++ b/server/core/middlewares/validators/shared/videos.ts @@ -18,7 +18,7 @@ import { MVideoFullLight, MVideoId, MVideoImmutable, - MVideoThumbnail, + MVideoThumbnailBlacklist, MVideoUUID, MVideoWithRights } from '@server/types/models/index.js' @@ -56,7 +56,7 @@ export async function doesVideoExist (id: number | string, res: Response, fetchT break case 'only-video-and-blacklist': - res.locals.onlyVideo = video as MVideoThumbnail + res.locals.onlyVideo = video as MVideoThumbnailBlacklist break } diff --git a/server/core/middlewares/validators/static.ts b/server/core/middlewares/validators/static.ts index afd1441ab..6c5eccb64 100644 --- a/server/core/middlewares/validators/static.ts +++ b/server/core/middlewares/validators/static.ts @@ -1,19 +1,19 @@ +import { HttpStatusCode } from '@peertube/peertube-models' +import { exists, isSafePeerTubeFilenameWithoutExtension, isUUIDValid, toBooleanOrNull } from '@server/helpers/custom-validators/misc.js' +import { logger } from '@server/helpers/logger.js' +import { LRU_CACHE } from '@server/initializers/constants.js' +import { VideoFileModel } from '@server/models/video/video-file.js' +import { VideoModel } from '@server/models/video/video.js' +import { MStreamingPlaylist, MVideoFile, MVideoThumbnailBlacklist } from '@server/types/models/index.js' import express from 'express' import { query } from 'express-validator' import { LRUCache } from 'lru-cache' import { basename, dirname } from 'path' -import { exists, isSafePeerTubeFilenameWithoutExtension, isUUIDValid, toBooleanOrNull } from '@server/helpers/custom-validators/misc.js' -import { logger } from '@server/helpers/logger.js' -import { LRU_CACHE } from '@server/initializers/constants.js' -import { VideoModel } from '@server/models/video/video.js' -import { VideoFileModel } from '@server/models/video/video-file.js' -import { MStreamingPlaylist, MVideoFile, MVideoThumbnail } from '@server/types/models/index.js' -import { HttpStatusCode } from '@peertube/peertube-models' import { areValidationErrors, checkCanAccessVideoStaticFiles, isValidVideoPasswordHeader } from './shared/index.js' type LRUValue = { allowed: boolean - video?: MVideoThumbnail + video?: MVideoThumbnailBlacklist file?: MVideoFile playlist?: MStreamingPlaylist } @@ -122,8 +122,7 @@ const ensureCanAccessPrivateVideoHLSFiles = [ ] export { - ensureCanAccessVideoPrivateWebVideoFiles, - ensureCanAccessPrivateVideoHLSFiles + ensureCanAccessPrivateVideoHLSFiles, ensureCanAccessVideoPrivateWebVideoFiles } // --------------------------------------------------------------------------- @@ -139,7 +138,7 @@ async function isWebVideoAllowed (req: express.Request, res: express.Response) { return { allowed: false } } - const video = await VideoModel.load(file.getVideo().id) + const video = await VideoModel.loadWithBlacklist(file.getVideo().id) return { file, @@ -151,7 +150,7 @@ async function isWebVideoAllowed (req: express.Request, res: express.Response) { async function isHLSAllowed (req: express.Request, res: express.Response, videoUUID: string) { const filename = basename(req.path) - const video = await VideoModel.loadWithFiles(videoUUID) + const video = await VideoModel.loadAndPopulateAccountAndFiles(videoUUID) if (!video) { logger.debug('Unknown static file %s to serve', req.originalUrl, { videoUUID }) diff --git a/server/core/middlewares/validators/videos/video-comments.ts b/server/core/middlewares/validators/videos/video-comments.ts index 37da4d488..7d92b16f1 100644 --- a/server/core/middlewares/validators/videos/video-comments.ts +++ b/server/core/middlewares/validators/videos/video-comments.ts @@ -17,6 +17,7 @@ import { isValidVideoIdParam, isValidVideoPasswordHeader } from '../shared/index.js' +import { canVideoBeFederated } from '@server/lib/activitypub/videos/federate.js' const listVideoCommentsValidator = [ query('isLocal') @@ -132,8 +133,11 @@ const videoCommentGetValidator = [ async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (areValidationErrors(req, res)) return - if (!await doesVideoExist(req.params.videoId, res, 'id')) return - if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoId, res)) return + if (!await doesVideoExist(req.params.videoId, res, 'only-video-and-blacklist')) return + + if (!canVideoBeFederated(res.locals.onlyVideo)) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) + + if (!await doesVideoCommentExist(req.params.commentId, res.locals.onlyVideo, res)) return return next() } diff --git a/server/core/middlewares/validators/videos/video-shares.ts b/server/core/middlewares/validators/videos/video-shares.ts index 1ecf0fd04..075745d1c 100644 --- a/server/core/middlewares/validators/videos/video-shares.ts +++ b/server/core/middlewares/validators/videos/video-shares.ts @@ -1,11 +1,12 @@ +import { HttpStatusCode } from '@peertube/peertube-models' +import { canVideoBeFederated } from '@server/lib/activitypub/videos/federate.js' import express from 'express' import { param } from 'express-validator' -import { HttpStatusCode } from '@peertube/peertube-models' import { isIdValid } from '../../../helpers/custom-validators/misc.js' import { VideoShareModel } from '../../../models/video/video-share.js' import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared/index.js' -const videosShareValidator = [ +export const videosShareValidator = [ isValidVideoIdParam('id'), param('actorId') @@ -16,20 +17,12 @@ const videosShareValidator = [ if (!await doesVideoExist(req.params.id, res)) return const video = res.locals.videoAll + if (!canVideoBeFederated(video)) res.sendStatus(HttpStatusCode.NOT_FOUND_404) const share = await VideoShareModel.load(req.params.actorId, video.id) - if (!share) { - return res.status(HttpStatusCode.NOT_FOUND_404) - .end() - } + if (!share) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) res.locals.videoShare = share return next() } ] - -// --------------------------------------------------------------------------- - -export { - videosShareValidator -} diff --git a/server/core/models/video/video.ts b/server/core/models/video/video.ts index 3cdf1666c..9f43a75a2 100644 --- a/server/core/models/video/video.ts +++ b/server/core/models/video/video.ts @@ -1392,6 +1392,12 @@ export class VideoModel extends SequelizeModel { return queryBuilder.queryVideo({ id, transaction, type: 'thumbnails-blacklist' }) } + static loadAndPopulateAccountAndFiles (id: number | string, transaction?: Transaction): Promise { + const queryBuilder = new VideoModelGetQueryBuilder(VideoModel.sequelize) + + return queryBuilder.queryVideo({ id, transaction, type: 'account-blacklist-files' }) + } + static loadImmutableAttributes (id: number | string, t?: Transaction): Promise { const fun = () => { const query = { diff --git a/server/core/types/express.d.ts b/server/core/types/express.d.ts index 2cabacc04..8bd3a829a 100644 --- a/server/core/types/express.d.ts +++ b/server/core/types/express.d.ts @@ -20,7 +20,8 @@ import { MVideoLiveFormattable, MVideoPassword, MVideoPlaylistFull, - MVideoPlaylistFullSummary + MVideoPlaylistFullSummary, + MVideoThumbnailBlacklist } from '@server/types/models/index.js' import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token.js' import { MPlugin, MServer, MServerBlocklist } from '@server/types/models/server.js' @@ -44,8 +45,7 @@ import { MVideoCaptionVideo, MVideoFullLight, MVideoRedundancyVideo, - MVideoShareActor, - MVideoThumbnail + MVideoShareActor } from './models/index.js' import { MRunner, MRunnerJobRunner, MRunnerRegistrationToken } from './models/runners/index.js' import { MVideoSource } from './models/video/video-source.js' @@ -135,7 +135,7 @@ declare module 'express' { videoAPI?: MVideoFormattableDetails videoAll?: MVideoFullLight onlyImmutableVideo?: MVideoImmutable - onlyVideo?: MVideoThumbnail + onlyVideo?: MVideoThumbnailBlacklist videoId?: MVideoId videoLive?: MVideoLiveFormattable