material-ui
material-ui copied to clipboard
[CSP] createCache nonce-value added to Cache not applied to inline-styles
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:
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
Hello @mnajdova 🤠
any updates regarding this?
It's hard to know what may be miss-configured without a repo example to look into. Can you create a simpler reproduction and share link to the repository? Thanks!
Since the issue is missing key information and has been inactive for 7 days, it has been automatically closed. If you wish to see the issue reopened, please provide the missing information.
hi @mnajdova
Here is a repo to see the error:
https://github.com/Mateo-P/nonce-problem.git
my thought is that the inconsistency is on the createEmotionCache function.
why does it need to be executed on the client, entry.client.tsx?
this is causing the nonce to have different values in the server and client. for instance, when hardcoding a nonce value the erros go away
I have the same problem when applying nonce's described in the doc, but on a NextJS project. https://github.com/mui/material-ui/issues/40435 I think all cases where the style attribute is used should be replaced with a child style element. (This is done by the emotion dependency, but maybe it's not the case for the main source?)