Repository: vercel/next.js
Pull request: [test] Account for React's usage of `eval` in dev

With https://github.com/facebook/react/pull/33590, we're starting to hit cases where React uses `eval` in dev more often.

A CSP without `unsafe-eval` hasn't been supported for a while due to React making heavy usage of `eval` to restore stacks from the Server in the Client. 

Changed files:
filename: test/e2e/app-dir/app/index.test.ts
patch: @@ -1814,40 +1814,24 @@ describe('app dir - basic', () => {
       expect($('body').find('script[async]').length).toBe(1)
     })
 
-    // Turbopack doesn't use eval by default, so we can check strict CSP.
-    if (!isNextDev || isTurbopack) {
-      // This test is here to ensure that we don't accidentally turn CSP off
-      // for the prod version.
-      it('should successfully bootstrap even when using CSP', async () => {
-        // This path has a nonce applied in middleware
-        const browser = await next.browser('/bootstrap/with-nonce')
-        const response = await next.fetch('/bootstrap/with-nonce')
-        // We expect this page to response with CSP headers requiring a nonce for scripts
-        expect(response.headers.get('content-security-policy')).toContain(
-          "script-src 'nonce"
-        )
-        // We expect to find the updated text which demonstrates our app
-        // was able to bootstrap successfully (scripts run)
-        expect(
-          await browser.eval('document.getElementById("val").textContent')
-        ).toBe('[[updated]]')
-      })
-    } else {
-      it('should fail to bootstrap when using CSP in Dev due to eval', async () => {
-        const browser = await next.browser('/bootstrap/with-nonce')
-        // We expect our app to fail to bootstrap due to invalid eval use in Dev.
-        // We assert the html is in it's SSR'd state.
-        expect(
-          await browser.eval('document.getElementById("val").textContent')
-        ).toBe('initial')
-
-        const response = await next.fetch('/bootstrap/with-nonce')
-        // We expect this page to response with CSP headers requiring a nonce for scripts
-        expect(response.headers.get('content-security-policy')).toContain(
-          "script-src 'nonce"
-        )
+    // This test is here to ensure that we don't accidentally turn CSP off
+    // for the prod version.
+    it('should successfully bootstrap even when using CSP', async () => {
+      // This path has a nonce applied in middleware
+      const browser = await next.browser('/bootstrap/with-nonce')
+      const response = await next.fetch('/bootstrap/with-nonce')
+      // We expect this page to response with CSP headers requiring a nonce for scripts
+      expect(response.headers.get('content-security-policy')).toEqual(
+        isNextDev
+          ? "script-src 'nonce-my-random-nonce' 'strict-dynamic' 'unsafe-eval';"
+          : "script-src 'nonce-my-random-nonce' 'strict-dynamic';"
+      )
+      // We expect to find the updated text which demonstrates our app
+      // was able to bootstrap successfully (scripts run)
+      await retry(async () => {
+        expect(await browser.elementByCss('#val').text()).toEqual('[[updated]]')
       })
-    }
+    })
   })
 
   // this one comes at the end to not change behavior from above

filename: test/e2e/app-dir/app/middleware.js
patch: @@ -25,13 +25,13 @@ export async function middleware(request) {
     return NextResponse.rewrite(new URL('/dashboard', request.url))
   }
 
-  // In dev this route will fail to bootstrap because webpack uses eval which is dissallowed by
-  // this policy. In production this route will work
   if (request.nextUrl.pathname === '/bootstrap/with-nonce') {
-    const nonce = crypto.randomUUID()
+    // In a real app, crypto.randomUUID() would be used to generate a safe nonce.
+    // React and Webpack use eval() in development mode, so we need to allow it.
+    const csp = `script-src 'nonce-my-random-nonce' 'strict-dynamic'${process.env.NODE_ENV !== 'production' ? " 'unsafe-eval'" : ''};`
     return NextResponse.next({
       headers: {
-        'Content-Security-Policy': `script-src 'nonce-${nonce}' 'strict-dynamic';`,
+        'Content-Security-Policy': csp,
       },
     })
   }