remix-run / remix

Build Better Websites. Create modern, resilient user experiences with web fundamentals.
https://remix.run
MIT License
29.66k stars 2.5k forks source link

CSP stops working when single fetch is enabled #10110

Closed ReidCole closed 1 hour ago

ReidCole commented 4 hours ago

Reproduction

StackBlitz reproduction: https://stackblitz.com/~/github.com/ReidCole/remix-singlefetch-csp

System Info

System:
  OS: Linux 6.8 Linux Mint 22 (Wilma)
  CPU: (12) x64 AMD Ryzen 5 7640U w/ Radeon 760M Graphics
  Memory: 4.00 GB / 14.93 GB
  Container: Yes
  Shell: 5.9 - /usr/bin/zsh
Binaries:
  Node: 22.8.0 - ~/.nvm/versions/node/v22.8.0/bin/node
  npm: 10.8.2 - ~/.nvm/versions/node/v22.8.0/bin/npm
npmPackages:
  @remix-run/dev: ^2.13.1 => 2.13.1
  @remix-run/node: ^2.13.1 => 2.13.1
  @remix-run/react: ^2.13.1 => 2.13.1
  @remix-run/serve: ^2.13.1 => 2.13.1
  vite: ^5.1.0 => 5.4.9

Used Package Manager

npm

Expected Behavior

In vite.config.ts, when v3_singleFetch is set to true, there should be no errors with the Content Security Policy header.

Actual Behavior

The browser console shows this error when visiting the index route:

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'nonce-my_nonce' 'strict-dynamic'". Either the 'unsafe-inline' keyword, a hash ('sha256-kFFCt2bRik8XnhrWeg2mf+46osExdawZhbMUaEMGnYk='), or a nonce ('nonce-...') is required to enable inline execution.

If you click the link on the browser console error, it shows that the error is ocurring when calling import("/app/entry.client.tsx");. The script tag that calls import() does have the correct nonce attribute on it, but the error happens anyway. Not sure if this is relevant but just thought I'd mention it.

At the index route, there is a simple counter with a button to increment the count using react state. There is also a script tag below that which is supposed to cause an error, because it is missing a nonce attribute. (Just to prove that CSP is working. Feel free to remove this script if it's getting in the way of debugging.)

If you set v3_singleFetch to false, and then visit the index route, the counter works, and the script without a nonce doesn't, which is expected. If you set v3_singleFetch to true, and then visit the index route, the counter does not work, and an error is shown in the console, which is not expected.

As far as I can tell, the reproduction is following the advice from the docs regarding CSP:

It's possible I missed something though. Any help is greatly appreciated.

lensbart commented 2 hours ago

Hi Reid! I just came here to open an issue with the suggestion to improve the docs.

Your issue can probably be fixed as follows:

The steps above should have already been taken care of prior to enabling v3_singleFetch. With this ~future~ feature flag enabled, you’ll also have to

Additional details are available in the docs

then you’ll find a reference in step 4 of the section Enabling Single Fetch.

Hope this helps!

ReidCole commented 1 hour ago

Looks like I forgot to include the nonce prop on <RemixServer />. I guess I assumed passing the nonce in the 2nd arg of renderToPipeableStream would have been enough, but you have to do it in both places.

Thanks Bart!

It's probably a good idea to open that issue about the docs. It would be great if the docs had all these steps for CSP in one place for easy viewing.