Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSP: Violating Inline Style Policy #2423

Closed
melloware opened this issue Nov 12, 2021 · 2 comments · Fixed by #2442 or #2525
Closed

CSP: Violating Inline Style Policy #2423

melloware opened this issue Nov 12, 2021 · 2 comments · Fixed by #2442 or #2525
Labels
Type: Enhancement Issue contains an enhancement related to a specific component. Additional functionality has been add
Milestone

Comments

@melloware
Copy link
Member

melloware commented Nov 12, 2021

[x ] bug report

Current behavior
Using Create React App and enabling CSP protection of the app some components in PrimeReact violate the inline style policy. This is due to use of document.head.appendChild in things like Datatable to make it responsive.

Policy:

const cspConfigPolicy = {
    'default-src': "'self'",
    'connect-src': "'self' localhost:* 127.0.0.1",
    'base-uri': "'self'",
    'object-src': "'none'",
    'script-src': ["'self'  https:"],
    'style-src': ["'self'  https:"]
};

Error:
image

The offending code for example can be found here when creating Responsive Styles:

createStyleElement() {
this.styleElement = document.createElement('style');
document.head.appendChild(this.styleElement);
}
createResponsiveStyle() {
if (!this.responsiveStyleElement) {
this.responsiveStyleElement = document.createElement('style');
document.head.appendChild(this.responsiveStyleElement);
let innerHTML = `
@media screen and (max-width: ${this.props.breakpoint}) {
.p-datatable[${this.attributeSelector}] .p-datatable-thead > tr > th,
.p-datatable[${this.attributeSelector}] .p-datatable-tfoot > tr > td {
display: none !important;
}
.p-datatable[${this.attributeSelector}] .p-datatable-tbody > tr > td {
display: flex;
width: 100% !important;
align-items: center;
justify-content: space-between;
}
.p-datatable[${this.attributeSelector}] .p-datatable-tbody > tr > td:not(:last-child) {
border: 0 none;
}
.p-datatable[${this.attributeSelector}].p-datatable-gridlines .p-datatable-tbody > tr > td:last-child {
border-top: 0;
border-right: 0;
border-left: 0;
}
.p-datatable[${this.attributeSelector}] .p-datatable-tbody > tr > td > .p-column-title {
display: block;
}
}
`;
this.responsiveStyleElement.innerHTML = innerHTML;
}
}

Expected behavior
PrimeReact can run with 'style-src': self CSP Policy.

Minimal reproduction of the problem with instructions
Follow these instructions to add CSP to a Create React App: https://medium.com/@nrshahri/csp-cra-324dd83fe5ff

Then run the showcase and hit the Datatable page you will see the issue

Please tell us about your environment:

  • React version: 17.0.2
  • PrimeReact version: 7.0.0
  • Browser: [all ]
  • Language: [all]
@melloware
Copy link
Member Author

melloware commented Nov 17, 2021

This is a very important fix for security. This checks if the downstream application using PrimeReact has set a CSP NONCE environment variable process.env.REACT_APP_CSS_NONCE. This can be hardcoded in their .env file or if they are using a dynamic NONCE generator like CRA CSP WebPack Plugin: https://github.com/slackhq/csp-html-webpack-plugin.

If you do dynamically generate your NONCE you can easily set it in your application like this for PrimeReact to pick it up and use it in all its inline styling efforts.

// attempt to read an existing NONCE from a  CSS link on the page
let nonce;
let cssLink = document.querySelector('link[nonce]') as HTMLScriptElement;
if (cssLink) {
    nonce = cssLink?.nonce!;
}

// now set the environment variable for PrimeReact to read
if (nonce) {
    process.env.REACT_APP_CSS_NONCE= nonce;
}

@mertsincan mertsincan added the Type: Enhancement Issue contains an enhancement related to a specific component. Additional functionality has been add label Nov 18, 2021
@mertsincan mertsincan added this to the 8.0.0 milestone Nov 18, 2021
@mertsincan mertsincan modified the milestones: 8.0.0, 7.1.0 Dec 13, 2021
melloware added a commit to melloware/primereact that referenced this issue Dec 15, 2021
@melloware
Copy link
Member Author

If anyone needs it here is my Customize CRA script for properly generating CSP for a PrimeReact app.

/**
 * Content Security Policy Configuration
 */
const webpack = require("webpack");
const get = require("lodash/get");
const { override } = require("customize-cra");
const cspHtmlWebpackPlugin = require("csp-html-webpack-plugin");
const crypto = require("crypto");

const cspConfigPolicy = {
    "default-src": "'self'",
    "connect-src": "'self' localhost:* https: *.google.com",
    "worker-src": "'self' blob:",
    "img-src": "'self' blob: data: content:",
    "font-src": "'self' data:",
    "frame-src": "'self'",
    "manifest-src": "'self'",
    "object-src": "'none'",
    "script-src": ["'strict-dynamic'  https:"],
    "style-src": "'self'",
};

// generate a new nonce for PrimeReact inline styles
const nonce = crypto.randomBytes(16).toString("base64");

/**
 * PrimeReact does some clever things for Responsive Design where it dynamically 
 * creates CSS based on the breakpoints you use in code. For example on the DataTable
 * if `responsiveLayout="stack" breakpoint="960px"` is used then PrimeReact generates 
 * dynamic CSS to handle this breakpoint.  
 * 
 * However that violates CSP Inline Style rules so 4
 * process.env.REACT_APP_CSS_NONCE must be set as part of the webpack build.
 * 
 * <style nonce="abc12344556667">...</style>
 * 
 * @see https://github.com/primefaces/primereact/issues/2423
 */
function generatePrimeReactCssNonce(builtPolicy, _htmlPluginData, $, compilation) {
    console.log("Generating CSP Policy... ");
    console.log("");
    console.log("process.env.REACT_APP_CSS_NONCE = " + nonce);
    let styleIndex = builtPolicy.indexOf("style-src 'self'") + "style-src 'self'".length;
    builtPolicy = builtPolicy.substr(0, styleIndex) + " 'nonce-" + nonce + "'" + builtPolicy.substr(styleIndex);
    console.log("");
    console.log("CSP POLICY = " + builtPolicy);

    let metaTag = $('meta[http-equiv="Content-Security-Policy"]');

    // Add element if it doesn't exist.
    if (!metaTag.length) {
        metaTag = cheerio.load('<meta http-equiv="Content-Security-Policy">')("meta");
        metaTag.prependTo($("head"));
    }

    // build the policy into the context attr of the csp meta tag
    metaTag.attr("content", builtPolicy);

    // eslint-disable-next-line no-param-reassign
    _htmlPluginData.html = get(_htmlPluginData, "plugin.options.xhtml", false) ? $.xml() : $.html();
}

/**
 * Create the CSP Plugin.
 */
const cspPlugin = new cspHtmlWebpackPlugin(cspConfigPolicy, {
    enabled: true,
    processFn: generatePrimeReactCssNonce,
    hashingMethod: "sha256",
    hashEnabled: {
        "script-src": true,
        "style-src": true,
    },
    nonceEnabled: {
        "script-src": true,
        "style-src": true,
    },
});

/**
 * Create the Environment Plugin to inject REACT_APP_CSS_NONCE.
 */
const envPlugin = new webpack.EnvironmentPlugin({
    REACT_APP_CSS_NONCE: nonce
});

/**
 * Updates the webpack config to have the new plugin.
 * 
 * @param config the current webpack config
 * @returns the updated webpack config
 */
function addCspHtmlWebpackPlugin(config) {
    if (process.env.NODE_ENV === "production") {
        config.plugins.push(cspPlugin);
        config.plugins.push(envPlugin);
    }
    config.optimization.splitChunks = {
        cacheGroups: {
            default: false,
        },
    };
    config.optimization.runtimeChunk = false;
    return config;
}

module.exports = {
    webpack: override(addCspHtmlWebpackPlugin),
};

melloware added a commit to melloware/primereact that referenced this issue Dec 19, 2021
melloware added a commit to melloware/primereact that referenced this issue Dec 19, 2021
melloware added a commit to melloware/primereact that referenced this issue Dec 19, 2021
melloware added a commit to melloware/primereact that referenced this issue Jan 5, 2022
melloware added a commit to melloware/primereact that referenced this issue Jan 5, 2022
mertsincan added a commit that referenced this issue Jan 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Enhancement Issue contains an enhancement related to a specific component. Additional functionality has been add
Projects
None yet
2 participants