aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-11-15 14:41:55 +0100
committerChocobozzz <me@florianbigard.com>2022-11-15 14:41:55 +0100
commit4638cd713dcdd007cd7f49b9a95fa62ac7823e7c (patch)
tree3e341c6ebbd1ce9e2bbacd72e7e3793e0bd467c2 /server
parent6bcb559fc9a491fc3ce83e7c077ee9dc742b1d63 (diff)
downloadPeerTube-4638cd713dcdd007cd7f49b9a95fa62ac7823e7c.tar.gz
PeerTube-4638cd713dcdd007cd7f49b9a95fa62ac7823e7c.tar.zst
PeerTube-4638cd713dcdd007cd7f49b9a95fa62ac7823e7c.zip
Don't inject untrusted input
Even if it's already checked in middlewares It's better to have safe modals too
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/users/my-history.ts3
-rw-r--r--server/controllers/api/users/my-video-playlists.ts5
-rw-r--r--server/controllers/api/video-playlist.ts5
-rw-r--r--server/controllers/api/videos/update.ts3
-rw-r--r--server/controllers/download.ts4
-rw-r--r--server/controllers/services.ts5
-rw-r--r--server/helpers/custom-validators/video-studio.ts3
-rw-r--r--server/helpers/video.ts5
-rw-r--r--server/lib/activitypub/collection.ts3
-rw-r--r--server/middlewares/pagination.ts5
-rw-r--r--server/middlewares/validators/abuse.ts3
-rw-r--r--server/middlewares/validators/redundancy.ts3
-rw-r--r--server/middlewares/validators/shared/abuses.ts3
-rw-r--r--server/middlewares/validators/shared/accounts.ts5
-rw-r--r--server/middlewares/validators/shared/users.ts3
-rw-r--r--server/middlewares/validators/shared/video-comments.ts7
-rw-r--r--server/middlewares/validators/shared/video-ownerships.ts3
-rw-r--r--server/middlewares/validators/users.ts3
-rw-r--r--server/middlewares/validators/videos/video-imports.ts3
-rw-r--r--server/middlewares/validators/videos/video-playlists.ts3
-rw-r--r--server/models/abuse/abuse-query-builder.ts5
-rw-r--r--server/models/actor/actor.ts4
-rw-r--r--server/models/user/user-notification.ts3
-rw-r--r--server/models/user/user.ts15
-rw-r--r--server/models/utils.ts5
-rw-r--r--server/models/video/sql/video/videos-id-list-query-builder.ts5
-rw-r--r--server/models/video/video-channel.ts4
-rw-r--r--server/models/video/video-playlist-element.ts21
-rw-r--r--server/models/video/video-share.ts5
-rw-r--r--server/tools/peertube-redundancy.ts4
30 files changed, 89 insertions, 59 deletions
diff --git a/server/controllers/api/users/my-history.ts b/server/controllers/api/users/my-history.ts
index bc5b40f59..e6d3e86ac 100644
--- a/server/controllers/api/users/my-history.ts
+++ b/server/controllers/api/users/my-history.ts
@@ -1,3 +1,4 @@
1import { forceNumber } from '@shared/core-utils'
1import express from 'express' 2import express from 'express'
2import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
3import { getFormattedObjects } from '../../../helpers/utils' 4import { getFormattedObjects } from '../../../helpers/utils'
@@ -55,7 +56,7 @@ async function listMyVideosHistory (req: express.Request, res: express.Response)
55async function removeUserHistoryElement (req: express.Request, res: express.Response) { 56async function removeUserHistoryElement (req: express.Request, res: express.Response) {
56 const user = res.locals.oauth.token.User 57 const user = res.locals.oauth.token.User
57 58
58 await UserVideoHistoryModel.removeUserHistoryElement(user, parseInt(req.params.videoId + '')) 59 await UserVideoHistoryModel.removeUserHistoryElement(user, forceNumber(req.params.videoId))
59 60
60 return res.sendStatus(HttpStatusCode.NO_CONTENT_204) 61 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
61} 62}
diff --git a/server/controllers/api/users/my-video-playlists.ts b/server/controllers/api/users/my-video-playlists.ts
index 715717610..fbdbb7e50 100644
--- a/server/controllers/api/users/my-video-playlists.ts
+++ b/server/controllers/api/users/my-video-playlists.ts
@@ -1,5 +1,6 @@
1import { uuidToShort } from '@shared/extra-utils'
2import express from 'express' 1import express from 'express'
2import { forceNumber } from '@shared/core-utils'
3import { uuidToShort } from '@shared/extra-utils'
3import { VideosExistInPlaylists } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model' 4import { VideosExistInPlaylists } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
4import { asyncMiddleware, authenticate } from '../../../middlewares' 5import { asyncMiddleware, authenticate } from '../../../middlewares'
5import { doVideosInPlaylistExistValidator } from '../../../middlewares/validators/videos/video-playlists' 6import { doVideosInPlaylistExistValidator } from '../../../middlewares/validators/videos/video-playlists'
@@ -22,7 +23,7 @@ export {
22// --------------------------------------------------------------------------- 23// ---------------------------------------------------------------------------
23 24
24async function doVideosInPlaylistExist (req: express.Request, res: express.Response) { 25async function doVideosInPlaylistExist (req: express.Request, res: express.Response) {
25 const videoIds = req.query.videoIds.map(i => parseInt(i + '', 10)) 26 const videoIds = req.query.videoIds.map(i => forceNumber(i))
26 const user = res.locals.oauth.token.User 27 const user = res.locals.oauth.token.User
27 28
28 const results = await VideoPlaylistModel.listPlaylistSummariesOf(user.Account.id, videoIds) 29 const results = await VideoPlaylistModel.listPlaylistSummariesOf(user.Account.id, videoIds)
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts
index 1255d14c6..67fac3751 100644
--- a/server/controllers/api/video-playlist.ts
+++ b/server/controllers/api/video-playlist.ts
@@ -46,6 +46,7 @@ import {
46import { AccountModel } from '../../models/account/account' 46import { AccountModel } from '../../models/account/account'
47import { VideoPlaylistModel } from '../../models/video/video-playlist' 47import { VideoPlaylistModel } from '../../models/video/video-playlist'
48import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' 48import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'
49import { forceNumber } from '@shared/core-utils'
49 50
50const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT) 51const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT)
51 52
@@ -245,7 +246,7 @@ async function updateVideoPlaylist (req: express.Request, res: express.Response)
245 if (videoPlaylistInfoToUpdate.description !== undefined) videoPlaylistInstance.description = videoPlaylistInfoToUpdate.description 246 if (videoPlaylistInfoToUpdate.description !== undefined) videoPlaylistInstance.description = videoPlaylistInfoToUpdate.description
246 247
247 if (videoPlaylistInfoToUpdate.privacy !== undefined) { 248 if (videoPlaylistInfoToUpdate.privacy !== undefined) {
248 videoPlaylistInstance.privacy = parseInt(videoPlaylistInfoToUpdate.privacy.toString(), 10) 249 videoPlaylistInstance.privacy = forceNumber(videoPlaylistInfoToUpdate.privacy)
249 250
250 if (wasNotPrivatePlaylist === true && videoPlaylistInstance.privacy === VideoPlaylistPrivacy.PRIVATE) { 251 if (wasNotPrivatePlaylist === true && videoPlaylistInstance.privacy === VideoPlaylistPrivacy.PRIVATE) {
251 await sendDeleteVideoPlaylist(videoPlaylistInstance, t) 252 await sendDeleteVideoPlaylist(videoPlaylistInstance, t)
@@ -424,7 +425,7 @@ async function reorderVideosPlaylist (req: express.Request, res: express.Respons
424 425
425 const endOldPosition = oldPosition + reorderLength - 1 426 const endOldPosition = oldPosition + reorderLength - 1
426 // Insert our reordered elements in their place (update) 427 // Insert our reordered elements in their place (update)
427 await VideoPlaylistElementModel.reassignPositionOf(videoPlaylist.id, oldPosition, endOldPosition, newPosition, t) 428 await VideoPlaylistElementModel.reassignPositionOf({ videoPlaylistId: videoPlaylist.id, firstPosition: oldPosition, endPosition: endOldPosition, newPosition, transaction: t })
428 429
429 // Decrease positions of elements after the old position of our ordered elements (decrease) 430 // Decrease positions of elements after the old position of our ordered elements (decrease)
430 await VideoPlaylistElementModel.increasePositionOf(videoPlaylist.id, oldPosition, -reorderLength, t) 431 await VideoPlaylistElementModel.increasePositionOf(videoPlaylist.id, oldPosition, -reorderLength, t)
diff --git a/server/controllers/api/videos/update.ts b/server/controllers/api/videos/update.ts
index 0a910379a..260dee2b9 100644
--- a/server/controllers/api/videos/update.ts
+++ b/server/controllers/api/videos/update.ts
@@ -19,6 +19,7 @@ import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosU
19import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' 19import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update'
20import { VideoModel } from '../../../models/video/video' 20import { VideoModel } from '../../../models/video/video'
21import { VideoPathManager } from '@server/lib/video-path-manager' 21import { VideoPathManager } from '@server/lib/video-path-manager'
22import { forceNumber } from '@shared/core-utils'
22 23
23const lTags = loggerTagsFactory('api', 'video') 24const lTags = loggerTagsFactory('api', 'video')
24const auditLogger = auditLoggerFactory('videos') 25const auditLogger = auditLoggerFactory('videos')
@@ -174,7 +175,7 @@ async function updateVideoPrivacy (options: {
174 const { videoInstance, videoInfoToUpdate, hadPrivacyForFederation, transaction } = options 175 const { videoInstance, videoInfoToUpdate, hadPrivacyForFederation, transaction } = options
175 const isNewVideo = videoInstance.isNewVideo(videoInfoToUpdate.privacy) 176 const isNewVideo = videoInstance.isNewVideo(videoInfoToUpdate.privacy)
176 177
177 const newPrivacy = parseInt(videoInfoToUpdate.privacy.toString(), 10) 178 const newPrivacy = forceNumber(videoInfoToUpdate.privacy)
178 setVideoPrivacy(videoInstance, newPrivacy) 179 setVideoPrivacy(videoInstance, newPrivacy)
179 180
180 // Unfederate the video if the new privacy is not compatible with federation 181 // Unfederate the video if the new privacy is not compatible with federation
diff --git a/server/controllers/download.ts b/server/controllers/download.ts
index d9f34109f..65b9a1d1b 100644
--- a/server/controllers/download.ts
+++ b/server/controllers/download.ts
@@ -5,7 +5,7 @@ import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache
5import { Hooks } from '@server/lib/plugins/hooks' 5import { Hooks } from '@server/lib/plugins/hooks'
6import { VideoPathManager } from '@server/lib/video-path-manager' 6import { VideoPathManager } from '@server/lib/video-path-manager'
7import { MStreamingPlaylist, MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' 7import { MStreamingPlaylist, MVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
8import { addQueryParams } from '@shared/core-utils' 8import { addQueryParams, forceNumber } from '@shared/core-utils'
9import { HttpStatusCode, VideoStorage, VideoStreamingPlaylistType } from '@shared/models' 9import { HttpStatusCode, VideoStorage, VideoStreamingPlaylistType } from '@shared/models'
10import { STATIC_DOWNLOAD_PATHS } from '../initializers/constants' 10import { STATIC_DOWNLOAD_PATHS } from '../initializers/constants'
11import { asyncMiddleware, optionalAuthenticate, videosDownloadValidator } from '../middlewares' 11import { asyncMiddleware, optionalAuthenticate, videosDownloadValidator } from '../middlewares'
@@ -132,7 +132,7 @@ async function downloadHLSVideoFile (req: express.Request, res: express.Response
132} 132}
133 133
134function getVideoFile (req: express.Request, files: MVideoFile[]) { 134function getVideoFile (req: express.Request, files: MVideoFile[]) {
135 const resolution = parseInt(req.params.resolution, 10) 135 const resolution = forceNumber(req.params.resolution)
136 return files.find(f => f.resolution === resolution) 136 return files.find(f => f.resolution === resolution)
137} 137}
138 138
diff --git a/server/controllers/services.ts b/server/controllers/services.ts
index cabcbc00b..7c7ca1ff3 100644
--- a/server/controllers/services.ts
+++ b/server/controllers/services.ts
@@ -4,6 +4,7 @@ import { escapeHTML } from '@shared/core-utils/renderer'
4import { EMBED_SIZE, PREVIEWS_SIZE, THUMBNAILS_SIZE, WEBSERVER } from '../initializers/constants' 4import { EMBED_SIZE, PREVIEWS_SIZE, THUMBNAILS_SIZE, WEBSERVER } from '../initializers/constants'
5import { asyncMiddleware, oembedValidator } from '../middlewares' 5import { asyncMiddleware, oembedValidator } from '../middlewares'
6import { accountNameWithHostGetValidator } from '../middlewares/validators' 6import { accountNameWithHostGetValidator } from '../middlewares/validators'
7import { forceNumber } from '@shared/core-utils'
7 8
8const servicesRouter = express.Router() 9const servicesRouter = express.Router()
9 10
@@ -108,8 +109,8 @@ function buildOEmbed (options: {
108 const { req, previewSize, previewPath, title, channel, embedPath } = options 109 const { req, previewSize, previewPath, title, channel, embedPath } = options
109 110
110 const webserverUrl = WEBSERVER.URL 111 const webserverUrl = WEBSERVER.URL
111 const maxHeight = parseInt(req.query.maxheight, 10) 112 const maxHeight = forceNumber(req.query.maxheight)
112 const maxWidth = parseInt(req.query.maxwidth, 10) 113 const maxWidth = forceNumber(req.query.maxwidth)
113 114
114 const embedUrl = webserverUrl + embedPath 115 const embedUrl = webserverUrl + embedPath
115 const embedTitle = escapeHTML(title) 116 const embedTitle = escapeHTML(title)
diff --git a/server/helpers/custom-validators/video-studio.ts b/server/helpers/custom-validators/video-studio.ts
index 19e7906d5..68dfec8dd 100644
--- a/server/helpers/custom-validators/video-studio.ts
+++ b/server/helpers/custom-validators/video-studio.ts
@@ -4,6 +4,7 @@ import { buildTaskFileFieldname } from '@server/lib/video-studio'
4import { VideoStudioTask } from '@shared/models' 4import { VideoStudioTask } from '@shared/models'
5import { isArray } from './misc' 5import { isArray } from './misc'
6import { isVideoFileMimeTypeValid, isVideoImageValid } from './videos' 6import { isVideoFileMimeTypeValid, isVideoImageValid } from './videos'
7import { forceNumber } from '@shared/core-utils'
7 8
8function isValidStudioTasksArray (tasks: any) { 9function isValidStudioTasksArray (tasks: any) {
9 if (!isArray(tasks)) return false 10 if (!isArray(tasks)) return false
@@ -24,7 +25,7 @@ function isStudioCutTaskValid (task: VideoStudioTask) {
24 25
25 if (!start || !end) return true 26 if (!start || !end) return true
26 27
27 return parseInt(start + '') < parseInt(end + '') 28 return forceNumber(start) < forceNumber(end)
28} 29}
29 30
30function isStudioTaskAddIntroOutroValid (task: VideoStudioTask, indice: number, files: Express.Multer.File[]) { 31function isStudioTaskAddIntroOutroValid (task: VideoStudioTask, indice: number, files: Express.Multer.File[]) {
diff --git a/server/helpers/video.ts b/server/helpers/video.ts
index f5f645d3e..c688ef1e3 100644
--- a/server/helpers/video.ts
+++ b/server/helpers/video.ts
@@ -2,6 +2,7 @@ import { Response } from 'express'
2import { CONFIG } from '@server/initializers/config' 2import { CONFIG } from '@server/initializers/config'
3import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo } from '@server/types/models' 3import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo } from '@server/types/models'
4import { VideoPrivacy, VideoState } from '@shared/models' 4import { VideoPrivacy, VideoState } from '@shared/models'
5import { forceNumber } from '@shared/core-utils'
5 6
6function getVideoWithAttributes (res: Response) { 7function getVideoWithAttributes (res: Response) {
7 return res.locals.videoAPI || res.locals.videoAll || res.locals.onlyVideo 8 return res.locals.videoAPI || res.locals.videoAll || res.locals.onlyVideo
@@ -14,14 +15,14 @@ function extractVideo (videoOrPlaylist: MVideo | MStreamingPlaylistVideo) {
14} 15}
15 16
16function isPrivacyForFederation (privacy: VideoPrivacy) { 17function isPrivacyForFederation (privacy: VideoPrivacy) {
17 const castedPrivacy = parseInt(privacy + '', 10) 18 const castedPrivacy = forceNumber(privacy)
18 19
19 return castedPrivacy === VideoPrivacy.PUBLIC || 20 return castedPrivacy === VideoPrivacy.PUBLIC ||
20 (CONFIG.FEDERATION.VIDEOS.FEDERATE_UNLISTED === true && castedPrivacy === VideoPrivacy.UNLISTED) 21 (CONFIG.FEDERATION.VIDEOS.FEDERATE_UNLISTED === true && castedPrivacy === VideoPrivacy.UNLISTED)
21} 22}
22 23
23function isStateForFederation (state: VideoState) { 24function isStateForFederation (state: VideoState) {
24 const castedState = parseInt(state + '', 10) 25 const castedState = forceNumber(state)
25 26
26 return castedState === VideoState.PUBLISHED || castedState === VideoState.WAITING_FOR_LIVE || castedState === VideoState.LIVE_ENDED 27 return castedState === VideoState.PUBLISHED || castedState === VideoState.WAITING_FOR_LIVE || castedState === VideoState.LIVE_ENDED
27} 28}
diff --git a/server/lib/activitypub/collection.ts b/server/lib/activitypub/collection.ts
index f897141ea..a176cab51 100644
--- a/server/lib/activitypub/collection.ts
+++ b/server/lib/activitypub/collection.ts
@@ -3,6 +3,7 @@ import validator from 'validator'
3import { pageToStartAndCount } from '@server/helpers/core-utils' 3import { pageToStartAndCount } from '@server/helpers/core-utils'
4import { ACTIVITY_PUB } from '@server/initializers/constants' 4import { ACTIVITY_PUB } from '@server/initializers/constants'
5import { ResultList } from '@shared/models' 5import { ResultList } from '@shared/models'
6import { forceNumber } from '@shared/core-utils'
6 7
7type ActivityPubCollectionPaginationHandler = (start: number, count: number) => Bluebird<ResultList<any>> | Promise<ResultList<any>> 8type ActivityPubCollectionPaginationHandler = (start: number, count: number) => Bluebird<ResultList<any>> | Promise<ResultList<any>>
8 9
@@ -33,7 +34,7 @@ async function activityPubCollectionPagination (
33 let prev: string | undefined 34 let prev: string | undefined
34 35
35 // Assert page is a number 36 // Assert page is a number
36 page = parseInt(page, 10) 37 page = forceNumber(page)
37 38
38 // There are more results 39 // There are more results
39 if (result.total > page * size) { 40 if (result.total > page * size) {
diff --git a/server/middlewares/pagination.ts b/server/middlewares/pagination.ts
index 9812af9e4..17e43f743 100644
--- a/server/middlewares/pagination.ts
+++ b/server/middlewares/pagination.ts
@@ -1,12 +1,13 @@
1import express from 'express' 1import express from 'express'
2import { forceNumber } from '@shared/core-utils'
2import { PAGINATION } from '../initializers/constants' 3import { PAGINATION } from '../initializers/constants'
3 4
4function setDefaultPagination (req: express.Request, res: express.Response, next: express.NextFunction) { 5function setDefaultPagination (req: express.Request, res: express.Response, next: express.NextFunction) {
5 if (!req.query.start) req.query.start = 0 6 if (!req.query.start) req.query.start = 0
6 else req.query.start = parseInt(req.query.start, 10) 7 else req.query.start = forceNumber(req.query.start)
7 8
8 if (!req.query.count) req.query.count = PAGINATION.GLOBAL.COUNT.DEFAULT 9 if (!req.query.count) req.query.count = PAGINATION.GLOBAL.COUNT.DEFAULT
9 else req.query.count = parseInt(req.query.count, 10) 10 else req.query.count = forceNumber(req.query.count)
10 11
11 return next() 12 return next()
12} 13}
diff --git a/server/middlewares/validators/abuse.ts b/server/middlewares/validators/abuse.ts
index 9b94008ce..70bae1775 100644
--- a/server/middlewares/validators/abuse.ts
+++ b/server/middlewares/validators/abuse.ts
@@ -18,6 +18,7 @@ import { AbuseMessageModel } from '@server/models/abuse/abuse-message'
18import { AbuseCreate, UserRight } from '@shared/models' 18import { AbuseCreate, UserRight } from '@shared/models'
19import { HttpStatusCode } from '../../../shared/models/http/http-error-codes' 19import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
20import { areValidationErrors, doesAbuseExist, doesAccountIdExist, doesCommentIdExist, doesVideoExist } from './shared' 20import { areValidationErrors, doesAbuseExist, doesAccountIdExist, doesCommentIdExist, doesVideoExist } from './shared'
21import { forceNumber } from '@shared/core-utils'
21 22
22const abuseReportValidator = [ 23const abuseReportValidator = [
23 body('account.id') 24 body('account.id')
@@ -216,7 +217,7 @@ const deleteAbuseMessageValidator = [
216 const user = res.locals.oauth.token.user 217 const user = res.locals.oauth.token.user
217 const abuse = res.locals.abuse 218 const abuse = res.locals.abuse
218 219
219 const messageId = parseInt(req.params.messageId + '', 10) 220 const messageId = forceNumber(req.params.messageId)
220 const abuseMessage = await AbuseMessageModel.loadByIdAndAbuseId(messageId, abuse.id) 221 const abuseMessage = await AbuseMessageModel.loadByIdAndAbuseId(messageId, abuse.id)
221 222
222 if (!abuseMessage) { 223 if (!abuseMessage) {
diff --git a/server/middlewares/validators/redundancy.ts b/server/middlewares/validators/redundancy.ts
index 79460f63c..c80f9b728 100644
--- a/server/middlewares/validators/redundancy.ts
+++ b/server/middlewares/validators/redundancy.ts
@@ -1,6 +1,7 @@
1import express from 'express' 1import express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { isVideoRedundancyTarget } from '@server/helpers/custom-validators/video-redundancies' 3import { isVideoRedundancyTarget } from '@server/helpers/custom-validators/video-redundancies'
4import { forceNumber } from '@shared/core-utils'
4import { HttpStatusCode } from '../../../shared/models/http/http-error-codes' 5import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
5import { 6import {
6 exists, 7 exists,
@@ -171,7 +172,7 @@ const removeVideoRedundancyValidator = [
171 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 172 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
172 if (areValidationErrors(req, res)) return 173 if (areValidationErrors(req, res)) return
173 174
174 const redundancy = await VideoRedundancyModel.loadByIdWithVideo(parseInt(req.params.redundancyId, 10)) 175 const redundancy = await VideoRedundancyModel.loadByIdWithVideo(forceNumber(req.params.redundancyId))
175 if (!redundancy) { 176 if (!redundancy) {
176 return res.fail({ 177 return res.fail({
177 status: HttpStatusCode.NOT_FOUND_404, 178 status: HttpStatusCode.NOT_FOUND_404,
diff --git a/server/middlewares/validators/shared/abuses.ts b/server/middlewares/validators/shared/abuses.ts
index 2b8d86ba5..2c988f9ec 100644
--- a/server/middlewares/validators/shared/abuses.ts
+++ b/server/middlewares/validators/shared/abuses.ts
@@ -1,9 +1,10 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import { AbuseModel } from '@server/models/abuse/abuse' 2import { AbuseModel } from '@server/models/abuse/abuse'
3import { HttpStatusCode } from '@shared/models' 3import { HttpStatusCode } from '@shared/models'
4import { forceNumber } from '@shared/core-utils'
4 5
5async function doesAbuseExist (abuseId: number | string, res: Response) { 6async function doesAbuseExist (abuseId: number | string, res: Response) {
6 const abuse = await AbuseModel.loadByIdWithReporter(parseInt(abuseId + '', 10)) 7 const abuse = await AbuseModel.loadByIdWithReporter(forceNumber(abuseId))
7 8
8 if (!abuse) { 9 if (!abuse) {
9 res.fail({ 10 res.fail({
diff --git a/server/middlewares/validators/shared/accounts.ts b/server/middlewares/validators/shared/accounts.ts
index fe4f83aa0..72b0e235e 100644
--- a/server/middlewares/validators/shared/accounts.ts
+++ b/server/middlewares/validators/shared/accounts.ts
@@ -2,10 +2,11 @@ import { Response } from 'express'
2import { AccountModel } from '@server/models/account/account' 2import { AccountModel } from '@server/models/account/account'
3import { UserModel } from '@server/models/user/user' 3import { UserModel } from '@server/models/user/user'
4import { MAccountDefault } from '@server/types/models' 4import { MAccountDefault } from '@server/types/models'
5import { forceNumber } from '@shared/core-utils'
5import { HttpStatusCode } from '@shared/models' 6import { HttpStatusCode } from '@shared/models'
6 7
7function doesAccountIdExist (id: number | string, res: Response, sendNotFound = true) { 8function doesAccountIdExist (id: number | string, res: Response, sendNotFound = true) {
8 const promise = AccountModel.load(parseInt(id + '', 10)) 9 const promise = AccountModel.load(forceNumber(id))
9 10
10 return doesAccountExist(promise, res, sendNotFound) 11 return doesAccountExist(promise, res, sendNotFound)
11} 12}
@@ -40,7 +41,7 @@ async function doesAccountExist (p: Promise<MAccountDefault>, res: Response, sen
40} 41}
41 42
42async function doesUserFeedTokenCorrespond (id: number, token: string, res: Response) { 43async function doesUserFeedTokenCorrespond (id: number, token: string, res: Response) {
43 const user = await UserModel.loadByIdWithChannels(parseInt(id + '', 10)) 44 const user = await UserModel.loadByIdWithChannels(forceNumber(id))
44 45
45 if (token !== user.feedToken) { 46 if (token !== user.feedToken) {
46 res.fail({ 47 res.fail({
diff --git a/server/middlewares/validators/shared/users.ts b/server/middlewares/validators/shared/users.ts
index fbaa7db0e..b8f1436d3 100644
--- a/server/middlewares/validators/shared/users.ts
+++ b/server/middlewares/validators/shared/users.ts
@@ -2,10 +2,11 @@ import express from 'express'
2import { ActorModel } from '@server/models/actor/actor' 2import { ActorModel } from '@server/models/actor/actor'
3import { UserModel } from '@server/models/user/user' 3import { UserModel } from '@server/models/user/user'
4import { MUserDefault } from '@server/types/models' 4import { MUserDefault } from '@server/types/models'
5import { forceNumber } from '@shared/core-utils'
5import { HttpStatusCode } from '@shared/models' 6import { HttpStatusCode } from '@shared/models'
6 7
7function checkUserIdExist (idArg: number | string, res: express.Response, withStats = false) { 8function checkUserIdExist (idArg: number | string, res: express.Response, withStats = false) {
8 const id = parseInt(idArg + '', 10) 9 const id = forceNumber(idArg)
9 return checkUserExist(() => UserModel.loadByIdWithChannels(id, withStats), res) 10 return checkUserExist(() => UserModel.loadByIdWithChannels(id, withStats), res)
10} 11}
11 12
diff --git a/server/middlewares/validators/shared/video-comments.ts b/server/middlewares/validators/shared/video-comments.ts
index 8d1a16294..0961b3ec9 100644
--- a/server/middlewares/validators/shared/video-comments.ts
+++ b/server/middlewares/validators/shared/video-comments.ts
@@ -1,10 +1,11 @@
1import express from 'express' 1import express from 'express'
2import { VideoCommentModel } from '@server/models/video/video-comment' 2import { VideoCommentModel } from '@server/models/video/video-comment'
3import { MVideoId } from '@server/types/models' 3import { MVideoId } from '@server/types/models'
4import { forceNumber } from '@shared/core-utils'
4import { HttpStatusCode, ServerErrorCode } from '@shared/models' 5import { HttpStatusCode, ServerErrorCode } from '@shared/models'
5 6
6async function doesVideoCommentThreadExist (idArg: number | string, video: MVideoId, res: express.Response) { 7async function doesVideoCommentThreadExist (idArg: number | string, video: MVideoId, res: express.Response) {
7 const id = parseInt(idArg + '', 10) 8 const id = forceNumber(idArg)
8 const videoComment = await VideoCommentModel.loadById(id) 9 const videoComment = await VideoCommentModel.loadById(id)
9 10
10 if (!videoComment) { 11 if (!videoComment) {
@@ -33,7 +34,7 @@ async function doesVideoCommentThreadExist (idArg: number | string, video: MVide
33} 34}
34 35
35async function doesVideoCommentExist (idArg: number | string, video: MVideoId, res: express.Response) { 36async function doesVideoCommentExist (idArg: number | string, video: MVideoId, res: express.Response) {
36 const id = parseInt(idArg + '', 10) 37 const id = forceNumber(idArg)
37 const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id) 38 const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id)
38 39
39 if (!videoComment) { 40 if (!videoComment) {
@@ -57,7 +58,7 @@ async function doesVideoCommentExist (idArg: number | string, video: MVideoId, r
57} 58}
58 59
59async function doesCommentIdExist (idArg: number | string, res: express.Response) { 60async function doesCommentIdExist (idArg: number | string, res: express.Response) {
60 const id = parseInt(idArg + '', 10) 61 const id = forceNumber(idArg)
61 const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id) 62 const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id)
62 63
63 if (!videoComment) { 64 if (!videoComment) {
diff --git a/server/middlewares/validators/shared/video-ownerships.ts b/server/middlewares/validators/shared/video-ownerships.ts
index 680613cda..33ac9c8b6 100644
--- a/server/middlewares/validators/shared/video-ownerships.ts
+++ b/server/middlewares/validators/shared/video-ownerships.ts
@@ -1,9 +1,10 @@
1import express from 'express' 1import express from 'express'
2import { VideoChangeOwnershipModel } from '@server/models/video/video-change-ownership' 2import { VideoChangeOwnershipModel } from '@server/models/video/video-change-ownership'
3import { forceNumber } from '@shared/core-utils'
3import { HttpStatusCode } from '@shared/models' 4import { HttpStatusCode } from '@shared/models'
4 5
5async function doesChangeVideoOwnershipExist (idArg: number | string, res: express.Response) { 6async function doesChangeVideoOwnershipExist (idArg: number | string, res: express.Response) {
6 const id = parseInt(idArg + '', 10) 7 const id = forceNumber(idArg)
7 const videoChangeOwnership = await VideoChangeOwnershipModel.load(id) 8 const videoChangeOwnership = await VideoChangeOwnershipModel.load(id)
8 9
9 if (!videoChangeOwnership) { 10 if (!videoChangeOwnership) {
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index 055af3b64..50327b6ae 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -1,6 +1,7 @@
1import express from 'express' 1import express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { Hooks } from '@server/lib/plugins/hooks' 3import { Hooks } from '@server/lib/plugins/hooks'
4import { forceNumber } from '@shared/core-utils'
4import { HttpStatusCode, UserRegister, UserRight, UserRole } from '@shared/models' 5import { HttpStatusCode, UserRegister, UserRight, UserRole } from '@shared/models'
5import { exists, isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' 6import { exists, isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
6import { isThemeNameValid } from '../../helpers/custom-validators/plugins' 7import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
@@ -515,7 +516,7 @@ const usersCheckCurrentPasswordFactory = (targetUserIdGetter: (req: express.Requ
515 516
516 const user = res.locals.oauth.token.User 517 const user = res.locals.oauth.token.User
517 const isAdminOrModerator = user.role === UserRole.ADMINISTRATOR || user.role === UserRole.MODERATOR 518 const isAdminOrModerator = user.role === UserRole.ADMINISTRATOR || user.role === UserRole.MODERATOR
518 const targetUserId = parseInt(targetUserIdGetter(req) + '') 519 const targetUserId = forceNumber(targetUserIdGetter(req))
519 520
520 // Admin/moderator action on another user, skip the password check 521 // Admin/moderator action on another user, skip the password check
521 if (isAdminOrModerator && targetUserId !== user.id) { 522 if (isAdminOrModerator && targetUserId !== user.id) {
diff --git a/server/middlewares/validators/videos/video-imports.ts b/server/middlewares/validators/videos/video-imports.ts
index f295b1885..72442aeb6 100644
--- a/server/middlewares/validators/videos/video-imports.ts
+++ b/server/middlewares/validators/videos/video-imports.ts
@@ -4,6 +4,7 @@ import { isResolvingToUnicastOnly } from '@server/helpers/dns'
4import { isPreImportVideoAccepted } from '@server/lib/moderation' 4import { isPreImportVideoAccepted } from '@server/lib/moderation'
5import { Hooks } from '@server/lib/plugins/hooks' 5import { Hooks } from '@server/lib/plugins/hooks'
6import { MUserAccountId, MVideoImport } from '@server/types/models' 6import { MUserAccountId, MVideoImport } from '@server/types/models'
7import { forceNumber } from '@shared/core-utils'
7import { HttpStatusCode, UserRight, VideoImportState } from '@shared/models' 8import { HttpStatusCode, UserRight, VideoImportState } from '@shared/models'
8import { VideoImportCreate } from '@shared/models/videos/import/video-import-create.model' 9import { VideoImportCreate } from '@shared/models/videos/import/video-import-create.model'
9import { isIdValid, toIntOrNull } from '../../../helpers/custom-validators/misc' 10import { isIdValid, toIntOrNull } from '../../../helpers/custom-validators/misc'
@@ -130,7 +131,7 @@ const videoImportCancelValidator = [
130 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 131 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
131 if (areValidationErrors(req, res)) return 132 if (areValidationErrors(req, res)) return
132 133
133 if (!await doesVideoImportExist(parseInt(req.params.id), res)) return 134 if (!await doesVideoImportExist(forceNumber(req.params.id), res)) return
134 if (!checkUserCanManageImport(res.locals.oauth.token.user, res.locals.videoImport, res)) return 135 if (!checkUserCanManageImport(res.locals.oauth.token.user, res.locals.videoImport, res)) return
135 136
136 if (res.locals.videoImport.state !== VideoImportState.PENDING) { 137 if (res.locals.videoImport.state !== VideoImportState.PENDING) {
diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts
index 6d4b8a6f1..e4b7e5c56 100644
--- a/server/middlewares/validators/videos/video-playlists.ts
+++ b/server/middlewares/validators/videos/video-playlists.ts
@@ -2,6 +2,7 @@ import express from 'express'
2import { body, param, query, ValidationChain } from 'express-validator' 2import { body, param, query, ValidationChain } from 'express-validator'
3import { ExpressPromiseHandler } from '@server/types/express-handler' 3import { ExpressPromiseHandler } from '@server/types/express-handler'
4import { MUserAccountId } from '@server/types/models' 4import { MUserAccountId } from '@server/types/models'
5import { forceNumber } from '@shared/core-utils'
5import { 6import {
6 HttpStatusCode, 7 HttpStatusCode,
7 UserRight, 8 UserRight,
@@ -258,7 +259,7 @@ const videoPlaylistElementAPGetValidator = [
258 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 259 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
259 if (areValidationErrors(req, res)) return 260 if (areValidationErrors(req, res)) return
260 261
261 const playlistElementId = parseInt(req.params.playlistElementId + '', 10) 262 const playlistElementId = forceNumber(req.params.playlistElementId)
262 const playlistId = req.params.playlistId 263 const playlistId = req.params.playlistId
263 264
264 const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndElementIdForAP(playlistId, playlistElementId) 265 const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndElementIdForAP(playlistId, playlistElementId)
diff --git a/server/models/abuse/abuse-query-builder.ts b/server/models/abuse/abuse-query-builder.ts
index cfc924ba4..74f4542e5 100644
--- a/server/models/abuse/abuse-query-builder.ts
+++ b/server/models/abuse/abuse-query-builder.ts
@@ -1,5 +1,6 @@
1 1
2import { exists } from '@server/helpers/custom-validators/misc' 2import { exists } from '@server/helpers/custom-validators/misc'
3import { forceNumber } from '@shared/core-utils'
3import { AbuseFilter, AbuseState, AbuseVideoIs } from '@shared/models' 4import { AbuseFilter, AbuseState, AbuseVideoIs } from '@shared/models'
4import { buildBlockedAccountSQL, buildDirectionAndField } from '../utils' 5import { buildBlockedAccountSQL, buildDirectionAndField } from '../utils'
5 6
@@ -135,12 +136,12 @@ function buildAbuseListQuery (options: BuildAbusesQueryOptions, type: 'count' |
135 } 136 }
136 137
137 if (exists(options.count)) { 138 if (exists(options.count)) {
138 const count = parseInt(options.count + '', 10) 139 const count = forceNumber(options.count)
139 suffix += `LIMIT ${count} ` 140 suffix += `LIMIT ${count} `
140 } 141 }
141 142
142 if (exists(options.start)) { 143 if (exists(options.start)) {
143 const start = parseInt(options.start + '', 10) 144 const start = forceNumber(options.start)
144 suffix += `OFFSET ${start} ` 145 suffix += `OFFSET ${start} `
145 } 146 }
146 } 147 }
diff --git a/server/models/actor/actor.ts b/server/models/actor/actor.ts
index 88db241dc..d7afa727d 100644
--- a/server/models/actor/actor.ts
+++ b/server/models/actor/actor.ts
@@ -18,7 +18,7 @@ import {
18import { activityPubContextify } from '@server/lib/activitypub/context' 18import { activityPubContextify } from '@server/lib/activitypub/context'
19import { getBiggestActorImage } from '@server/lib/actor-image' 19import { getBiggestActorImage } from '@server/lib/actor-image'
20import { ModelCache } from '@server/models/model-cache' 20import { ModelCache } from '@server/models/model-cache'
21import { getLowercaseExtension } from '@shared/core-utils' 21import { forceNumber, getLowercaseExtension } from '@shared/core-utils'
22import { ActivityIconObject, ActivityPubActorType, ActorImageType } from '@shared/models' 22import { ActivityIconObject, ActivityPubActorType, ActorImageType } from '@shared/models'
23import { AttributesOnly } from '@shared/typescript-utils' 23import { AttributesOnly } from '@shared/typescript-utils'
24import { 24import {
@@ -446,7 +446,7 @@ export class ActorModel extends Model<Partial<AttributesOnly<ActorModel>>> {
446 } 446 }
447 447
448 static rebuildFollowsCount (ofId: number, type: 'followers' | 'following', transaction?: Transaction) { 448 static rebuildFollowsCount (ofId: number, type: 'followers' | 'following', transaction?: Transaction) {
449 const sanitizedOfId = parseInt(ofId + '', 10) 449 const sanitizedOfId = forceNumber(ofId)
450 const where = { id: sanitizedOfId } 450 const where = { id: sanitizedOfId }
451 451
452 let columnToUpdate: string 452 let columnToUpdate: string
diff --git a/server/models/user/user-notification.ts b/server/models/user/user-notification.ts
index 6209cb4bf..d37fa5dc7 100644
--- a/server/models/user/user-notification.ts
+++ b/server/models/user/user-notification.ts
@@ -2,6 +2,7 @@ import { ModelIndexesOptions, Op, WhereOptions } from 'sequelize'
2import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
3import { getBiggestActorImage } from '@server/lib/actor-image' 3import { getBiggestActorImage } from '@server/lib/actor-image'
4import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user' 4import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user'
5import { forceNumber } from '@shared/core-utils'
5import { uuidToShort } from '@shared/extra-utils' 6import { uuidToShort } from '@shared/extra-utils'
6import { UserNotification, UserNotificationType } from '@shared/models' 7import { UserNotification, UserNotificationType } from '@shared/models'
7import { AttributesOnly } from '@shared/typescript-utils' 8import { AttributesOnly } from '@shared/typescript-utils'
@@ -284,7 +285,7 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
284 } 285 }
285 286
286 static removeNotificationsOf (options: { id: number, type: 'account' | 'server', forUserId?: number }) { 287 static removeNotificationsOf (options: { id: number, type: 'account' | 'server', forUserId?: number }) {
287 const id = parseInt(options.id + '', 10) 288 const id = forceNumber(options.id)
288 289
289 function buildAccountWhereQuery (base: string) { 290 function buildAccountWhereQuery (base: string) {
290 const whereSuffix = options.forUserId 291 const whereSuffix = options.forUserId
diff --git a/server/models/user/user.ts b/server/models/user/user.ts
index f70feed73..672728a2a 100644
--- a/server/models/user/user.ts
+++ b/server/models/user/user.ts
@@ -70,6 +70,7 @@ import { VideoImportModel } from '../video/video-import'
70import { VideoLiveModel } from '../video/video-live' 70import { VideoLiveModel } from '../video/video-live'
71import { VideoPlaylistModel } from '../video/video-playlist' 71import { VideoPlaylistModel } from '../video/video-playlist'
72import { UserNotificationSettingModel } from './user-notification-setting' 72import { UserNotificationSettingModel } from './user-notification-setting'
73import { forceNumber } from '@shared/core-utils'
73 74
74enum ScopeNames { 75enum ScopeNames {
75 FOR_ME_API = 'FOR_ME_API', 76 FOR_ME_API = 'FOR_ME_API',
@@ -900,27 +901,27 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
900 videoQuotaDaily: this.videoQuotaDaily, 901 videoQuotaDaily: this.videoQuotaDaily,
901 902
902 videoQuotaUsed: videoQuotaUsed !== undefined 903 videoQuotaUsed: videoQuotaUsed !== undefined
903 ? parseInt(videoQuotaUsed + '', 10) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id) 904 ? forceNumber(videoQuotaUsed) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
904 : undefined, 905 : undefined,
905 906
906 videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined 907 videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined
907 ? parseInt(videoQuotaUsedDaily + '', 10) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id) 908 ? forceNumber(videoQuotaUsedDaily) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
908 : undefined, 909 : undefined,
909 910
910 videosCount: videosCount !== undefined 911 videosCount: videosCount !== undefined
911 ? parseInt(videosCount + '', 10) 912 ? forceNumber(videosCount)
912 : undefined, 913 : undefined,
913 abusesCount: abusesCount 914 abusesCount: abusesCount
914 ? parseInt(abusesCount, 10) 915 ? forceNumber(abusesCount)
915 : undefined, 916 : undefined,
916 abusesAcceptedCount: abusesAcceptedCount 917 abusesAcceptedCount: abusesAcceptedCount
917 ? parseInt(abusesAcceptedCount, 10) 918 ? forceNumber(abusesAcceptedCount)
918 : undefined, 919 : undefined,
919 abusesCreatedCount: abusesCreatedCount !== undefined 920 abusesCreatedCount: abusesCreatedCount !== undefined
920 ? parseInt(abusesCreatedCount + '', 10) 921 ? forceNumber(abusesCreatedCount)
921 : undefined, 922 : undefined,
922 videoCommentsCount: videoCommentsCount !== undefined 923 videoCommentsCount: videoCommentsCount !== undefined
923 ? parseInt(videoCommentsCount + '', 10) 924 ? forceNumber(videoCommentsCount)
924 : undefined, 925 : undefined,
925 926
926 noInstanceConfigWarningModal: this.noInstanceConfigWarningModal, 927 noInstanceConfigWarningModal: this.noInstanceConfigWarningModal,
diff --git a/server/models/utils.ts b/server/models/utils.ts
index 1e168d419..3476799ce 100644
--- a/server/models/utils.ts
+++ b/server/models/utils.ts
@@ -1,5 +1,6 @@
1import { literal, Op, OrderItem, Sequelize } from 'sequelize' 1import { literal, Op, OrderItem, Sequelize } from 'sequelize'
2import validator from 'validator' 2import validator from 'validator'
3import { forceNumber } from '@shared/core-utils'
3 4
4type SortType = { sortModel: string, sortValue: string } 5type SortType = { sortModel: string, sortValue: string }
5 6
@@ -202,7 +203,7 @@ function buildBlockedAccountSQLOptimized (columnNameJoin: string, blockerIds: nu
202} 203}
203 204
204function buildServerIdsFollowedBy (actorId: any) { 205function buildServerIdsFollowedBy (actorId: any) {
205 const actorIdNumber = parseInt(actorId + '', 10) 206 const actorIdNumber = forceNumber(actorId)
206 207
207 return '(' + 208 return '(' +
208 'SELECT "actor"."serverId" FROM "actorFollow" ' + 209 'SELECT "actor"."serverId" FROM "actorFollow" ' +
@@ -218,7 +219,7 @@ function buildWhereIdOrUUID (id: number | string) {
218function parseAggregateResult (result: any) { 219function parseAggregateResult (result: any) {
219 if (!result) return 0 220 if (!result) return 0
220 221
221 const total = parseInt(result + '', 10) 222 const total = forceNumber(result)
222 if (isNaN(total)) return 0 223 if (isNaN(total)) return 0
223 224
224 return total 225 return total
diff --git a/server/models/video/sql/video/videos-id-list-query-builder.ts b/server/models/video/sql/video/videos-id-list-query-builder.ts
index 14f903851..7c864bf27 100644
--- a/server/models/video/sql/video/videos-id-list-query-builder.ts
+++ b/server/models/video/sql/video/videos-id-list-query-builder.ts
@@ -6,6 +6,7 @@ import { buildDirectionAndField, createSafeIn, parseRowCountResult } from '@serv
6import { MUserAccountId, MUserId } from '@server/types/models' 6import { MUserAccountId, MUserId } from '@server/types/models'
7import { VideoInclude, VideoPrivacy, VideoState } from '@shared/models' 7import { VideoInclude, VideoPrivacy, VideoState } from '@shared/models'
8import { AbstractRunQuery } from '../../../shared/abstract-run-query' 8import { AbstractRunQuery } from '../../../shared/abstract-run-query'
9import { forceNumber } from '@shared/core-utils'
9 10
10/** 11/**
11 * 12 *
@@ -689,12 +690,12 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery {
689 } 690 }
690 691
691 private setLimit (countArg: number) { 692 private setLimit (countArg: number) {
692 const count = parseInt(countArg + '', 10) 693 const count = forceNumber(countArg)
693 this.limit = `LIMIT ${count}` 694 this.limit = `LIMIT ${count}`
694 } 695 }
695 696
696 private setOffset (startArg: number) { 697 private setOffset (startArg: number) {
697 const start = parseInt(startArg + '', 10) 698 const start = forceNumber(startArg)
698 this.offset = `OFFSET ${start}` 699 this.offset = `OFFSET ${start}`
699 } 700 }
700} 701}
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 91dafbcf1..9e461b6ca 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -19,7 +19,7 @@ import {
19} from 'sequelize-typescript' 19} from 'sequelize-typescript'
20import { CONFIG } from '@server/initializers/config' 20import { CONFIG } from '@server/initializers/config'
21import { MAccountActor } from '@server/types/models' 21import { MAccountActor } from '@server/types/models'
22import { pick } from '@shared/core-utils' 22import { forceNumber, pick } from '@shared/core-utils'
23import { AttributesOnly } from '@shared/typescript-utils' 23import { AttributesOnly } from '@shared/typescript-utils'
24import { ActivityPubActor } from '../../../shared/models/activitypub' 24import { ActivityPubActor } from '../../../shared/models/activitypub'
25import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos' 25import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos'
@@ -280,7 +280,7 @@ export type SummaryOptions = {
280 ] 280 ]
281 }, 281 },
282 [ScopeNames.WITH_STATS]: (options: AvailableWithStatsOptions = { daysPrior: 30 }) => { 282 [ScopeNames.WITH_STATS]: (options: AvailableWithStatsOptions = { daysPrior: 30 }) => {
283 const daysPrior = parseInt(options.daysPrior + '', 10) 283 const daysPrior = forceNumber(options.daysPrior)
284 284
285 return { 285 return {
286 attributes: { 286 attributes: {
diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts
index b45f15bd6..7181b5599 100644
--- a/server/models/video/video-playlist-element.ts
+++ b/server/models/video/video-playlist-element.ts
@@ -23,6 +23,7 @@ import {
23 MVideoPlaylistElementVideoUrlPlaylistPrivacy, 23 MVideoPlaylistElementVideoUrlPlaylistPrivacy,
24 MVideoPlaylistVideoThumbnail 24 MVideoPlaylistVideoThumbnail
25} from '@server/types/models/video/video-playlist-element' 25} from '@server/types/models/video/video-playlist-element'
26import { forceNumber } from '@shared/core-utils'
26import { AttributesOnly } from '@shared/typescript-utils' 27import { AttributesOnly } from '@shared/typescript-utils'
27import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object' 28import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object'
28import { VideoPrivacy } from '../../../shared/models/videos' 29import { VideoPrivacy } from '../../../shared/models/videos'
@@ -185,7 +186,9 @@ export class VideoPlaylistElementModel extends Model<Partial<AttributesOnly<Vide
185 playlistId: number | string, 186 playlistId: number | string,
186 playlistElementId: number 187 playlistElementId: number
187 ): Promise<MVideoPlaylistElementVideoUrlPlaylistPrivacy> { 188 ): Promise<MVideoPlaylistElementVideoUrlPlaylistPrivacy> {
188 const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId } 189 const playlistWhere = validator.isUUID('' + playlistId)
190 ? { uuid: playlistId }
191 : { id: playlistId }
189 192
190 const query = { 193 const query = {
191 include: [ 194 include: [
@@ -262,13 +265,15 @@ export class VideoPlaylistElementModel extends Model<Partial<AttributesOnly<Vide
262 .then(position => position ? position + 1 : 1) 265 .then(position => position ? position + 1 : 1)
263 } 266 }
264 267
265 static reassignPositionOf ( 268 static reassignPositionOf (options: {
266 videoPlaylistId: number, 269 videoPlaylistId: number
267 firstPosition: number, 270 firstPosition: number
268 endPosition: number, 271 endPosition: number
269 newPosition: number, 272 newPosition: number
270 transaction?: Transaction 273 transaction?: Transaction
271 ) { 274 }) {
275 const { videoPlaylistId, firstPosition, endPosition, newPosition, transaction } = options
276
272 const query = { 277 const query = {
273 where: { 278 where: {
274 videoPlaylistId, 279 videoPlaylistId,
@@ -281,7 +286,7 @@ export class VideoPlaylistElementModel extends Model<Partial<AttributesOnly<Vide
281 validate: false // We use a literal to update the position 286 validate: false // We use a literal to update the position
282 } 287 }
283 288
284 const positionQuery = Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`) 289 const positionQuery = Sequelize.literal(`${forceNumber(newPosition)} + "position" - ${forceNumber(firstPosition)}`)
285 return VideoPlaylistElementModel.update({ position: positionQuery }, query) 290 return VideoPlaylistElementModel.update({ position: positionQuery }, query)
286 } 291 }
287 292
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts
index ca63bb2d9..f2190037e 100644
--- a/server/models/video/video-share.ts
+++ b/server/models/video/video-share.ts
@@ -1,5 +1,6 @@
1import { literal, Op, QueryTypes, Transaction } from 'sequelize' 1import { literal, Op, QueryTypes, Transaction } from 'sequelize'
2import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
3import { forceNumber } from '@shared/core-utils'
3import { AttributesOnly } from '@shared/typescript-utils' 4import { AttributesOnly } from '@shared/typescript-utils'
4import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 5import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
5import { CONSTRAINTS_FIELDS } from '../../initializers/constants' 6import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
@@ -123,7 +124,7 @@ export class VideoShareModel extends Model<Partial<AttributesOnly<VideoShareMode
123 } 124 }
124 125
125 static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Promise<MActorDefault[]> { 126 static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Promise<MActorDefault[]> {
126 const safeOwnerId = parseInt(actorOwnerId + '', 10) 127 const safeOwnerId = forceNumber(actorOwnerId)
127 128
128 // /!\ On actor model 129 // /!\ On actor model
129 const query = { 130 const query = {
@@ -148,7 +149,7 @@ export class VideoShareModel extends Model<Partial<AttributesOnly<VideoShareMode
148 } 149 }
149 150
150 static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Promise<MActorDefault[]> { 151 static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Promise<MActorDefault[]> {
151 const safeChannelId = parseInt(videoChannelId + '', 10) 152 const safeChannelId = forceNumber(videoChannelId)
152 153
153 // /!\ On actor model 154 // /!\ On actor model
154 const query = { 155 const query = {
diff --git a/server/tools/peertube-redundancy.ts b/server/tools/peertube-redundancy.ts
index 4bb9fbc5a..5c82fa420 100644
--- a/server/tools/peertube-redundancy.ts
+++ b/server/tools/peertube-redundancy.ts
@@ -2,7 +2,7 @@ import CliTable3 from 'cli-table3'
2import { Command, program } from 'commander' 2import { Command, program } from 'commander'
3import { URL } from 'url' 3import { URL } from 'url'
4import validator from 'validator' 4import validator from 'validator'
5import { uniqify } from '@shared/core-utils' 5import { forceNumber, uniqify } from '@shared/core-utils'
6import { HttpStatusCode, VideoRedundanciesTarget } from '@shared/models' 6import { HttpStatusCode, VideoRedundanciesTarget } from '@shared/models'
7import { assignToken, buildServer, getServerCredentials } from './cli' 7import { assignToken, buildServer, getServerCredentials } from './cli'
8 8
@@ -138,7 +138,7 @@ async function removeRedundancyCLI (options: { video: number }, command: Command
138 process.exit(-1) 138 process.exit(-1)
139 } 139 }
140 140
141 const videoId = parseInt(options.video + '', 10) 141 const videoId = forceNumber(options.video)
142 142
143 const myVideoRedundancies = await server.redundancy.listVideos({ target: 'my-videos' }) 143 const myVideoRedundancies = await server.redundancy.listVideos({ target: 'my-videos' })
144 let videoRedundancy = myVideoRedundancies.data.find(r => videoId === r.id) 144 let videoRedundancy = myVideoRedundancies.data.find(r => videoId === r.id)