mui / mui/material-ui

[CSP] createCache nonce-value added to Cache not applied to inline-styles

Open
#38,965 5 comments 0 reactions 1 assignee View on GitHub

@mnajdova is already working on this.

Since Sep 14, 2023.

scope: styles
Dominant language
JavaScript
Stars
99.1k
Forks
32.5k
Avg merge
2d 17h
Merged PRs (30d)
106

Description

Steps to reproduce 🕹

we are using remixjs+MUI and want to add CSP-Headers following these guides:
CSP-guide
remix-Styling

we added the nonce-value to the cache on CreateEmotionCache.tsx:

CreateEmotionCache.tsx
import createCache from '@emotion/cache';

import { cspScriptNonce } from '@/root';

export default function createEmotionCache() {
  return createCache({ key: 'css', nonce: cspScriptNonce });
}

here is how we have entry.server.tsx:

entry.server.tsx
import { CacheProvider } from '@emotion/react';
import createEmotionServer from '@emotion/server/create-instance';
import CssBaseline from '@mui/material/CssBaseline';
import { ThemeProvider } from '@mui/material/styles';
import type { EntryContext } from '@remix-run/node';
import { Response } from '@remix-run/node';
import { RemixServer } from '@remix-run/react';
import { renderToString } from 'react-dom/server';

import { theme } from '@/styles/theme';
import createEmotionCache from '@/styles/utils/createEmotionCache';

import { StateManagerProvider } from '@/lib/state-manager';

export default function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext,
): Response {
  const cache = createEmotionCache();
  const { extractCriticalToChunks } = createEmotionServer(cache);
  const html = renderToString(
    <StateManagerProvider>
      <CacheProvider value={cache}>
        <ThemeProvider theme={theme}>
          <CssBaseline />
          <RemixServer context={remixContext} url={request.url} />
        </ThemeProvider>
      </CacheProvider>
    </StateManagerProvider>,
  );

  const { styles } = extractCriticalToChunks(html);
  const loaderData = remixContext.staticHandlerContext.loaderData as {
    root: {
      cspScriptNonce: string | undefined;
    };
  };
  const nonce = loaderData.root.cspScriptNonce;
  let stylesHTML = '';
  styles.forEach(({ key, ids, css }) => {
    const emotionKey = `${key} ${ids.join(' ')}`;
    const newStyleTag = `<style nonce="${nonce}" data-emotion="${emotionKey}">${css}</style>`;
    stylesHTML = `${stylesHTML}${newStyleTag}`;
  });
  const markup = html.replace(
    /<meta(\s)*name="emotion-insertion-point"(\s)*content="emotion-insertion-point"(\s)*\/>/,
    `<meta name="emotion-insertion-point" content="emotion-insertion-point"/>${stylesHTML}`,
  );
  const { CSPolicy, CSPheaders } = createCSP(nonce!!);
  responseHeaders.set(CSPolicy, CSPheaders);
  responseHeaders.set('Content-Type', 'text/html');

  return new Response(`<!DOCTYPE html>${markup}`, {
    headers: responseHeaders,
    status: responseStatusCode,
  });
}

const createCSP = (nonce: string) => {
  const isDevelopment = process.env.NODE_ENV === 'development';
  let scriptSrc: string;
  if (typeof nonce === 'string' && nonce.length > 10) {
    scriptSrc = `'report-sample' 'nonce-${nonce}'`;
  } else if (isDevelopment) {
    // Allow the <LiveReload /> component to load without a nonce in the error pages
    scriptSrc = "'report-sample' 'unsafe-inline'";
  } else {
    scriptSrc = "'report-sample'";
  }

  const connectSrc = isDevelopment ? 'ws://localhost:*' : '';

  const CSPolicy = isDevelopment
    ? 'Content-Security-Policy-Report-Only'
    : 'Content-Security-Policy';

  const CSPheaders =
    "default-src 'self'; " +
    `script-src 'self' ${scriptSrc}; ` +
    `style-src 'self' ${scriptSrc}; ` +
    "img-src 'self' data: blob:; " +
    `connect-src 'self' ${connectSrc} data:; ` +
    "object-src 'none'; " +
    "worker-src 'self' blob:; " +
    "frame-ancestors 'none'; " +
    'block-all-mixed-content; ' +
    'report-to /reporting/csp';
  return { CSPolicy, CSPheaders };
};

Current behavior 😯

we added nonce-value to the <style/> tag in the entry.server.tsx but we are facing the errors:

Screenshot 2023-09-13 at 14 42 35
Expected behavior 🤔

nonce-value assigned to Cache added to <Style/> tags so that they are identified and nonce-values match

Context 🔦

we want to add CSP headers to harden security to our Remixjs app.

Your environment 🌎
`npx @mui/envinfo`
  Chrome, Safari, Firefox
 System:
    OS: macOS 13.5.2
  Binaries:
    Node: 18.16.1 - /usr/local/bin/node
    Yarn: 1.22.19 - /opt/homebrew/bin/yarn
    npm: 9.6.6 - /opt/homebrew/bin/npm
  Browsers:
    Chrome: 116.0.5845.187
    Edge: Not Found
    Safari: 16.6
  npmPackages:
    @emotion/react: 11.11.1 => 11.11.1 
    @emotion/styled: 11.11.0 => 11.11.0 
    @mui/base: 5.0.0-beta.14 => 5.0.0-beta.14 
    @mui/core-downloads-tracker:  5.14.8 
    @mui/material: 5.14.8 => 5.14.8 
    @mui/private-theming:  5.14.8 
    @mui/styled-engine:  5.14.8 
    @mui/system: 5.14.8 => 5.14.8 
    @mui/types:  7.2.4 
    @mui/utils:  5.14.8 
    @mui/x-data-grid:  6.13.0 
    @mui/x-data-grid-pro: 6.13.0 => 6.13.0 
    @mui/x-license-pro: 6.10.2 => 6.10.2 
    @types/react: 18.2.21 => 18.2.21 
    react: 18.2.0 => 18.2.0 
    react-dom: 18.2.0 => 18.2.0 
    typescript: 5.2.2 => 5.2.2 

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.