Fixed #4241 - Add Content Security Policy (CSP) config
parent
590504d137
commit
c42f45455c
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<AccordionPassThroughOptions>;
|
||||
accordiontab?: DefaultPTOptions<AccordionTabPassThroughOptions>;
|
||||
|
|
|
@ -132,7 +132,10 @@ export const defaultOptions = {
|
|||
tooltip: 1100
|
||||
},
|
||||
pt: undefined,
|
||||
unstyled: false
|
||||
unstyled: false,
|
||||
csp: {
|
||||
nonce: undefined
|
||||
}
|
||||
};
|
||||
|
||||
const PrimeVueSymbol = Symbol();
|
||||
|
|
|
@ -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`;
|
||||
|
|
|
@ -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 = '';
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = `
|
||||
|
|
|
@ -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 = '';
|
||||
|
|
|
@ -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';
|
||||
|
@ -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 = '';
|
||||
|
|
|
@ -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 = `
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
@ -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 = '';
|
||||
|
|
|
@ -5,6 +5,7 @@ export interface StyleOptions {
|
|||
name?: string;
|
||||
id?: string;
|
||||
media?: string;
|
||||
nonce?: string;
|
||||
}
|
||||
|
||||
export declare function useStyle(
|
||||
|
@ -15,6 +16,6 @@ export declare function useStyle(
|
|||
name: string;
|
||||
css: any;
|
||||
unload: () => void;
|
||||
load: () => void;
|
||||
load: (css?: string, options?: any) => void;
|
||||
isLoaded: boolean;
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@ 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 = () => {};
|
||||
|
||||
|
@ -27,18 +27,21 @@ export function useStyle(css, options = {}) {
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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>
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue