chrome-extension-tools icon indicating copy to clipboard operation
chrome-extension-tools copied to clipboard

CSP Issue on Chrome 130+

Open sebastienfontaine opened this issue 1 year ago • 46 comments

Build tool

Vite

Where do you see the problem?

  • [X] In the browser
  • [ ] In the terminal

Describe the bug

When using Chrome version 130 and above, a browser error occurs when loading the content.js script.

content.ts-loader-DE-4zCml.js:

Refused to load the script 'chrome-extension://65fbf486-37dc-4a71-a17e-a567b680778b/assets/content.ts-B_w-r5D-.js' because it violates the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:*". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
TypeError: Failed to fetch dynamically imported module: chrome-extension://65fbf486-37dc-4a71-a17e-a567b680778b/assets/content.ts-B_w-r5D-.js
image

Reproduction

Clone this repository: https://github.com/furybee/crxjs-extension, or create a new repository with a content.js file as content_scripts.

Run the following command:

npm run build

Import the extension into Chrome 130+ (Canary Builds): https://www.google.com/chrome/canary/

Check the console to see the error.

Logs

No response

System Info

System:
    OS: macOS 14.5
    CPU: (10) arm64 Apple M2 Pro
    Memory: 77.75 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.12.2 - ~/.nvm/versions/node/v20.12.2/bin/node
    Yarn: 1.22.22 - ~/.yarn/bin/yarn
    npm: 10.5.0 - ~/.nvm/versions/node/v20.12.2/bin/npm
    Watchman: 2024.05.06.00 - /opt/homebrew/bin/watchman
  Browsers:
    Chrome: 128.0.6613.138
    Chrome Canary: 130.0.6719.0
    Edge: 128.0.2739.79
    Safari: 17.5
  npmPackages:
    @crxjs/vite-plugin: ^2.0.0-beta.25 => 2.0.0-beta.25 
    vite: ^5.4.1 => 5.4.5

manifest.json

{
  "manifest_version": 3,
  "name": "CRXJS Vue Vite Example",
  "version": "1.0.0",
  "action": { "default_popup": "index.html" },
  "content_scripts": [{
    "matches": [ "*://*/*" ],
    "js": [
      "src/content.ts"
    ]
  }]
}

Severity

🚨 blocking all usage of crxjs 🚨

sebastienfontaine avatar Sep 15 '24 10:09 sebastienfontaine

Same 🚨

My solution for build production

remove chrome.runtime.getURL in content script

(function () {
  'use strict';

  (async () => {
    await import("./chunk-b674a675.js");
  })().catch(console.error);

})();

trungpv1601 avatar Sep 16 '24 01:09 trungpv1601

Having the same issue as well.,

matteing avatar Sep 16 '24 18:09 matteing

I worked around this by setting use_dynamic_url to false. But even if you don't use use_dynamic_url in manifest.ts, the compiled manifest.json will still have it, and crxjs doesn't have the option to disable it. So one might try to write a Vite plugin to forcefully remove them from the compiled manifest.json.

pnd280 avatar Sep 20 '24 16:09 pnd280

It's not only about this CSP issue; it's always been frustrating to work with CRXJS. The docs are the worst I've ever seen. The dev runtime doesn't even work on Firefox. For my project, it has become a debt now. I want to move away from it, but I can't find an alternative that supports HMR for integrated CSUIs (not iframe). I've tried out wxt and plasmo but had no luck. I would much appreciate if someone could recommend alternatives.

pnd280 avatar Sep 21 '24 04:09 pnd280

It's not only about this CSP issue; it's always been frustrating to work with CRXJS. The docs are the worst I've ever seen. The dev runtime doesn't even work on Firefox. For my project, it has become a debt now. I want to move away from it, but I can't find an alternative that supports HMR for integrated CSUIs (not iframe). I've tried out wxt and plasmo but had no luck. I would much appreciate if someone could recommend alternatives.

Same idea with you but not found the best one 👨‍💻

Firefox does not support MV3 yet. So for me, only focus on Chrome.

Still using this hot-fix for v130.

https://github.com/crxjs/chrome-extension-tools/issues/918#issuecomment-2351900637

trungpv1601 avatar Sep 21 '24 04:09 trungpv1601

It's not only about this CSP issue; it's always been frustrating to work with CRXJS. The docs are the worst I've ever seen. The dev runtime doesn't even work on Firefox. For my project, it has become a debt now. I want to move away from it, but I can't find an alternative that supports HMR for integrated CSUIs (not iframe). I've tried out wxt and plasmo but had no luck. I would much appreciate if someone could recommend alternatives.

Same idea with you but not found the best one 👨‍💻

Firefox does not support MV3 yet. So for me, only focus on Chrome.

Still using this hot-fix for v130.

#918 (comment)

I think it supports it now. I've ported my MV3 extension to firefox with almost no changes.

pietrofxq avatar Sep 24 '24 00:09 pietrofxq

It's not only about this CSP issue; it's always been frustrating to work with CRXJS. The docs are the worst I've ever seen. The dev runtime doesn't even work on Firefox. For my project, it has become a debt now. I want to move away from it, but I can't find an alternative that supports HMR for integrated CSUIs (not iframe). I've tried out wxt and plasmo but had no luck. I would much appreciate if someone could recommend alternatives.

Same idea with you but not found the best one 👨‍💻 Firefox does not support MV3 yet. So for me, only focus on Chrome. Still using this hot-fix for v130. #918 (comment)

I think it supports it now. I've ported my MV3 extension to firefox with almost no changes.

Nice 😊. Thank you for your information. I will take a look

trungpv1601 avatar Sep 24 '24 02:09 trungpv1601

This seems like a big deal. I've just got my first complaint about my extension not working (a user has Chrome Beta). Took me forever to find that this was the problem. Seems like this will impact every extension soon or am I missing something?

I don't think the workaround posted earlier (https://github.com/crxjs/chrome-extension-tools/issues/918#issuecomment-2351900637) will work for me. I need to call chrome.runtime.getURL in my content script.

scisley avatar Sep 25 '24 23:09 scisley

This seems like a big deal. I've just got my first complaint about my extension not working (a user has Chrome Beta). Took me forever to find that this was the problem. Seems like this will impact every extension soon or am I missing something?

I don't think the workaround posted earlier (#918 (comment)) will work for me. I need to call chrome.runtime.getURL in my content script.

Can you show example in your code? I will help

trungpv1601 avatar Sep 26 '24 00:09 trungpv1601

This seems like a big deal. I've just got my first complaint about my extension not working (a user has Chrome Beta). Took me forever to find that this was the problem. Seems like this will impact every extension soon or am I missing something?

I don't think the workaround posted earlier (#918 (comment)) will work for me. I need to call chrome.runtime.getURL in my content script.

here's how I worked around this issue https://github.com/pnd280/complexity/issues/7

pnd280 avatar Sep 26 '24 02:09 pnd280

This seems like a big deal. I've just got my first complaint about my extension not working (a user has Chrome Beta). Took me forever to find that this was the problem. Seems like this will impact every extension soon or am I missing something?

I don't think the workaround posted earlier (#918 (comment)) will work for me. I need to call chrome.runtime.getURL in my content script.

More attachment on how to hot-fix my extensions

After building, you should edit it like this. I hope it helps.

CleanShot 2024-09-26 at 09 27 20@2x

trungpv1601 avatar Sep 26 '24 02:09 trungpv1601

@trungpv1601, @pnd280 - thank you both! The extra details allowed me to piece together a solution.

scisley avatar Sep 26 '24 16:09 scisley

Are we able prepare a PR for that change or more work is needed?

Toumash avatar Sep 27 '24 14:09 Toumash

This seems like a big deal. I've just got my first complaint about my extension not working (a user has Chrome Beta). Took me forever to find that this was the problem. Seems like this will impact every extension soon or am I missing something? I don't think the workaround posted earlier (#918 (comment)) will work for me. I need to call chrome.runtime.getURL in my content script.

here's how I worked around this issue pnd280/complexity#7

I think this is the best solution for now. It works well for Chrome and Firefox builds.

Thank you @pnd280 🙏

trungpv1601 avatar Sep 28 '24 00:09 trungpv1601

Same problem, we use our extension with InboxSDK to insert some buttons in the gmail page. I was able to temporary fix the extension by changing the "use_dynamic_url" flag in the builded manifest to false, but we hope in a more stable solution

Lonolf avatar Sep 30 '24 13:09 Lonolf

Can we expect a PR to handle that ? Is someone already on that ?

quentin-decre avatar Oct 16 '24 10:10 quentin-decre

is this an issue with chrome that needs to be reported? how come use_dynamic_url became a problem all of a sudden?

salmin89 avatar Oct 16 '24 13:10 salmin89

is this an issue with chrome that needs to be reported? how come use_dynamic_url became a problem all of a sudden?

Looks like support for that property was actually just added in Chrome 130:

https://developer.chrome.com/blog/extension-news-october-2024

Starting in Chrome 130, we will enable support for the use_dynamic_url property on entries under the web_accessible_resources key in the manifest.

I don't know why CRXJS marks it as true in the final manifest.json, looks like a simple PR that removes it would be enough?

pietrofxq avatar Oct 16 '24 14:10 pietrofxq

FIX WITH PATCH-PACKAGE (DEV AND BUILD)

Add patch-package:

yarn add --dev patch-package

Change the use_dynamic_url flag to false in node_modules/@crxjs/vite-plugin/dist/index.mjs in lines 1842 and 1924:

use_dynamic_url: false

Run patch-package:

npx patch-package @crxjs/vite-plugin

Add patch-package to postinstall script in package.json:

"postinstall": "patch-package"

This fixes the issue at the source so it will work correctly in both dev and prod builds

PS: If you are using yarn 3, this can be fixed with yarn patch instead of patch-package

FIX WITH NODEJS SCRIPT (BUILD ONLY)

create a js script, e.g set-dynamic-url.cjs. Update the path of manifest if needed to reflect your folder structure

const fs = require('node:fs')
const path = require('node:path')
const manifest = require('../dist/manifest.json')

const webAccessibleResources = manifest.web_accessible_resources

const updatedWebAccessibleResources = webAccessibleResources.map(resource => {
  if (resource.use_dynamic_url) {
    return {
      ...resource,
      use_dynamic_url: false,
    }
  }
  return resource
})

manifest.web_accessible_resources = updatedWebAccessibleResources

const json = JSON.stringify(manifest, null, 2)
fs.writeFileSync(path.resolve(__dirname, '../dist/manifest.json'), json, 'utf8')

Add it to postbuild under package.json scripts:

"build": "vite build",
"postbuild": "node ./scripts/set-dynamic-url.cjs",

Now the manifest.json in the dist folder will be automatically patched when running yarn build

pietrofxq avatar Oct 16 '24 14:10 pietrofxq

For now, the production build works using @pnd280 's solution (thank you), dev mode is still an issue, did anyone get it to work yet?

scarletczen avatar Oct 16 '24 15:10 scarletczen

I threw together this hack plugin that uses fs.watchFile to update manifest.json in dev mode (works normally for build) I'm no expert on vite-plugins so feel free to modify or share something more robust.

import path from 'node:path';
import fs from 'fs';
import { PluginOption } from 'vite';
import { ManifestV3Export } from '@crxjs/vite-plugin';

const manifestPath = path.resolve('dist/manifest.json'); // your manifest output location

export function updateManifestPlugin(): PluginOption {
  return {
    name: 'update-manifest-plugin',
    enforce: 'post',
    closeBundle() {
      forceDisableUseDynamicUrl();
    },

    configureServer(server) {
      server.httpServer?.once('listening', () => {
        const updated = forceDisableUseDynamicUrl();
        if (updated) {
          server.ws.send({ type: 'full-reload' });
          console.log('** updated **');
        }

        fs.watchFile(manifestPath, (data) => {
          console.log('** watchFile ** ');
          const manifestContents = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
          if (manifestContents.web_accessible_resources.some((resource: any) => resource.use_dynamic_url)) {
            const updated = forceDisableUseDynamicUrl();
            if (updated) {
              server.ws.send({ type: 'full-reload' });
              console.log('** updated **');
            }
          }
        });
      });
    },

    writeBundle() {
      console.log('### writeBundle ##');
      forceDisableUseDynamicUrl();
    },
  };
}

function forceDisableUseDynamicUrl() {
  if (!fs.existsSync(manifestPath)) {
    return false;
  }

  const manifestContents = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as Awaited<ManifestV3Export>;

  if (typeof manifestContents === 'function' || !manifestContents.web_accessible_resources) return false;
  if (manifestContents.web_accessible_resources.every((resource) => !resource.use_dynamic_url)) return false;

  manifestContents.web_accessible_resources.forEach((resource) => {
    if (resource.use_dynamic_url) resource.use_dynamic_url = false;
  });

  fs.writeFileSync(manifestPath, JSON.stringify(manifestContents, null, 2));
  return true;
}

use normally in vite.config.ts

    plugins: [react(), crx({ manifest }), updateManifestPlugin()],

salmin89 avatar Oct 16 '24 16:10 salmin89

my solution above with patch-package fixes it for dev and build modes

pietrofxq avatar Oct 16 '24 16:10 pietrofxq

my solution above with patch-package fixes it for dev and build modes

I'm not using yarn tho

salmin89 avatar Oct 16 '24 16:10 salmin89

my solution above with patch-package fixes it for dev and build modes

I'm not using yarn tho

yarn is not required, patch-package is a npm package. Just change it to

npm install --save-dev patch-package

pietrofxq avatar Oct 16 '24 16:10 pietrofxq

I will test that approach out. Thanks!

salmin89 avatar Oct 16 '24 16:10 salmin89

An example to patch it with pnpm: https://github.com/xc2/follow-it-later/pull/22

xc2 avatar Oct 16 '24 17:10 xc2

The chromium team were aware of this issue (see comment https://issues.chromium.org/issues/363027634#comment4)

And someone even mentioned vite-plugin-web-extension being affected which seems to have been patched quickly by the author.

CrxJs seems unmaintained. No update since 2022 (which is a shame, since it's awesome) https://github.com/crxjs/chrome-extension-tools/discussions/872


Change the use_dynamic_url flag to false in node_modules/@crxjs/vite-plugin/dist/index.mjs in lines 1842 and 1924:

@pietrofxq if you want to make a PR with the fix above, we can all thumbs up it. If you aren't going to, let us know and I can do it.

salmin89 avatar Oct 16 '24 18:10 salmin89

Last version was released on July, but it was a beta version of version 2 (which is the one I am using): https://github.com/crxjs/chrome-extension-tools/releases/tag/%40crxjs%2Fvite-plugin%402.0.0-beta.25

not sure who to ping about it. I can create a PR to change the default flag to false

pietrofxq avatar Oct 16 '24 18:10 pietrofxq

besides setting the default to false, I believe it should be also fixed to respect what is set on manifest.json. Looks like the value set there is completely ignored currently.

pietrofxq avatar Oct 16 '24 18:10 pietrofxq

Last version was released on July, but it was a beta version of version 2 (which is the one I am using): https://github.com/crxjs/chrome-extension-tools/releases/tag/%40crxjs%2Fvite-plugin%402.0.0-beta.25

not sure who to ping about it. I can create a PR to change the default flag to false

I guess lets ping jacksteamdev with the PR and he will wake up to merge it :D

Toumash avatar Oct 16 '24 20:10 Toumash