From c42f45455c0c24d90ce5d85f0ebd7f8365053867 Mon Sep 17 00:00:00 2001 From: mertsincan Date: Fri, 11 Aug 2023 02:34:02 +0100 Subject: [PATCH] Fixed #4241 - Add Content Security Policy (CSP) config --- .../lib/basecomponent/BaseComponent.vue | 17 ++++++++------ components/lib/basedirective/BaseDirective.js | 7 ++++-- components/lib/calendar/Calendar.vue | 1 + components/lib/carousel/Carousel.vue | 1 + components/lib/config/PrimeVue.d.ts | 5 ++++ components/lib/config/PrimeVue.js | 5 +++- components/lib/datatable/DataTable.vue | 2 ++ components/lib/dialog/Dialog.vue | 1 + components/lib/editor/BaseEditor.vue | 2 +- .../lib/galleria/GalleriaThumbnails.vue | 1 + components/lib/orderlist/OrderList.vue | 1 + components/lib/overlaypanel/OverlayPanel.vue | 1 + components/lib/paginator/Paginator.vue | 3 ++- components/lib/picklist/PickList.vue | 1 + components/lib/toast/Toast.vue | 3 ++- components/lib/usestyle/UseStyle.d.ts | 3 ++- components/lib/usestyle/UseStyle.js | 15 +++++++----- components/lib/utils/DomHandler.js | 6 +++++ components/lib/utils/Utils.d.ts | 1 + .../virtualscroller/BaseVirtualScroller.vue | 2 +- doc/configuration/csp/NonceDoc.vue | 23 +++++++++++++++++++ pages/configuration/index.vue | 12 ++++++++++ 22 files changed, 92 insertions(+), 21 deletions(-) create mode 100644 doc/configuration/csp/NonceDoc.vue diff --git a/components/lib/basecomponent/BaseComponent.vue b/components/lib/basecomponent/BaseComponent.vue index 461c1aeb7..3d9e9a0c8 100644 --- a/components/lib/basecomponent/BaseComponent.vue +++ b/components/lib/basecomponent/BaseComponent.vue @@ -381,8 +381,8 @@ export default { immediate: true, handler(newValue) { if (!newValue) { - loadStyle(); - this.$options.css && this.$css.loadStyle(); + loadStyle(undefined, { nonce: this.$config?.csp?.nonce }); + this.$options.css && this.$css.loadStyle(undefined, { nonce: this.$config?.csp?.nonce }); } } } @@ -395,7 +395,7 @@ export default { this._hook('onCreated'); }, beforeMount() { - loadBaseStyle(); + loadBaseStyle(undefined, { nonce: this.$config?.csp?.nonce }); this._loadGlobalStyles(); this._hook('onBeforeMount'); }, @@ -435,7 +435,7 @@ export default { const globalCSS = this._getOptionValue(this.globalPT, 'global.css', this.$params); - ObjectUtils.isNotEmpty(globalCSS) && loadGlobalStyle(globalCSS); + ObjectUtils.isNotEmpty(globalCSS) && loadGlobalStyle(globalCSS, { nonce: this.$config?.csp?.nonce }); }, _getHostInstance(instance) { return instance ? (this.$options.hostName ? (instance.$.type.name === this.$options.hostName ? instance : this._getHostInstance(instance.$parentInstance)) : instance.$parentInstance) : undefined; @@ -497,19 +497,22 @@ export default { }, computed: { globalPT() { - return ObjectUtils.getItemValue(this.$primevue.config.pt, { instance: this }); + return ObjectUtils.getItemValue(this.$config.pt, { instance: this }); }, defaultPT() { - return this._getOptionValue(this.$primevue.config.pt, this.$options.hostName || this.$.type.name, { instance: this }) || this.globalPT; + return this._getOptionValue(this.$config.pt, this.$options.hostName || this.$.type.name, { instance: this }) || this.globalPT; }, isUnstyled() { - return this.unstyled !== undefined ? this.unstyled : this.$primevue.config.unstyled; + return this.unstyled !== undefined ? this.unstyled : this.$config.unstyled; }, $params() { return { instance: this, props: this.$props, state: this.$data, parentInstance: this.$parentInstance }; }, $css() { return { classes: undefined, inlineStyles: undefined, loadStyle: () => {}, loadCustomStyle: () => {}, ...(this._getHostInstance(this) || {}).$css, ...this.$options.css }; + }, + $config() { + return this.$primevue?.config; } } }; diff --git a/components/lib/basedirective/BaseDirective.js b/components/lib/basedirective/BaseDirective.js index 9a774e5c5..40c2d418b 100644 --- a/components/lib/basedirective/BaseDirective.js +++ b/components/lib/basedirective/BaseDirective.js @@ -57,6 +57,7 @@ const BaseDirective = { $binding: binding, $el: $prevInstance['$el'] || undefined, $css: { classes: undefined, inlineStyles: undefined, loadStyle: () => {}, ...options?.css }, + $config: config, /* computed instance variables */ defaultPT: config?.pt?.directives?.[name], isUnstyled: el.unstyled !== undefined ? el.unstyled : config?.unstyled, @@ -78,8 +79,10 @@ const BaseDirective = { handleHook('created', el, binding, vnode, prevVnode); }, beforeMount: (el, binding, vnode, prevVnode) => { - loadBaseStyle(); - !el.$instance?.isUnstyled && el.$instance?.$css?.loadStyle(); + const config = binding?.instance?.$primevue?.config; + + loadBaseStyle(undefined, { nonce: config?.csp?.nonce }); + !el.$instance?.isUnstyled && el.$instance?.$css?.loadStyle(undefined, { nonce: config?.csp?.nonce }); handleHook('beforeMount', el, binding, vnode, prevVnode); }, mounted: (el, binding, vnode, prevVnode) => { diff --git a/components/lib/calendar/Calendar.vue b/components/lib/calendar/Calendar.vue index 231c8cf21..4d2fabc99 100755 --- a/components/lib/calendar/Calendar.vue +++ b/components/lib/calendar/Calendar.vue @@ -2582,6 +2582,7 @@ export default { if (!this.responsiveStyleElement) { this.responsiveStyleElement = document.createElement('style'); this.responsiveStyleElement.type = 'text/css'; + DomHandler.setAttribute(this.responsiveStyleElement, 'nonce', this.$primevue?.config?.csp?.nonce); document.body.appendChild(this.responsiveStyleElement); } diff --git a/components/lib/carousel/Carousel.vue b/components/lib/carousel/Carousel.vue index 1c07aeef2..b326d5d6b 100755 --- a/components/lib/carousel/Carousel.vue +++ b/components/lib/carousel/Carousel.vue @@ -516,6 +516,7 @@ export default { if (!this.carouselStyle) { this.carouselStyle = document.createElement('style'); this.carouselStyle.type = 'text/css'; + DomHandler.setAttribute(this.carouselStyle, 'nonce', this.$primevue?.config?.csp?.nonce); document.body.appendChild(this.carouselStyle); } diff --git a/components/lib/config/PrimeVue.d.ts b/components/lib/config/PrimeVue.d.ts index bc2cc7c5e..018ea43ab 100644 --- a/components/lib/config/PrimeVue.d.ts +++ b/components/lib/config/PrimeVue.d.ts @@ -103,6 +103,7 @@ export interface PrimeVueConfiguration { zIndex?: PrimeVueZIndexOptions; pt?: PrimeVuePTOptions; unstyled?: boolean; + csp?: PrimeVueCSPOptions; } export interface PrimeVueZIndexOptions { @@ -112,6 +113,10 @@ export interface PrimeVueZIndexOptions { tooltip?: number; } +export interface PrimeVueCSPOptions { + nonce?: string; +} + export interface PrimeVuePTOptions { accordion?: DefaultPTOptions; accordiontab?: DefaultPTOptions; diff --git a/components/lib/config/PrimeVue.js b/components/lib/config/PrimeVue.js index 078b401b0..5f0b1c155 100644 --- a/components/lib/config/PrimeVue.js +++ b/components/lib/config/PrimeVue.js @@ -132,7 +132,10 @@ export const defaultOptions = { tooltip: 1100 }, pt: undefined, - unstyled: false + unstyled: false, + csp: { + nonce: undefined + } }; const PrimeVueSymbol = Symbol(); diff --git a/components/lib/datatable/DataTable.vue b/components/lib/datatable/DataTable.vue index a60a72d31..33430c637 100755 --- a/components/lib/datatable/DataTable.vue +++ b/components/lib/datatable/DataTable.vue @@ -1852,12 +1852,14 @@ export default { createStyleElement() { this.styleElement = document.createElement('style'); this.styleElement.type = 'text/css'; + DomHandler.setAttribute(this.styleElement, 'nonce', this.$primevue?.config?.csp?.nonce); document.head.appendChild(this.styleElement); }, createResponsiveStyle() { if (!this.responsiveStyleElement) { this.responsiveStyleElement = document.createElement('style'); this.responsiveStyleElement.type = 'text/css'; + DomHandler.setAttribute(this.responsiveStyleElement, 'nonce', this.$primevue?.config?.csp?.nonce); document.head.appendChild(this.responsiveStyleElement); let tableSelector = `.p-datatable-wrapper ${this.virtualScrollerDisabled ? '' : '> .p-virtualscroller'} > .p-datatable-table`; diff --git a/components/lib/dialog/Dialog.vue b/components/lib/dialog/Dialog.vue index bc4ac172e..8ce22dc27 100755 --- a/components/lib/dialog/Dialog.vue +++ b/components/lib/dialog/Dialog.vue @@ -259,6 +259,7 @@ export default { if (!this.styleElement && !this.isUnstyled) { this.styleElement = document.createElement('style'); this.styleElement.type = 'text/css'; + DomHandler.setAttribute(this.styleElement, 'nonce', this.$primevue?.config?.csp?.nonce); document.head.appendChild(this.styleElement); let innerHTML = ''; diff --git a/components/lib/editor/BaseEditor.vue b/components/lib/editor/BaseEditor.vue index 698202e39..0d73b0fbf 100644 --- a/components/lib/editor/BaseEditor.vue +++ b/components/lib/editor/BaseEditor.vue @@ -956,7 +956,7 @@ const classes = { content: 'p-editor-content' }; -const { load: loadStyle } = useStyle(styles, { name: 'editor' }); +const { load: loadStyle } = useStyle(styles, { name: 'editor', manual: true }); export default { name: 'BaseEditor', diff --git a/components/lib/galleria/GalleriaThumbnails.vue b/components/lib/galleria/GalleriaThumbnails.vue index f6c456678..b4ee3abd7 100755 --- a/components/lib/galleria/GalleriaThumbnails.vue +++ b/components/lib/galleria/GalleriaThumbnails.vue @@ -424,6 +424,7 @@ export default { if (!this.thumbnailsStyle) { this.thumbnailsStyle = document.createElement('style'); this.thumbnailsStyle.type = 'text/css'; + DomHandler.setAttribute(this.thumbnailsStyle, 'nonce', this.$primevue?.config?.csp?.nonce); document.body.appendChild(this.thumbnailsStyle); } diff --git a/components/lib/orderlist/OrderList.vue b/components/lib/orderlist/OrderList.vue index 2d6ee257c..dd412cbaf 100755 --- a/components/lib/orderlist/OrderList.vue +++ b/components/lib/orderlist/OrderList.vue @@ -482,6 +482,7 @@ export default { this.$el.setAttribute(this.attributeSelector, ''); this.styleElement = document.createElement('style'); this.styleElement.type = 'text/css'; + DomHandler.setAttribute(this.styleElement, 'nonce', this.$primevue?.config?.csp?.nonce); document.head.appendChild(this.styleElement); let innerHTML = ` diff --git a/components/lib/overlaypanel/OverlayPanel.vue b/components/lib/overlaypanel/OverlayPanel.vue index fa588d00b..97dadcef5 100755 --- a/components/lib/overlaypanel/OverlayPanel.vue +++ b/components/lib/overlaypanel/OverlayPanel.vue @@ -246,6 +246,7 @@ export default { if (!this.styleElement && !this.isUnstyled) { this.styleElement = document.createElement('style'); this.styleElement.type = 'text/css'; + DomHandler.setAttribute(this.styleElement, 'nonce', this.$primevue?.config?.csp?.nonce); document.head.appendChild(this.styleElement); let innerHTML = ''; diff --git a/components/lib/paginator/Paginator.vue b/components/lib/paginator/Paginator.vue index af9fb3617..9a38490d6 100755 --- a/components/lib/paginator/Paginator.vue +++ b/components/lib/paginator/Paginator.vue @@ -52,7 +52,7 @@ diff --git a/pages/configuration/index.vue b/pages/configuration/index.vue index 4bd80b3cd..a09ea305d 100644 --- a/pages/configuration/index.vue +++ b/pages/configuration/index.vue @@ -22,6 +22,7 @@ import PTDoc from '@/doc/configuration/PTDoc.vue'; import RippleDoc from '@/doc/configuration/RippleDoc'; import UnstyledDoc from '@/doc/configuration/UnstyledDoc'; import ZIndexDoc from '@/doc/configuration/ZIndexDoc'; +import NonceDoc from '@/doc/configuration/csp/NonceDoc'; import LocaleApiDoc from '@/doc/configuration/locale/LocaleApiDoc'; import RepositoryDoc from '@/doc/configuration/locale/RepositoryDoc'; import SetLocaleDoc from '@/doc/configuration/locale/SetLocaleDoc'; @@ -60,6 +61,17 @@ export default { label: 'ZIndex', component: ZIndexDoc }, + { + id: 'csp', + label: 'Content Security Policy (CSP)', + children: [ + { + id: 'nonce', + label: 'Nonce', + component: NonceDoc + } + ] + }, { id: 'locale', label: 'Locale',