Don't inject untrusted input

Even if it's already checked in middlewares
It's better to have safe modals too
This commit is contained in:
Chocobozzz 2022-11-15 14:41:55 +01:00
parent 6bcb559fc9
commit 4638cd713d
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
35 changed files with 101 additions and 63 deletions

View File

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

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)
}

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)

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)

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

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)
}

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)

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[]) {

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
}

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) {

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()
}

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) {

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,

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({

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({

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)
}

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) {

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) {

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) {

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) {

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)

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} `
}
}

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

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

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,

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

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}`
}
}

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: {

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)
}

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 = {

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)

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'

View File

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

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'])
}
}
}

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) {