Browse Source

Don't inject untrusted input

Even if it's already checked in middlewares
It's better to have safe modals too
develop
Chocobozzz 1 week ago
parent
commit
4638cd713d
No known key found for this signature in database GPG Key ID: 583A612D890159BE
35 changed files with 101 additions and 63 deletions
  1. +0
    -2
      client/src/assets/player/shared/manager-options/manager-options-builder.ts
  2. +2
    -1
      server/controllers/api/users/my-history.ts
  3. +3
    -2
      server/controllers/api/users/my-video-playlists.ts
  4. +3
    -2
      server/controllers/api/video-playlist.ts
  5. +2
    -1
      server/controllers/api/videos/update.ts
  6. +2
    -2
      server/controllers/download.ts
  7. +3
    -2
      server/controllers/services.ts
  8. +2
    -1
      server/helpers/custom-validators/video-studio.ts
  9. +3
    -2
      server/helpers/video.ts
  10. +2
    -1
      server/lib/activitypub/collection.ts
  11. +3
    -2
      server/middlewares/pagination.ts
  12. +2
    -1
      server/middlewares/validators/abuse.ts
  13. +2
    -1
      server/middlewares/validators/redundancy.ts
  14. +2
    -1
      server/middlewares/validators/shared/abuses.ts
  15. +3
    -2
      server/middlewares/validators/shared/accounts.ts
  16. +2
    -1
      server/middlewares/validators/shared/users.ts
  17. +4
    -3
      server/middlewares/validators/shared/video-comments.ts
  18. +2
    -1
      server/middlewares/validators/shared/video-ownerships.ts
  19. +2
    -1
      server/middlewares/validators/users.ts
  20. +2
    -1
      server/middlewares/validators/videos/video-imports.ts
  21. +2
    -1
      server/middlewares/validators/videos/video-playlists.ts
  22. +3
    -2
      server/models/abuse/abuse-query-builder.ts
  23. +2
    -2
      server/models/actor/actor.ts
  24. +2
    -1
      server/models/user/user-notification.ts
  25. +8
    -7
      server/models/user/user.ts
  26. +3
    -2
      server/models/utils.ts
  27. +3
    -2
      server/models/video/sql/video/videos-id-list-query-builder.ts
  28. +2
    -2
      server/models/video/video-channel.ts
  29. +13
    -8
      server/models/video/video-playlist-element.ts
  30. +3
    -2
      server/models/video/video-share.ts
  31. +2
    -2
      server/tools/peertube-redundancy.ts
  32. +1
    -0
      shared/core-utils/common/index.ts
  33. +7
    -0
      shared/core-utils/common/number.ts
  34. +2
    -1
      shared/extra-utils/ffprobe.ts
  35. +2
    -1
      shared/server-commands/miscs/sql-command.ts

+ 0
- 2
client/src/assets/player/shared/manager-options/manager-options-builder.ts View File

@@ -105,8 +105,6 @@ export class ManagerOptionsBuilder {
Object.assign(videojsOptions, { language: commonOptions.language })
}

console.log(videojsOptions)

return videojsOptions
}



+ 2
- 1
server/controllers/api/users/my-history.ts View File

@@ -1,3 +1,4 @@
import { forceNumber } from '@shared/core-utils'
import express from 'express'
import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
import { getFormattedObjects } from '../../../helpers/utils'
@@ -55,7 +56,7 @@ async function listMyVideosHistory (req: express.Request, res: express.Response)
async function removeUserHistoryElement (req: express.Request, res: express.Response) {
const user = res.locals.oauth.token.User

await UserVideoHistoryModel.removeUserHistoryElement(user, parseInt(req.params.videoId + ''))
await UserVideoHistoryModel.removeUserHistoryElement(user, forceNumber(req.params.videoId))

return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
}


+ 3
- 2
server/controllers/api/users/my-video-playlists.ts View File

@@ -1,5 +1,6 @@
import { uuidToShort } from '@shared/extra-utils'
import express from 'express'
import { forceNumber } from '@shared/core-utils'
import { uuidToShort } from '@shared/extra-utils'
import { VideosExistInPlaylists } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
import { asyncMiddleware, authenticate } from '../../../middlewares'
import { doVideosInPlaylistExistValidator } from '../../../middlewares/validators/videos/video-playlists'
@@ -22,7 +23,7 @@ export {
// ---------------------------------------------------------------------------

async function doVideosInPlaylistExist (req: express.Request, res: express.Response) {
const videoIds = req.query.videoIds.map(i => parseInt(i + '', 10))
const videoIds = req.query.videoIds.map(i => forceNumber(i))
const user = res.locals.oauth.token.User

const results = await VideoPlaylistModel.listPlaylistSummariesOf(user.Account.id, videoIds)


+ 3
- 2
server/controllers/api/video-playlist.ts View File

@@ -46,6 +46,7 @@ import {
import { AccountModel } from '../../models/account/account'
import { VideoPlaylistModel } from '../../models/video/video-playlist'
import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'
import { forceNumber } from '@shared/core-utils'

const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT)

@@ -245,7 +246,7 @@ async function updateVideoPlaylist (req: express.Request, res: express.Response)
if (videoPlaylistInfoToUpdate.description !== undefined) videoPlaylistInstance.description = videoPlaylistInfoToUpdate.description

if (videoPlaylistInfoToUpdate.privacy !== undefined) {
videoPlaylistInstance.privacy = parseInt(videoPlaylistInfoToUpdate.privacy.toString(), 10)
videoPlaylistInstance.privacy = forceNumber(videoPlaylistInfoToUpdate.privacy)

if (wasNotPrivatePlaylist === true && videoPlaylistInstance.privacy === VideoPlaylistPrivacy.PRIVATE) {
await sendDeleteVideoPlaylist(videoPlaylistInstance, t)
@@ -424,7 +425,7 @@ async function reorderVideosPlaylist (req: express.Request, res: express.Respons

const endOldPosition = oldPosition + reorderLength - 1
// Insert our reordered elements in their place (update)
await VideoPlaylistElementModel.reassignPositionOf(videoPlaylist.id, oldPosition, endOldPosition, newPosition, t)
await VideoPlaylistElementModel.reassignPositionOf({ videoPlaylistId: videoPlaylist.id, firstPosition: oldPosition, endPosition: endOldPosition, newPosition, transaction: t })

// Decrease positions of elements after the old position of our ordered elements (decrease)
await VideoPlaylistElementModel.increasePositionOf(videoPlaylist.id, oldPosition, -reorderLength, t)


+ 2
- 1
server/controllers/api/videos/update.ts View File

@@ -19,6 +19,7 @@ import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosU
import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update'
import { VideoModel } from '../../../models/video/video'
import { VideoPathManager } from '@server/lib/video-path-manager'
import { forceNumber } from '@shared/core-utils'

const lTags = loggerTagsFactory('api', 'video')
const auditLogger = auditLoggerFactory('videos')
@@ -174,7 +175,7 @@ async function updateVideoPrivacy (options: {
const { videoInstance, videoInfoToUpdate, hadPrivacyForFederation, transaction } = options
const isNewVideo = videoInstance.isNewVideo(videoInfoToUpdate.privacy)

const newPrivacy = parseInt(videoInfoToUpdate.privacy.toString(), 10)
const newPrivacy = forceNumber(videoInfoToUpdate.privacy)
setVideoPrivacy(videoInstance, newPrivacy)

// Unfederate the video if the new privacy is not compatible with federation


+ 2
- 2
server/controllers/download.ts View File

@@ -5,7 +5,7 @@ import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache
import { Hooks } from '@server/lib/plugins/hooks'
import { VideoPathManager } from '@server/lib/video-path-manager'
import { MStreamingPlaylist, MVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
import { addQueryParams } from '@shared/core-utils'
import { addQueryParams, forceNumber } from '@shared/core-utils'
import { HttpStatusCode, VideoStorage, VideoStreamingPlaylistType } from '@shared/models'
import { STATIC_DOWNLOAD_PATHS } from '../initializers/constants'
import { asyncMiddleware, optionalAuthenticate, videosDownloadValidator } from '../middlewares'
@@ -132,7 +132,7 @@ async function downloadHLSVideoFile (req: express.Request, res: express.Response
}

function getVideoFile (req: express.Request, files: MVideoFile[]) {
const resolution = parseInt(req.params.resolution, 10)
const resolution = forceNumber(req.params.resolution)
return files.find(f => f.resolution === resolution)
}



+ 3
- 2
server/controllers/services.ts View File

@@ -4,6 +4,7 @@ import { escapeHTML } from '@shared/core-utils/renderer'
import { EMBED_SIZE, PREVIEWS_SIZE, THUMBNAILS_SIZE, WEBSERVER } from '../initializers/constants'
import { asyncMiddleware, oembedValidator } from '../middlewares'
import { accountNameWithHostGetValidator } from '../middlewares/validators'
import { forceNumber } from '@shared/core-utils'

const servicesRouter = express.Router()

@@ -108,8 +109,8 @@ function buildOEmbed (options: {
const { req, previewSize, previewPath, title, channel, embedPath } = options

const webserverUrl = WEBSERVER.URL
const maxHeight = parseInt(req.query.maxheight, 10)
const maxWidth = parseInt(req.query.maxwidth, 10)
const maxHeight = forceNumber(req.query.maxheight)
const maxWidth = forceNumber(req.query.maxwidth)

const embedUrl = webserverUrl + embedPath
const embedTitle = escapeHTML(title)


+ 2
- 1
server/helpers/custom-validators/video-studio.ts View File

@@ -4,6 +4,7 @@ import { buildTaskFileFieldname } from '@server/lib/video-studio'
import { VideoStudioTask } from '@shared/models'
import { isArray } from './misc'
import { isVideoFileMimeTypeValid, isVideoImageValid } from './videos'
import { forceNumber } from '@shared/core-utils'

function isValidStudioTasksArray (tasks: any) {
if (!isArray(tasks)) return false
@@ -24,7 +25,7 @@ function isStudioCutTaskValid (task: VideoStudioTask) {

if (!start || !end) return true

return parseInt(start + '') < parseInt(end + '')
return forceNumber(start) < forceNumber(end)
}

function isStudioTaskAddIntroOutroValid (task: VideoStudioTask, indice: number, files: Express.Multer.File[]) {


+ 3
- 2
server/helpers/video.ts View File

@@ -2,6 +2,7 @@ import { Response } from 'express'
import { CONFIG } from '@server/initializers/config'
import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo } from '@server/types/models'
import { VideoPrivacy, VideoState } from '@shared/models'
import { forceNumber } from '@shared/core-utils'

function getVideoWithAttributes (res: Response) {
return res.locals.videoAPI || res.locals.videoAll || res.locals.onlyVideo
@@ -14,14 +15,14 @@ function extractVideo (videoOrPlaylist: MVideo | MStreamingPlaylistVideo) {
}

function isPrivacyForFederation (privacy: VideoPrivacy) {
const castedPrivacy = parseInt(privacy + '', 10)
const castedPrivacy = forceNumber(privacy)

return castedPrivacy === VideoPrivacy.PUBLIC ||
(CONFIG.FEDERATION.VIDEOS.FEDERATE_UNLISTED === true && castedPrivacy === VideoPrivacy.UNLISTED)
}

function isStateForFederation (state: VideoState) {
const castedState = parseInt(state + '', 10)
const castedState = forceNumber(state)

return castedState === VideoState.PUBLISHED || castedState === VideoState.WAITING_FOR_LIVE || castedState === VideoState.LIVE_ENDED
}


+ 2
- 1
server/lib/activitypub/collection.ts View File

@@ -3,6 +3,7 @@ import validator from 'validator'
import { pageToStartAndCount } from '@server/helpers/core-utils'
import { ACTIVITY_PUB } from '@server/initializers/constants'
import { ResultList } from '@shared/models'
import { forceNumber } from '@shared/core-utils'

type ActivityPubCollectionPaginationHandler = (start: number, count: number) => Bluebird<ResultList<any>> | Promise<ResultList<any>>

@@ -33,7 +34,7 @@ async function activityPubCollectionPagination (
let prev: string | undefined

// Assert page is a number
page = parseInt(page, 10)
page = forceNumber(page)

// There are more results
if (result.total > page * size) {


+ 3
- 2
server/middlewares/pagination.ts View File

@@ -1,12 +1,13 @@
import express from 'express'
import { forceNumber } from '@shared/core-utils'
import { PAGINATION } from '../initializers/constants'

function setDefaultPagination (req: express.Request, res: express.Response, next: express.NextFunction) {
if (!req.query.start) req.query.start = 0
else req.query.start = parseInt(req.query.start, 10)
else req.query.start = forceNumber(req.query.start)

if (!req.query.count) req.query.count = PAGINATION.GLOBAL.COUNT.DEFAULT
else req.query.count = parseInt(req.query.count, 10)
else req.query.count = forceNumber(req.query.count)

return next()
}


+ 2
- 1
server/middlewares/validators/abuse.ts View File

@@ -18,6 +18,7 @@ import { AbuseMessageModel } from '@server/models/abuse/abuse-message'
import { AbuseCreate, UserRight } from '@shared/models'
import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
import { areValidationErrors, doesAbuseExist, doesAccountIdExist, doesCommentIdExist, doesVideoExist } from './shared'
import { forceNumber } from '@shared/core-utils'

const abuseReportValidator = [
body('account.id')
@@ -216,7 +217,7 @@ const deleteAbuseMessageValidator = [
const user = res.locals.oauth.token.user
const abuse = res.locals.abuse

const messageId = parseInt(req.params.messageId + '', 10)
const messageId = forceNumber(req.params.messageId)
const abuseMessage = await AbuseMessageModel.loadByIdAndAbuseId(messageId, abuse.id)

if (!abuseMessage) {


+ 2
- 1
server/middlewares/validators/redundancy.ts View File

@@ -1,6 +1,7 @@
import express from 'express'
import { body, param, query } from 'express-validator'
import { isVideoRedundancyTarget } from '@server/helpers/custom-validators/video-redundancies'
import { forceNumber } from '@shared/core-utils'
import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
import {
exists,
@@ -171,7 +172,7 @@ const removeVideoRedundancyValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return

const redundancy = await VideoRedundancyModel.loadByIdWithVideo(parseInt(req.params.redundancyId, 10))
const redundancy = await VideoRedundancyModel.loadByIdWithVideo(forceNumber(req.params.redundancyId))
if (!redundancy) {
return res.fail({
status: HttpStatusCode.NOT_FOUND_404,


+ 2
- 1
server/middlewares/validators/shared/abuses.ts View File

@@ -1,9 +1,10 @@
import { Response } from 'express'
import { AbuseModel } from '@server/models/abuse/abuse'
import { HttpStatusCode } from '@shared/models'
import { forceNumber } from '@shared/core-utils'

async function doesAbuseExist (abuseId: number | string, res: Response) {
const abuse = await AbuseModel.loadByIdWithReporter(parseInt(abuseId + '', 10))
const abuse = await AbuseModel.loadByIdWithReporter(forceNumber(abuseId))

if (!abuse) {
res.fail({


+ 3
- 2
server/middlewares/validators/shared/accounts.ts View File

@@ -2,10 +2,11 @@ import { Response } from 'express'
import { AccountModel } from '@server/models/account/account'
import { UserModel } from '@server/models/user/user'
import { MAccountDefault } from '@server/types/models'
import { forceNumber } from '@shared/core-utils'
import { HttpStatusCode } from '@shared/models'

function doesAccountIdExist (id: number | string, res: Response, sendNotFound = true) {
const promise = AccountModel.load(parseInt(id + '', 10))
const promise = AccountModel.load(forceNumber(id))

return doesAccountExist(promise, res, sendNotFound)
}
@@ -40,7 +41,7 @@ async function doesAccountExist (p: Promise<MAccountDefault>, res: Response, sen
}

async function doesUserFeedTokenCorrespond (id: number, token: string, res: Response) {
const user = await UserModel.loadByIdWithChannels(parseInt(id + '', 10))
const user = await UserModel.loadByIdWithChannels(forceNumber(id))

if (token !== user.feedToken) {
res.fail({


+ 2
- 1
server/middlewares/validators/shared/users.ts View File

@@ -2,10 +2,11 @@ import express from 'express'
import { ActorModel } from '@server/models/actor/actor'
import { UserModel } from '@server/models/user/user'
import { MUserDefault } from '@server/types/models'
import { forceNumber } from '@shared/core-utils'
import { HttpStatusCode } from '@shared/models'

function checkUserIdExist (idArg: number | string, res: express.Response, withStats = false) {
const id = parseInt(idArg + '', 10)
const id = forceNumber(idArg)
return checkUserExist(() => UserModel.loadByIdWithChannels(id, withStats), res)
}



+ 4
- 3
server/middlewares/validators/shared/video-comments.ts View File

@@ -1,10 +1,11 @@
import express from 'express'
import { VideoCommentModel } from '@server/models/video/video-comment'
import { MVideoId } from '@server/types/models'
import { forceNumber } from '@shared/core-utils'
import { HttpStatusCode, ServerErrorCode } from '@shared/models'

async function doesVideoCommentThreadExist (idArg: number | string, video: MVideoId, res: express.Response) {
const id = parseInt(idArg + '', 10)
const id = forceNumber(idArg)
const videoComment = await VideoCommentModel.loadById(id)

if (!videoComment) {
@@ -33,7 +34,7 @@ async function doesVideoCommentThreadExist (idArg: number | string, video: MVide
}

async function doesVideoCommentExist (idArg: number | string, video: MVideoId, res: express.Response) {
const id = parseInt(idArg + '', 10)
const id = forceNumber(idArg)
const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id)

if (!videoComment) {
@@ -57,7 +58,7 @@ async function doesVideoCommentExist (idArg: number | string, video: MVideoId, r
}

async function doesCommentIdExist (idArg: number | string, res: express.Response) {
const id = parseInt(idArg + '', 10)
const id = forceNumber(idArg)
const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id)

if (!videoComment) {


+ 2
- 1
server/middlewares/validators/shared/video-ownerships.ts View File

@@ -1,9 +1,10 @@
import express from 'express'
import { VideoChangeOwnershipModel } from '@server/models/video/video-change-ownership'
import { forceNumber } from '@shared/core-utils'
import { HttpStatusCode } from '@shared/models'

async function doesChangeVideoOwnershipExist (idArg: number | string, res: express.Response) {
const id = parseInt(idArg + '', 10)
const id = forceNumber(idArg)
const videoChangeOwnership = await VideoChangeOwnershipModel.load(id)

if (!videoChangeOwnership) {


+ 2
- 1
server/middlewares/validators/users.ts View File

@@ -1,6 +1,7 @@
import express from 'express'
import { body, param, query } from 'express-validator'
import { Hooks } from '@server/lib/plugins/hooks'
import { forceNumber } from '@shared/core-utils'
import { HttpStatusCode, UserRegister, UserRight, UserRole } from '@shared/models'
import { exists, isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
@@ -515,7 +516,7 @@ const usersCheckCurrentPasswordFactory = (targetUserIdGetter: (req: express.Requ

const user = res.locals.oauth.token.User
const isAdminOrModerator = user.role === UserRole.ADMINISTRATOR || user.role === UserRole.MODERATOR
const targetUserId = parseInt(targetUserIdGetter(req) + '')
const targetUserId = forceNumber(targetUserIdGetter(req))

// Admin/moderator action on another user, skip the password check
if (isAdminOrModerator && targetUserId !== user.id) {


+ 2
- 1
server/middlewares/validators/videos/video-imports.ts View File

@@ -4,6 +4,7 @@ import { isResolvingToUnicastOnly } from '@server/helpers/dns'
import { isPreImportVideoAccepted } from '@server/lib/moderation'
import { Hooks } from '@server/lib/plugins/hooks'
import { MUserAccountId, MVideoImport } from '@server/types/models'
import { forceNumber } from '@shared/core-utils'
import { HttpStatusCode, UserRight, VideoImportState } from '@shared/models'
import { VideoImportCreate } from '@shared/models/videos/import/video-import-create.model'
import { isIdValid, toIntOrNull } from '../../../helpers/custom-validators/misc'
@@ -130,7 +131,7 @@ const videoImportCancelValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return

if (!await doesVideoImportExist(parseInt(req.params.id), res)) return
if (!await doesVideoImportExist(forceNumber(req.params.id), res)) return
if (!checkUserCanManageImport(res.locals.oauth.token.user, res.locals.videoImport, res)) return

if (res.locals.videoImport.state !== VideoImportState.PENDING) {


+ 2
- 1
server/middlewares/validators/videos/video-playlists.ts View File

@@ -2,6 +2,7 @@ import express from 'express'
import { body, param, query, ValidationChain } from 'express-validator'
import { ExpressPromiseHandler } from '@server/types/express-handler'
import { MUserAccountId } from '@server/types/models'
import { forceNumber } from '@shared/core-utils'
import {
HttpStatusCode,
UserRight,
@@ -258,7 +259,7 @@ const videoPlaylistElementAPGetValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return

const playlistElementId = parseInt(req.params.playlistElementId + '', 10)
const playlistElementId = forceNumber(req.params.playlistElementId)
const playlistId = req.params.playlistId

const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndElementIdForAP(playlistId, playlistElementId)


+ 3
- 2
server/models/abuse/abuse-query-builder.ts View File

@@ -1,5 +1,6 @@

import { exists } from '@server/helpers/custom-validators/misc'
import { forceNumber } from '@shared/core-utils'
import { AbuseFilter, AbuseState, AbuseVideoIs } from '@shared/models'
import { buildBlockedAccountSQL, buildDirectionAndField } from '../utils'

@@ -135,12 +136,12 @@ function buildAbuseListQuery (options: BuildAbusesQueryOptions, type: 'count' |
}

if (exists(options.count)) {
const count = parseInt(options.count + '', 10)
const count = forceNumber(options.count)
suffix += `LIMIT ${count} `
}

if (exists(options.start)) {
const start = parseInt(options.start + '', 10)
const start = forceNumber(options.start)
suffix += `OFFSET ${start} `
}
}


+ 2
- 2
server/models/actor/actor.ts View File

@@ -18,7 +18,7 @@ import {
import { activityPubContextify } from '@server/lib/activitypub/context'
import { getBiggestActorImage } from '@server/lib/actor-image'
import { ModelCache } from '@server/models/model-cache'
import { getLowercaseExtension } from '@shared/core-utils'
import { forceNumber, getLowercaseExtension } from '@shared/core-utils'
import { ActivityIconObject, ActivityPubActorType, ActorImageType } from '@shared/models'
import { AttributesOnly } from '@shared/typescript-utils'
import {
@@ -446,7 +446,7 @@ export class ActorModel extends Model<Partial<AttributesOnly<ActorModel>>> {
}

static rebuildFollowsCount (ofId: number, type: 'followers' | 'following', transaction?: Transaction) {
const sanitizedOfId = parseInt(ofId + '', 10)
const sanitizedOfId = forceNumber(ofId)
const where = { id: sanitizedOfId }

let columnToUpdate: string


+ 2
- 1
server/models/user/user-notification.ts View File

@@ -2,6 +2,7 @@ import { ModelIndexesOptions, Op, WhereOptions } from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { getBiggestActorImage } from '@server/lib/actor-image'
import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user'
import { forceNumber } from '@shared/core-utils'
import { uuidToShort } from '@shared/extra-utils'
import { UserNotification, UserNotificationType } from '@shared/models'
import { AttributesOnly } from '@shared/typescript-utils'
@@ -284,7 +285,7 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
}

static removeNotificationsOf (options: { id: number, type: 'account' | 'server', forUserId?: number }) {
const id = parseInt(options.id + '', 10)
const id = forceNumber(options.id)

function buildAccountWhereQuery (base: string) {
const whereSuffix = options.forUserId


+ 8
- 7
server/models/user/user.ts View File

@@ -70,6 +70,7 @@ import { VideoImportModel } from '../video/video-import'
import { VideoLiveModel } from '../video/video-live'
import { VideoPlaylistModel } from '../video/video-playlist'
import { UserNotificationSettingModel } from './user-notification-setting'
import { forceNumber } from '@shared/core-utils'

enum ScopeNames {
FOR_ME_API = 'FOR_ME_API',
@@ -900,27 +901,27 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
videoQuotaDaily: this.videoQuotaDaily,

videoQuotaUsed: videoQuotaUsed !== undefined
? parseInt(videoQuotaUsed + '', 10) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
? forceNumber(videoQuotaUsed) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
: undefined,

videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined
? parseInt(videoQuotaUsedDaily + '', 10) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
? forceNumber(videoQuotaUsedDaily) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
: undefined,

videosCount: videosCount !== undefined
? parseInt(videosCount + '', 10)
? forceNumber(videosCount)
: undefined,
abusesCount: abusesCount
? parseInt(abusesCount, 10)
? forceNumber(abusesCount)
: undefined,
abusesAcceptedCount: abusesAcceptedCount
? parseInt(abusesAcceptedCount, 10)
? forceNumber(abusesAcceptedCount)
: undefined,
abusesCreatedCount: abusesCreatedCount !== undefined
? parseInt(abusesCreatedCount + '', 10)
? forceNumber(abusesCreatedCount)
: undefined,
videoCommentsCount: videoCommentsCount !== undefined
? parseInt(videoCommentsCount + '', 10)
? forceNumber(videoCommentsCount)
: undefined,

noInstanceConfigWarningModal: this.noInstanceConfigWarningModal,


+ 3
- 2
server/models/utils.ts View File

@@ -1,5 +1,6 @@
import { literal, Op, OrderItem, Sequelize } from 'sequelize'
import validator from 'validator'
import { forceNumber } from '@shared/core-utils'

type SortType = { sortModel: string, sortValue: string }

@@ -202,7 +203,7 @@ function buildBlockedAccountSQLOptimized (columnNameJoin: string, blockerIds: nu
}

function buildServerIdsFollowedBy (actorId: any) {
const actorIdNumber = parseInt(actorId + '', 10)
const actorIdNumber = forceNumber(actorId)

return '(' +
'SELECT "actor"."serverId" FROM "actorFollow" ' +
@@ -218,7 +219,7 @@ function buildWhereIdOrUUID (id: number | string) {
function parseAggregateResult (result: any) {
if (!result) return 0

const total = parseInt(result + '', 10)
const total = forceNumber(result)
if (isNaN(total)) return 0

return total


+ 3
- 2
server/models/video/sql/video/videos-id-list-query-builder.ts View File

@@ -6,6 +6,7 @@ import { buildDirectionAndField, createSafeIn, parseRowCountResult } from '@serv
import { MUserAccountId, MUserId } from '@server/types/models'
import { VideoInclude, VideoPrivacy, VideoState } from '@shared/models'
import { AbstractRunQuery } from '../../../shared/abstract-run-query'
import { forceNumber } from '@shared/core-utils'

/**
*
@@ -689,12 +690,12 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery {
}

private setLimit (countArg: number) {
const count = parseInt(countArg + '', 10)
const count = forceNumber(countArg)
this.limit = `LIMIT ${count}`
}

private setOffset (startArg: number) {
const start = parseInt(startArg + '', 10)
const start = forceNumber(startArg)
this.offset = `OFFSET ${start}`
}
}

+ 2
- 2
server/models/video/video-channel.ts View File

@@ -19,7 +19,7 @@ import {
} from 'sequelize-typescript'
import { CONFIG } from '@server/initializers/config'
import { MAccountActor } from '@server/types/models'
import { pick } from '@shared/core-utils'
import { forceNumber, pick } from '@shared/core-utils'
import { AttributesOnly } from '@shared/typescript-utils'
import { ActivityPubActor } from '../../../shared/models/activitypub'
import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos'
@@ -280,7 +280,7 @@ export type SummaryOptions = {
]
},
[ScopeNames.WITH_STATS]: (options: AvailableWithStatsOptions = { daysPrior: 30 }) => {
const daysPrior = parseInt(options.daysPrior + '', 10)
const daysPrior = forceNumber(options.daysPrior)

return {
attributes: {


+ 13
- 8
server/models/video/video-playlist-element.ts View File

@@ -23,6 +23,7 @@ import {
MVideoPlaylistElementVideoUrlPlaylistPrivacy,
MVideoPlaylistVideoThumbnail
} from '@server/types/models/video/video-playlist-element'
import { forceNumber } from '@shared/core-utils'
import { AttributesOnly } from '@shared/typescript-utils'
import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object'
import { VideoPrivacy } from '../../../shared/models/videos'
@@ -185,7 +186,9 @@ export class VideoPlaylistElementModel extends Model<Partial<AttributesOnly<Vide
playlistId: number | string,
playlistElementId: number
): Promise<MVideoPlaylistElementVideoUrlPlaylistPrivacy> {
const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId }
const playlistWhere = validator.isUUID('' + playlistId)
? { uuid: playlistId }
: { id: playlistId }

const query = {
include: [
@@ -262,13 +265,15 @@ export class VideoPlaylistElementModel extends Model<Partial<AttributesOnly<Vide
.then(position => position ? position + 1 : 1)
}

static reassignPositionOf (
videoPlaylistId: number,
firstPosition: number,
endPosition: number,
newPosition: number,
static reassignPositionOf (options: {
videoPlaylistId: number
firstPosition: number
endPosition: number
newPosition: number
transaction?: Transaction
) {
}) {
const { videoPlaylistId, firstPosition, endPosition, newPosition, transaction } = options

const query = {
where: {
videoPlaylistId,
@@ -281,7 +286,7 @@ export class VideoPlaylistElementModel extends Model<Partial<AttributesOnly<Vide
validate: false // We use a literal to update the position
}

const positionQuery = Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`)
const positionQuery = Sequelize.literal(`${forceNumber(newPosition)} + "position" - ${forceNumber(firstPosition)}`)
return VideoPlaylistElementModel.update({ position: positionQuery }, query)
}



+ 3
- 2
server/models/video/video-share.ts View File

@@ -1,5 +1,6 @@
import { literal, Op, QueryTypes, Transaction } from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { forceNumber } from '@shared/core-utils'
import { AttributesOnly } from '@shared/typescript-utils'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
@@ -123,7 +124,7 @@ export class VideoShareModel extends Model<Partial<AttributesOnly<VideoShareMode
}

static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Promise<MActorDefault[]> {
const safeOwnerId = parseInt(actorOwnerId + '', 10)
const safeOwnerId = forceNumber(actorOwnerId)

// /!\ On actor model
const query = {
@@ -148,7 +149,7 @@ export class VideoShareModel extends Model<Partial<AttributesOnly<VideoShareMode
}

static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Promise<MActorDefault[]> {
const safeChannelId = parseInt(videoChannelId + '', 10)
const safeChannelId = forceNumber(videoChannelId)

// /!\ On actor model
const query = {


+ 2
- 2
server/tools/peertube-redundancy.ts View File

@@ -2,7 +2,7 @@ import CliTable3 from 'cli-table3'
import { Command, program } from 'commander'
import { URL } from 'url'
import validator from 'validator'
import { uniqify } from '@shared/core-utils'
import { forceNumber, uniqify } from '@shared/core-utils'
import { HttpStatusCode, VideoRedundanciesTarget } from '@shared/models'
import { assignToken, buildServer, getServerCredentials } from './cli'

@@ -138,7 +138,7 @@ async function removeRedundancyCLI (options: { video: number }, command: Command
process.exit(-1)
}

const videoId = parseInt(options.video + '', 10)
const videoId = forceNumber(options.video)

const myVideoRedundancies = await server.redundancy.listVideos({ target: 'my-videos' })
let videoRedundancy = myVideoRedundancies.data.find(r => videoId === r.id)


+ 1
- 0
shared/core-utils/common/index.ts View File

@@ -2,6 +2,7 @@ export * from './array'
export * from './random'
export * from './date'
export * from './env'
export * from './number'
export * from './object'
export * from './path'
export * from './regexp'


+ 7
- 0
shared/core-utils/common/number.ts View File

@@ -0,0 +1,7 @@
function forceNumber (value: any) {
return parseInt(value + '')
}

export {
forceNumber
}

+ 2
- 1
shared/extra-utils/ffprobe.ts View File

@@ -1,4 +1,5 @@
import { ffprobe, FfprobeData } from 'fluent-ffmpeg'
import { forceNumber } from '@shared/core-utils'
import { VideoFileMetadata, VideoResolution } from '@shared/models/videos'

/**
@@ -55,7 +56,7 @@ async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) {
return {
absolutePath: data.format.filename,
audioStream,
bitrate: parseInt(audioStream['bit_rate'] + '', 10)
bitrate: forceNumber(audioStream['bit_rate'])
}
}
}


+ 2
- 1
shared/server-commands/miscs/sql-command.ts View File

@@ -1,4 +1,5 @@
import { QueryTypes, Sequelize } from 'sequelize'
import { forceNumber } from '@shared/core-utils'
import { AbstractCommand } from '../shared'

export class SQLCommand extends AbstractCommand {
@@ -63,7 +64,7 @@ export class SQLCommand extends AbstractCommand {

if (!total) return 0

return parseInt(total + '', 10)
return forceNumber(total)
}

getActorImage (filename: string) {


Loading…
Cancel
Save