From 4638cd713dcdd007cd7f49b9a95fa62ac7823e7c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 15 Nov 2022 14:41:55 +0100 Subject: Don't inject untrusted input Even if it's already checked in middlewares It's better to have safe modals too --- server/models/abuse/abuse-query-builder.ts | 5 +++-- server/models/actor/actor.ts | 4 ++-- server/models/user/user-notification.ts | 3 ++- server/models/user/user.ts | 15 ++++++++------- server/models/utils.ts | 5 +++-- .../video/sql/video/videos-id-list-query-builder.ts | 5 +++-- server/models/video/video-channel.ts | 4 ++-- server/models/video/video-playlist-element.ts | 21 +++++++++++++-------- server/models/video/video-share.ts | 5 +++-- 9 files changed, 39 insertions(+), 28 deletions(-) (limited to 'server/models') 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 @@ 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} ` } } 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 { 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>> { } 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 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' 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>> { 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, 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 @@ 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 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 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}` } } 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 { } 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: { 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 { 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 { - 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 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 { - const safeOwnerId = parseInt(actorOwnerId + '', 10) + const safeOwnerId = forceNumber(actorOwnerId) // /!\ On actor model const query = { @@ -148,7 +149,7 @@ export class VideoShareModel extends Model { - const safeChannelId = parseInt(videoChannelId + '', 10) + const safeChannelId = forceNumber(videoChannelId) // /!\ On actor model const query = { -- cgit v1.2.3