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

Bug: Auto-inserted rel=preload script resources are blocked by nonce-CSP #26781

Closed
ingmarh opened this issue May 5, 2023 · 7 comments
Closed
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@ingmarh
Copy link

ingmarh commented May 5, 2023

In Chrome, auto-inserted rel=preload script resources (via renderToPipeableStream with "nonce" option) are blocked/not preloaded when the page has a CSP with script-src 'nonce-NONCE' set up.

React version: Latest Canary, 18.3.0-canary-aef7ce554-20230503.
As far as I can see, the issue exists since 18.3.0-next-28a574ea8-20221027.
The issue doesn't exist in the latest stable version, 18.2.0, since auto-inserted rel=preload link tags were added later.

Context: We’re currently in the progress of updating React to v18 in a rather big codebase, and tried to use a Canary release for the updated hydration behavior as described in this comment.

Steps to reproduce

  1. Set up a CSP with a nonce, e.g. Content-Security-Policy: script-src 'nonce-NONCE'
  2. Use renderToPipeableStream with the "nonce" option
  3. Open the app in Chrome

The current behavior

React automatically adds rel=preload link tags for resources, including scripts. When using a nonce with a script-src 'nonce-NONCE' Content-Security-Policy, and passing the nonce value to renderToPipeableStream with the "nonce" option, the corresponding auto-inserted <link rel="preload" as="script"> tags don't receive the nonce.

The script resources aren’t preloaded in Chrome because they’re blocked by the CSP (Firefox and Safari preload correctly).

Content-Security-Policy: script-src 'nonce-CSP_NONCE'
import { renderToPipeableStream } from 'react-dom/server';

const { pipe } = renderToPipeableStream(<App />, {
  nonce: CSP_NONCE,
  bootstrapScripts: ['/main.js'],
  onShellReady() {
    response.setHeader('content-type', 'text/html');
    pipe(response);
  }
});

function App() {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="stylesheet" href="/styles.css"></link>
        <title>My app</title>
      </head>
      <body>
         <script src="/script.js" nonce={CSP_NONCE}></script>
      </body>
    </html>
  );

React streams something like the following:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charSet="utf-8" />
    <link rel="preload" as="style" href="/styles.css" />
    <link rel="preload" as="script" href="/script.js" />
    <link rel="stylesheet" href="/styles.css"></link>
    <title>My app</title>
  </head>
  <body>
    <script src="/script.js" nonce="CSP_NONCE"></script>
  </body>
</html>

The expected behavior

When using a nonce value with the renderToPipeableStream "nonce" option, the corresponding auto-inserted <link rel="preload" as="script"> tags include the "nonce" attribute so that the resource can preload in Chrome and is not blocked by a script-src 'nonce-NONCE' Content-Security-Policy.

Related

@ingmarh ingmarh added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label May 5, 2023
@gaearon
Copy link
Collaborator

gaearon commented May 5, 2023

cc @gnoff

@liuyenwei
Copy link
Contributor

I was testing 18.3.0-canary-a389046a5-20230512 and also ran into this same issue.

Just to add onto the use case in the initial post - this also happens for preload links which are generated for React.lazy components.

I'm happy to take a pass at pushing up a PR for this if there is no disagreement that this should be the expected behavior.

@gnoff
Copy link
Collaborator

gnoff commented May 16, 2023

@ingmarh thanks for reporting. yes we should be using the nonce on the preload if it was present on the underlying script.

@liuyenwei if you want to take a pass at putting together a PR that's be great. I'll review. If you don't think you will be able to provide one this week I'll take care of it soon. let me know if you intend to (no pressure either way)

@liuyenwei
Copy link
Contributor

@gnoff sounds good - I can work on that this week 👍

@liuyenwei
Copy link
Contributor

@ingmarh @gnoff #26826

@gaearon
Copy link
Collaborator

gaearon commented May 23, 2023

Closing per #26826 (review).

@gaearon gaearon closed this as completed May 23, 2023
@ingmarh
Copy link
Author

ingmarh commented May 24, 2023

I can confirm that the issue is fixed with the latest canary, 18.3.0-canary-535c038d1-20230522.
Thank you for the fix, @liuyenwei.

@gnoff Thanks for your additional comment #26826 (review). It makes a lot of sense to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug
Projects
None yet
Development

No branches or pull requests

4 participants