Browse Source

Protect all video related AP endpoints

Chocobozzz 1 week ago
parent
commit
1642c5b9e7

+ 3 - 0
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
 

+ 2 - 2
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
   }
 

+ 11 - 12
server/core/middlewares/validators/static.ts

@@ -1,19 +1,19 @@
-import express from 'express'
-import { query } from 'express-validator'
-import { LRUCache } from 'lru-cache'
-import { basename, dirname } from 'path'
+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 { 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 { 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 { 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 })

+ 6 - 2
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()
   }

+ 5 - 12
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
-}

+ 6 - 0
server/core/models/video/video.ts

@@ -1392,6 +1392,12 @@ export class VideoModel extends SequelizeModel<VideoModel> {
     return queryBuilder.queryVideo({ id, transaction, type: 'thumbnails-blacklist' })
   }
 
+  static loadAndPopulateAccountAndFiles (id: number | string, transaction?: Transaction): Promise<MVideoAccountLightBlacklistAllFiles> {
+    const queryBuilder = new VideoModelGetQueryBuilder(VideoModel.sequelize)
+
+    return queryBuilder.queryVideo({ id, transaction, type: 'account-blacklist-files' })
+  }
+
   static loadImmutableAttributes (id: number | string, t?: Transaction): Promise<MVideoImmutable> {
     const fun = () => {
       const query = {

+ 4 - 4
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