Skip to content

Commit

Permalink
Fixed #4241 - Add Content Security Policy (CSP) config
Browse files Browse the repository at this point in the history
  • Loading branch information
mertsincan committed Aug 11, 2023
1 parent 590504d commit c42f454
Show file tree
Hide file tree
Showing 22 changed files with 92 additions and 21 deletions.
17 changes: 10 additions & 7 deletions components/lib/basecomponent/BaseComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
}
}
}
Expand All @@ -395,7 +395,7 @@ export default {
this._hook('onCreated');
},
beforeMount() {
loadBaseStyle();
loadBaseStyle(undefined, { nonce: this.$config?.csp?.nonce });
this._loadGlobalStyles();
this._hook('onBeforeMount');
},
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
};
Expand Down
7 changes: 5 additions & 2 deletions components/lib/basedirective/BaseDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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) => {
Expand Down
1 change: 1 addition & 0 deletions components/lib/calendar/Calendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions components/lib/carousel/Carousel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
5 changes: 5 additions & 0 deletions components/lib/config/PrimeVue.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export interface PrimeVueConfiguration {
zIndex?: PrimeVueZIndexOptions;
pt?: PrimeVuePTOptions;
unstyled?: boolean;
csp?: PrimeVueCSPOptions;
}

export interface PrimeVueZIndexOptions {
Expand All @@ -112,6 +113,10 @@ export interface PrimeVueZIndexOptions {
tooltip?: number;
}

export interface PrimeVueCSPOptions {
nonce?: string;
}

export interface PrimeVuePTOptions {
accordion?: DefaultPTOptions<AccordionPassThroughOptions>;
accordiontab?: DefaultPTOptions<AccordionTabPassThroughOptions>;
Expand Down
5 changes: 4 additions & 1 deletion components/lib/config/PrimeVue.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ export const defaultOptions = {
tooltip: 1100
},
pt: undefined,
unstyled: false
unstyled: false,
csp: {
nonce: undefined
}
};

const PrimeVueSymbol = Symbol();
Expand Down
2 changes: 2 additions & 0 deletions components/lib/datatable/DataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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`;
Expand Down
1 change: 1 addition & 0 deletions components/lib/dialog/Dialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '';
Expand Down
2 changes: 1 addition & 1 deletion components/lib/editor/BaseEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
1 change: 1 addition & 0 deletions components/lib/galleria/GalleriaThumbnails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions components/lib/orderlist/OrderList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `
Expand Down
1 change: 1 addition & 0 deletions components/lib/overlaypanel/OverlayPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '';
Expand Down
3 changes: 2 additions & 1 deletion components/lib/paginator/Paginator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
</template>

<script>
import { UniqueComponentId } from 'primevue/utils';
import { DomHandler, UniqueComponentId } from 'primevue/utils';
import BasePaginator from './BasePaginator.vue';
import CurrrentPageReport from './CurrentPageReport.vue';
import FirstPageLink from './FirstPageLink.vue';
Expand Down Expand Up @@ -144,6 +144,7 @@ export default {
if (this.hasBreakpoints() && !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 = '';
Expand Down
1 change: 1 addition & 0 deletions components/lib/picklist/PickList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,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 = `
Expand Down
3 changes: 2 additions & 1 deletion components/lib/toast/Toast.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<script>
import Portal from 'primevue/portal';
import ToastEventBus from 'primevue/toasteventbus';
import { ObjectUtils, UniqueComponentId, ZIndexUtils } from 'primevue/utils';
import { DomHandler, ObjectUtils, UniqueComponentId, ZIndexUtils } from 'primevue/utils';
import BaseToast from './BaseToast.vue';
import ToastMessage from './ToastMessage.vue';
Expand Down Expand Up @@ -113,6 +113,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 = '';
Expand Down
3 changes: 2 additions & 1 deletion components/lib/usestyle/UseStyle.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface StyleOptions {
name?: string;
id?: string;
media?: string;
nonce?: string;
}

export declare function useStyle(
Expand All @@ -15,6 +16,6 @@ export declare function useStyle(
name: string;
css: any;
unload: () => void;
load: () => void;
load: (css?: string, options?: any) => void;
isLoaded: boolean;
};
15 changes: 9 additions & 6 deletions components/lib/usestyle/UseStyle.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,29 @@ export function useStyle(css, options = {}) {
const styleRef = ref(null);

const defaultDocument = DomHandler.isClient() ? window.document : undefined;
const { document = defaultDocument, immediate = true, manual = false, name = `style_${++_id}`, id = undefined, media = undefined } = options;
const { document = defaultDocument, immediate = true, manual = false, name = `style_${++_id}`, id = undefined, media = undefined, nonce = undefined } = options;

let stop = () => {};

/* @todo: Improve _options params */
const load = (_css, _options = {}) => {
if (!document) return;

const [_name, _id] = [_options.name || name, _options.id || id];
const [_name, _id, _nonce] = [_options.name || name, _options.id || id, _options.nonce || nonce];

styleRef.value = document.querySelector(`style[data-primevue-style-id="${_name}"]`) || document.getElementById(_id) || document.createElement('style');

if (!styleRef.value.isConnected) {
cssRef.value = _css || css;

styleRef.value.type = 'text/css';
_id && (styleRef.value.id = _id);
media && (styleRef.value.media = media);
DomHandler.setAttributes(styleRef.value, {
type: 'text/css',
id: _id,
media,
nonce: _nonce
});
document.head.appendChild(styleRef.value);
name && styleRef.value.setAttribute('data-primevue-style-id', name);
DomHandler.setAttribute(styleRef.value, 'data-primevue-style-id', name);
DomHandler.setAttributes(styleRef.value, _options);
}

Expand Down
6 changes: 6 additions & 0 deletions components/lib/utils/DomHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ export default {
return undefined;
},

setAttribute(element, attribute = '', value) {
if (element && value !== null && value !== undefined) {
element.setAttribute(attribute, value);
}
},

setAttributes(element, attributes = {}) {
if (element) {
const computedStyles = (rule, value) => {
Expand Down
1 change: 1 addition & 0 deletions components/lib/utils/Utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export declare class DomHandler {
static find(el: HTMLElement, selector: string): any[];
static findSingle(el: HTMLElement, selector: string): any;
static createElement(type: string, attributes: object, ...children: any): HTMLElement;
static setAttribute(el: HTMLElement, attribute: string, value: any): void;
static setAttributes(el: HTMLElement, attributes: object): void;
static getAttribute(el: HTMLElement, name: string): any;
static isAttributeEquals(el: HTMLElement, name: string, value: any): boolean;
Expand Down
2 changes: 1 addition & 1 deletion components/lib/virtualscroller/BaseVirtualScroller.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const styles = `
}
`;
const { load: loadStyle } = useStyle(styles, { name: 'virtualscroller' });
const { load: loadStyle } = useStyle(styles, { name: 'virtualscroller', manual: true });
export default {
name: 'BaseVirtualScroller',
Expand Down
23 changes: 23 additions & 0 deletions doc/configuration/csp/NonceDoc.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<template>
<DocSectionText v-bind="$attrs">
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">nonce</a> value to use on dynamically generated style elements in core.</p>

<DocSectionCode :code="code1" hideToggleCode importCode hideCodeSandbox hideStackBlitz />
</DocSectionText>
</template>

<script>
export default {
data() {
return {
code1: {
basic: `app.use(PrimeVue, {
csp: {
nonce: '...'
}
});`
}
};
}
};
</script>
12 changes: 12 additions & 0 deletions pages/configuration/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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',
Expand Down

0 comments on commit c42f454

Please sign in to comment.