From 2198bb5a1981177b04dd94b2b1b6a90c5d7a5c25 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 14 Nov 2022 14:21:40 +0100 Subject: Prevent XSS with ng-select When using ng-option See https://github.com/ng-select/ng-select/issues/1363 --- .../src/app/+admin/system/jobs/jobs.component.ts | 6 ++++- .../select/select-channel.component.ts | 6 +++-- .../video-filters-header.component.html | 1 + .../shared-video-miniature/video-filters.model.ts | 26 +++++++++++++++------- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/client/src/app/+admin/system/jobs/jobs.component.ts b/client/src/app/+admin/system/jobs/jobs.component.ts index d5da1b743..b8f3c3a68 100644 --- a/client/src/app/+admin/system/jobs/jobs.component.ts +++ b/client/src/app/+admin/system/jobs/jobs.component.ts @@ -2,6 +2,7 @@ import { SortMeta } from 'primeng/api' import { Component, OnInit } from '@angular/core' import { Notifier, RestPagination, RestTable } from '@app/core' import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' +import { escapeHTML } from '@shared/core-utils/renderer' import { Job, JobState, JobType } from '@shared/models' import { JobStateClient } from '../../../../types/job-state-client.type' import { JobTypeClient } from '../../../../types/job-type-client.type' @@ -142,7 +143,10 @@ export class JobsComponent extends RestTable implements OnInit { private loadJobStateAndType () { const state = peertubeLocalStorage.getItem(JobsComponent.LOCAL_STORAGE_STATE) - if (state) this.jobState = state as JobState + + // FIXME: We use that doesn't escape HTML + // https://github.com/ng-select/ng-select/issues/1363 + if (state) this.jobState = escapeHTML(state) as JobState const type = peertubeLocalStorage.getItem(JobsComponent.LOCAL_STORAGE_TYPE) if (type) this.jobType = type as JobType diff --git a/client/src/app/shared/shared-forms/select/select-channel.component.ts b/client/src/app/shared/shared-forms/select/select-channel.component.ts index 5fcae0050..26d6216ef 100644 --- a/client/src/app/shared/shared-forms/select/select-channel.component.ts +++ b/client/src/app/shared/shared-forms/select/select-channel.component.ts @@ -39,8 +39,10 @@ export class SelectChannelComponent implements ControlValueAccessor, OnChanges { propagateChange = (_: any) => { /* empty */ } - writeValue (id: number) { - this.selectedId = id + writeValue (id: number | string) { + this.selectedId = typeof id === 'string' + ? parseInt(id, 10) + : id } registerOnChange (fn: (_: any) => void) { diff --git a/client/src/app/shared/shared-video-miniature/video-filters-header.component.html b/client/src/app/shared/shared-video-miniature/video-filters-header.component.html index fe7a59bdb..9ddfd7dda 100644 --- a/client/src/app/shared/shared-video-miniature/video-filters-header.component.html +++ b/client/src/app/shared/shared-video-miniature/video-filters-header.component.html @@ -42,6 +42,7 @@ formControlName="sort" [clearable]="false" [searchable]="false" + [bindValue]="null" > Sort by "Recently Added" Sort by "Original Publication Date" diff --git a/client/src/app/shared/shared-video-miniature/video-filters.model.ts b/client/src/app/shared/shared-video-miniature/video-filters.model.ts index 73a30ca08..4069ab4b5 100644 --- a/client/src/app/shared/shared-video-miniature/video-filters.model.ts +++ b/client/src/app/shared/shared-video-miniature/video-filters.model.ts @@ -1,7 +1,8 @@ import { splitIntoArray, toBoolean } from '@app/helpers' import { getAllPrivacies } from '@shared/core-utils' -import { AttributesOnly } from '@shared/typescript-utils' +import { escapeHTML } from '@shared/core-utils/renderer' import { BooleanBothQuery, NSFWPolicyType, VideoInclude, VideoPrivacy, VideoSortField } from '@shared/models' +import { AttributesOnly } from '@shared/typescript-utils' type VideoFiltersKeys = { [ id in keyof AttributesOnly ]: any @@ -90,19 +91,28 @@ export class VideoFilters { } load (obj: Partial>) { - if (obj.sort !== undefined) this.sort = obj.sort + // FIXME: We may use that doesn't escape HTML so prefer to escape things + // https://github.com/ng-select/ng-select/issues/1363 + + const escapeIfNeeded = (value: any) => { + if (typeof value === 'string') return escapeHTML(value) + + return value + } + + if (obj.sort !== undefined) this.sort = escapeIfNeeded(obj.sort) as VideoSortField - if (obj.nsfw !== undefined) this.nsfw = obj.nsfw + if (obj.nsfw !== undefined) this.nsfw = escapeIfNeeded(obj.nsfw) as BooleanBothQuery - if (obj.languageOneOf !== undefined) this.languageOneOf = splitIntoArray(obj.languageOneOf) - if (obj.categoryOneOf !== undefined) this.categoryOneOf = splitIntoArray(obj.categoryOneOf) + if (obj.languageOneOf !== undefined) this.languageOneOf = splitIntoArray(escapeIfNeeded(obj.languageOneOf)) + if (obj.categoryOneOf !== undefined) this.categoryOneOf = splitIntoArray(escapeIfNeeded(obj.categoryOneOf)) - if (obj.scope !== undefined) this.scope = obj.scope + if (obj.scope !== undefined) this.scope = escapeIfNeeded(obj.scope) as VideoFilterScope if (obj.allVideos !== undefined) this.allVideos = toBoolean(obj.allVideos) - if (obj.live !== undefined) this.live = obj.live + if (obj.live !== undefined) this.live = escapeIfNeeded(obj.live) as BooleanBothQuery - if (obj.search !== undefined) this.search = obj.search + if (obj.search !== undefined) this.search = escapeIfNeeded(obj.search) this.buildActiveFilters() } -- cgit v1.2.3