aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-11-14 14:21:40 +0100
committerChocobozzz <me@florianbigard.com>2022-11-14 14:21:40 +0100
commit2198bb5a1981177b04dd94b2b1b6a90c5d7a5c25 (patch)
tree668365a50351c573949e066c1be440683795e262
parentb1934b7e9cdece7c0c38e05b0f905dc2ccab9167 (diff)
downloadPeerTube-2198bb5a1981177b04dd94b2b1b6a90c5d7a5c25.tar.gz
PeerTube-2198bb5a1981177b04dd94b2b1b6a90c5d7a5c25.tar.zst
PeerTube-2198bb5a1981177b04dd94b2b1b6a90c5d7a5c25.zip
Prevent XSS with ng-select
When using ng-option See https://github.com/ng-select/ng-select/issues/1363
-rw-r--r--client/src/app/+admin/system/jobs/jobs.component.ts6
-rw-r--r--client/src/app/shared/shared-forms/select/select-channel.component.ts6
-rw-r--r--client/src/app/shared/shared-video-miniature/video-filters-header.component.html1
-rw-r--r--client/src/app/shared/shared-video-miniature/video-filters.model.ts26
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'
2import { Component, OnInit } from '@angular/core' 2import { Component, OnInit } from '@angular/core'
3import { Notifier, RestPagination, RestTable } from '@app/core' 3import { Notifier, RestPagination, RestTable } from '@app/core'
4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
5import { escapeHTML } from '@shared/core-utils/renderer'
5import { Job, JobState, JobType } from '@shared/models' 6import { Job, JobState, JobType } from '@shared/models'
6import { JobStateClient } from '../../../../types/job-state-client.type' 7import { JobStateClient } from '../../../../types/job-state-client.type'
7import { JobTypeClient } from '../../../../types/job-type-client.type' 8import { JobTypeClient } from '../../../../types/job-type-client.type'
@@ -142,7 +143,10 @@ export class JobsComponent extends RestTable implements OnInit {
142 143
143 private loadJobStateAndType () { 144 private loadJobStateAndType () {
144 const state = peertubeLocalStorage.getItem(JobsComponent.LOCAL_STORAGE_STATE) 145 const state = peertubeLocalStorage.getItem(JobsComponent.LOCAL_STORAGE_STATE)
145 if (state) this.jobState = state as JobState 146
147 // FIXME: We use <ng-option> that doesn't escape HTML
148 // https://github.com/ng-select/ng-select/issues/1363
149 if (state) this.jobState = escapeHTML(state) as JobState
146 150
147 const type = peertubeLocalStorage.getItem(JobsComponent.LOCAL_STORAGE_TYPE) 151 const type = peertubeLocalStorage.getItem(JobsComponent.LOCAL_STORAGE_TYPE)
148 if (type) this.jobType = type as JobType 152 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 {
39 39
40 propagateChange = (_: any) => { /* empty */ } 40 propagateChange = (_: any) => { /* empty */ }
41 41
42 writeValue (id: number) { 42 writeValue (id: number | string) {
43 this.selectedId = id 43 this.selectedId = typeof id === 'string'
44 ? parseInt(id, 10)
45 : id
44 } 46 }
45 47
46 registerOnChange (fn: (_: any) => void) { 48 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 @@
42 formControlName="sort" 42 formControlName="sort"
43 [clearable]="false" 43 [clearable]="false"
44 [searchable]="false" 44 [searchable]="false"
45 [bindValue]="null"
45 > 46 >
46 <ng-option i18n value="-publishedAt">Sort by <strong>"Recently Added"</strong></ng-option> 47 <ng-option i18n value="-publishedAt">Sort by <strong>"Recently Added"</strong></ng-option>
47 <ng-option i18n value="-originallyPublishedAt">Sort by <strong>"Original Publication Date"</strong></ng-option> 48 <ng-option i18n value="-originallyPublishedAt">Sort by <strong>"Original Publication Date"</strong></ng-option>
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 @@
1import { splitIntoArray, toBoolean } from '@app/helpers' 1import { splitIntoArray, toBoolean } from '@app/helpers'
2import { getAllPrivacies } from '@shared/core-utils' 2import { getAllPrivacies } from '@shared/core-utils'
3import { AttributesOnly } from '@shared/typescript-utils' 3import { escapeHTML } from '@shared/core-utils/renderer'
4import { BooleanBothQuery, NSFWPolicyType, VideoInclude, VideoPrivacy, VideoSortField } from '@shared/models' 4import { BooleanBothQuery, NSFWPolicyType, VideoInclude, VideoPrivacy, VideoSortField } from '@shared/models'
5import { AttributesOnly } from '@shared/typescript-utils'
5 6
6type VideoFiltersKeys = { 7type VideoFiltersKeys = {
7 [ id in keyof AttributesOnly<VideoFilters> ]: any 8 [ id in keyof AttributesOnly<VideoFilters> ]: any
@@ -90,19 +91,28 @@ export class VideoFilters {
90 } 91 }
91 92
92 load (obj: Partial<AttributesOnly<VideoFilters>>) { 93 load (obj: Partial<AttributesOnly<VideoFilters>>) {
93 if (obj.sort !== undefined) this.sort = obj.sort 94 // FIXME: We may use <ng-option> that doesn't escape HTML so prefer to escape things
95 // https://github.com/ng-select/ng-select/issues/1363
96
97 const escapeIfNeeded = (value: any) => {
98 if (typeof value === 'string') return escapeHTML(value)
99
100 return value
101 }
102
103 if (obj.sort !== undefined) this.sort = escapeIfNeeded(obj.sort) as VideoSortField
94 104
95 if (obj.nsfw !== undefined) this.nsfw = obj.nsfw 105 if (obj.nsfw !== undefined) this.nsfw = escapeIfNeeded(obj.nsfw) as BooleanBothQuery
96 106
97 if (obj.languageOneOf !== undefined) this.languageOneOf = splitIntoArray(obj.languageOneOf) 107 if (obj.languageOneOf !== undefined) this.languageOneOf = splitIntoArray(escapeIfNeeded(obj.languageOneOf))
98 if (obj.categoryOneOf !== undefined) this.categoryOneOf = splitIntoArray(obj.categoryOneOf) 108 if (obj.categoryOneOf !== undefined) this.categoryOneOf = splitIntoArray(escapeIfNeeded(obj.categoryOneOf))
99 109
100 if (obj.scope !== undefined) this.scope = obj.scope 110 if (obj.scope !== undefined) this.scope = escapeIfNeeded(obj.scope) as VideoFilterScope
101 if (obj.allVideos !== undefined) this.allVideos = toBoolean(obj.allVideos) 111 if (obj.allVideos !== undefined) this.allVideos = toBoolean(obj.allVideos)
102 112
103 if (obj.live !== undefined) this.live = obj.live 113 if (obj.live !== undefined) this.live = escapeIfNeeded(obj.live) as BooleanBothQuery
104 114
105 if (obj.search !== undefined) this.search = obj.search 115 if (obj.search !== undefined) this.search = escapeIfNeeded(obj.search)
106 116
107 this.buildActiveFilters() 117 this.buildActiveFilters()
108 } 118 }