Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

CORS policy blocks script access in development #971

Open
1 of 2 tasks
aqiongbei opened this issue Jan 20, 2025 · 20 comments
Open
1 of 2 tasks

CORS policy blocks script access in development #971

aqiongbei opened this issue Jan 20, 2025 · 20 comments

Comments

@aqiongbei
Copy link

Build tool

Vite

Where do you see the problem?

  • In the browser
  • In the terminal

Describe the bug

i create a react crx demo according to this guide Get Started with React, when i run pnpm dev, i found this error info in chrome extension inspect page(chrome://extensions/?errors=gbfkgllkhphjnpfpgfgmpahkbekgeenh)

Image

the service-worker-loader.js's content is

import 'http://localhost:5173/@vite/env';
import 'http://localhost:5173/@crx/client-worker';

my chrome version is 131.0.6778.265 (arm64)

Reproduction

do according to Get Started with React.

Logs

Access to script at 'http://localhost:5173/@crx/client-worker' from origin 'chrome-extension://gbfkgllkhphjnpfpgfgmpahkbekgeenh' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

System Info

System:
    OS: macOS 12.7.6
    CPU: (8) arm64 Apple M1 Pro
    Memory: 121.64 MB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  npmPackages:
    @crxjs/vite-plugin: 2.0.0-beta.30 => 2.0.0-beta.30 
    @eslint/js: ^9.17.0 => 9.18.0 
    @types/react: ^18.3.18 => 18.3.18 
    @types/react-dom: ^18.3.5 => 18.3.5 
    @vitejs/plugin-react: ^4.3.4 => 4.3.4 
    eslint: ^9.17.0 => 9.18.0 
    eslint-plugin-react-hooks: ^5.0.0 => 5.1.0 
    eslint-plugin-react-refresh: ^0.4.16 => 0.4.18 
    globals: ^15.14.0 => 15.14.0 
    react: ^18.3.1 => 18.3.1 
    react-dom: ^18.3.1 => 18.3.1 
    typescript: ~5.6.2 => 5.6.3 
    typescript-eslint: ^8.18.2 => 8.20.0 
    vite: ^6.0.5 => 6.0.9

Severity

annoyance

@aqiongbei aqiongbei changed the title 2.0.0-bate.30 request js has been blocked by CORS policy 2.0.0-bate.30 request background has been blocked by CORS policy Jan 21, 2025
@Maks-Yaremenko
Copy link

Maks-Yaremenko commented Jan 21, 2025

managed to resolve by adding "host_permissions": ["<all_urls>"] to the manifest file, but now I have another issue, when I'm trying to load the extension it refreshes infinitely, and finally, I'm getting the error:

WebSocket connection to 'ws://localhost:5173/' failed: Error during WebSocket handshake: Unexpected response code: 400 

@aqiongbei
Copy link
Author

managed to resolve by adding "host_permissions": ["<all_urls>"] to the manifest file, but now I have another issue, when I'm trying to load the extension it refreshes infinitely, and finally, I'm getting the error:

WebSocket connection to 'ws://localhost:5173/' failed: Error during WebSocket handshake: Unexpected response code: 400 

now, we have another issue.

@aqiongbei aqiongbei reopened this Jan 21, 2025
@liuwilliamBUPT
Copy link

liuwilliamBUPT commented Jan 21, 2025

i just encounter this same issue about websocket and take a lot time to find the reason. luckily, i found it.

because of the update of vite. they add
a new mechanism that used to enhance the security of websocket.

following this link

https://github.com/vitejs/vite/blob/9654348258eaa0883171533a2b74b4e2825f5fb6/packages/vite/src/node/config.ts#L535

* In Vite 6.0.8 and below, WebSocket server was able to connect from any web pages. However,
   * that could be exploited by a malicious web page.
   *
   * In Vite 6.0.9+, the WebSocket server now requires a token to connect from a web page.
   * But this may break some plugins and frameworks that connects to the WebSocket server
   * on their own. Enabling this option will make Vite skip the token check.
   *
   * **We do not recommend enabling this option unless you are sure that you are fine with
   * that security weakness.**

so the resolution is adding the legacy option to your vite config to close token verifying, then the problem solved.

@aqiongbei
Copy link
Author

aqiongbei commented Jan 21, 2025

@liuwilliamBUPT you resolve my problem, thx you bro. You're really amazing!
so guys, add"host_permissions": ["<all_urls>"]to the manifest file and add the follow config in your vite.config.ts will resolve the issue.

legacy: {
    skipWebSocketTokenCheck: true,
}

@jacksteamdev
Copy link
Contributor

jacksteamdev commented Jan 21, 2025

Amazing work @liuwilliamBUPT and @aqiongbei

I'm going to reopen this because this is a significant breaking change in Vite that's affecting Chrome Extension development workflows. If you've updated Vite recently and your extension development suddenly stopped working, this is why.

What's Happening

Starting with these Vite versions:

  • =6.0.9

  • =5.4.12, <6.0.0

  • =4.5.6, <5.0.0

Vite changed default CORS policies in the dev server, which breaks CRXJS's development workflow. While this was done for security reasons, it's causing real issues for extension developers.

Quick Fix

If you need to get back to work immediately, you can add this to your vite.config.js:

export default defineConfig({
  // ... existing config
  legacy: {
    skipWebSocketTokenCheck: true
  }
})

Long-term Solution

CRXJS should update to properly handle Vite's new WebSocket security requirements. Something like this might work:

// vite.config.js
export default defineConfig({
  server: {
    cors: {
      origin: [
        'chrome-extension://',  // Allow all extensions in dev
        // Or for production, specific extension IDs:
        // 'chrome-extension://<your-extension-id>'
      ]
    }
  }
})

Related Links

Would appreciate feedback from extension developers on these solutions, particularly if you're working with different Chrome extension configurations.

@jacksteamdev jacksteamdev reopened this Jan 21, 2025
@jacksteamdev jacksteamdev changed the title 2.0.0-bate.30 request background has been blocked by CORS policy CORS policy blocks script access in development Jan 21, 2025
@jacksteamdev jacksteamdev pinned this issue Jan 21, 2025
@sapphi-red
Copy link

sapphi-red commented Jan 23, 2025

Sorry for the breakage. For the websocket connection, passing the token here would probably work:

const socket = new WebSocket(`${socketProtocol}://${socketHost}`, 'vite-hmr')

In other words, it can probably be fixed by updating this line to new WebSocket(`${socketProtocol}://${socketHost}?token=${token}`, 'vite-hmr'). You can get the token from config.webSocketToken in the configResolved hook.
Ideally, it should not connect to the WebSocket server on its own and instead use import.meta.hot. I assume it's related to that fact that ESM support in extensions are not good. Is that the reason why it's connecting on its own?

For server.cors, I'd recommend adding "host_permissions": ["http://localhost:5173"] to the manifest rather than adding chrome-extension:// to the server.cors.origin. This way you can block requests from extensions without host_permissions.

@nechmads
Copy link

I have the same problem, even after adding the following to the vite.config.ts
legacy: {
skipWebSocketTokenCheck: true
}

It seems that just when I add the "host_permissions": ["<all_urls>"] to the manifest the problem is solved. But obviously, I don't want to ask for this kind of permission. Asking for permissions to all websites get Google to take ages when reviewing every update + it can show a warning to users.

Is there another way to solve this for now?

@vitorgamer58
Copy link

@nechmads I believe it is possible to generate the manifest.json dynamically, where it is possible to configure this permission only when the environment is development, and then not add this line in the manifest.json of the build (production). I saw this in some code where the extension generated the manifest dynamically depending on the target browser and the environment.

Including the background permission itself is an example, you may not need it in production.

And here we are dealing with a problem when connecting to the Vite server to hot reload the extension while we are developing, it is not necessary in production because everything will be in the .js files.

@ch0c0l8ra1n
Copy link

@aqiongbei Use npm init vite@^3.0

crxjs's website says it only supports vite 2 and 3 (3 is beta)

@vitorgamer58
Copy link

vitorgamer58 commented Jan 27, 2025

I kept getting CORS error until I added this in defineConfig of vite.config.js file

  server: {
    cors: {
      origin: '*'
    }
  },

@aqiongbei @jacksteamdev

@JacobGrady
Copy link

JacobGrady commented Jan 28, 2025

Wasted a few hours trying to fix this issue today "Error during WebSocket handshake: Unexpected response code: 400"
luckily came across this thread, and this fixed it! Thank you!

Quick Fix

If you need to get back to work immediately, you can add this to your vite.config.js:

  // ... existing config
  legacy: {
    skipWebSocketTokenCheck: true
  }

@salmin89
Copy link

Long-term Solution

Let us know when the long-term solution is available

@martinbutt
Copy link

It seems that just when I add the "host_permissions": ["<all_urls>"] to the manifest the problem is solved. But obviously, I don't want to ask for this kind of permission. Asking for permissions to all websites get Google to take ages when reviewing every update + it can show a warning to users.

If you are using a manifest.json, this is a less pervasive permission:

  "host_permissions": [
    "http://localhost:5173/*",
    // rest of host_permissions
  ],

If you are using a manifest.config.ts, then you can update the defineManifest function to look like:

export default defineManifest(async (env) => {
    if (env.mode !== 'production') {
        hostPermissions.push("http://localhost:5173/*");
    }

    return {
        manifest_version: 3,
        // rest of manifest

Copy link
Contributor

github-actions bot commented Feb 5, 2025

⚠️ Important Notice: CRXJS is seeking new maintainers.

  • New issues and PRs may not receive immediate attention
  • A new maintenance team must establish itself by March 31, 2025 or this repository will be archived on June 1, 2025
  • Learn more about the transition

This is an automated message. Please do not reply to this comment.

@arjhun
Copy link

arjhun commented Feb 7, 2025

Sorry for the breakage. For the websocket connection, passing the token here would probably work:

chrome-extension-tools/packages/vite-plugin/src/client/es/hmr-client-worker.ts

Line 84 in a03f09b

const socket = new WebSocket(${socketProtocol}://${socketHost}, 'vite-hmr')

In other words, it can probably be fixed by updating this line to new WebSocket(`${socketProtocol}://${socketHost}?token=${token}`, 'vite-hmr'). You can get the token from config.webSocketToken in the configResolved hook.
Ideally, it should not connect to the WebSocket server on its own and instead use import.meta.hot. I assume it's related to that fact that ESM support in extensions are not good. Is that the reason why it's connecting on its own?
For server.cors, I'd recommend adding "host_permissions": ["http://localhost:5173"] to the manifest rather than adding chrome-extension:// to the server.cors.origin. This way you can block requests from extensions without host_permissions.

Can I just say, Thank you Vite Core maintainer @sapphi-red for responding here and pointing maintainers in the right direction... I see little to no response to this very valuable comment...

@ChangerHe
Copy link

i just encounter this same issue about websocket and take a lot time to find the reason. luckily, i found it.

because of the update of vite. they add a new mechanism that used to enhance the security of websocket.

following this link

https://github.com/vitejs/vite/blob/9654348258eaa0883171533a2b74b4e2825f5fb6/packages/vite/src/node/config.ts#L535

* In Vite 6.0.8 and below, WebSocket server was able to connect from any web pages. However,
   * that could be exploited by a malicious web page.
   *
   * In Vite 6.0.9+, the WebSocket server now requires a token to connect from a web page.
   * But this may break some plugins and frameworks that connects to the WebSocket server
   * on their own. Enabling this option will make Vite skip the token check.
   *
   * **We do not recommend enabling this option unless you are sure that you are fine with
   * that security weakness.**

so the resolution is adding the legacy option to your vite config to close token verifying, then the problem solved.

thanks this saved my time

@danny-does-stuff
Copy link

@jacksteamdev it seems like your response above assumes there is only 1 issue, but I believe are 2 issues which would both need to be handled separately.

  1. CORS error - The extension can't access the vite server due to CORS error. Fixed by adding a new origin to server.cors.origin
  2. WebSocket token error - The WebSocket created by CRXjs does not pass the token. Temporarily fixed by setting legacy.skipWebSocketTokenCheck to true. Long Term fix by implementing @sapphi-red's suggestion given above

There are also some suggestions in this thread to add "localhost:5173" to manifest.json, but I don't see why that would be necessary. I don't think we need any updates for a prod build, and the dev fixes can be handled in vite.config.ts.

Is this all correct? I just wanted to make sure I'm understanding properly and that the scope of the issue is well defined. Note that I needed to use the regex version of your suggested origin (/chrome-extension:\/\// instead of "chrome-extension://"). See below.

For others looking for a short term solution, this is what worked for me

  1. Fix CORS error. We need the vite server to accept requests from our chrome extension. Add the origin "chrome-extension://your-dev-extension-id" to allow your extension, or /chrome-extension:\/\// to allow any chrome-extension
  2. Fix WS error. add the option legacy.skipWebSocketTokenCheck: true
  3. That's all. I did not need to update my manifest file

Here's what I added in my vite config.

	server: {
		cors: {
			origin: [
				// ⚠️ SECURITY RISK: Allows any chrome-extension to access the vite server ⚠️
				// See https://github.com/crxjs/chrome-extension-tools/issues/971 for more info
				// I don't believe that the linked issue mentions a potential solution
				/chrome-extension:\/\//,
			],
		},
	},
	legacy: {
		// ⚠️ SECURITY RISK: Allows WebSockets to connect to the vite server without a token check ⚠️
		// See https://github.com/crxjs/chrome-extension-tools/issues/971 for more info
		// The linked issue gives a potential fix that @crxjs/vite-plugin could implement
		skipWebSocketTokenCheck: true,
	},

@danny-does-stuff
Copy link

@martinbutt do you know why it would be necessary to update manifest.json?

@martinbutt
Copy link

During development only, vite.js opens a websocket to http://localhost:5173, which allows it to refresh changes without reloading the entire project.

Browser extensions require a list of all of the places they can open a network connection to in the [manifest.json](http://manifest.json/).

So for the websocket to work when using vite.js to develop browser extensions, it needs to have http://localhost:5173 listed.

If you do this, you don't need to make any other changes.

@danny-does-stuff
Copy link

danny-does-stuff commented Feb 24, 2025

@martinbutt correct me if I'm wrong, but this will only allow the extension to make a connection to the vite server. That doesn't necessarily mean that the vite server will accept that connection? I tested making this change in manifest.json with no changes in vite config, and it didn't resolve the issue. Like I said in my comment, I was able to "fix" the issue by editing the vite config file, nothing else.

Which version of vite are you on? The versions that do not require a change to your vite config are

>=6.0.0, <=6.0.8
>=5.0.0, <=5.4.11
<=4.5.5

Maybe you are on one of these?

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests