動態

詳情 返回 返回

使用 CRXJS 構建 Chrome 插件在 Chrome 瀏覽器升級到 130xxx 版本之後,出現CSP錯誤 - 動態 詳情

使用 CRXJS 構建 Chrome 插件在 Chrome 瀏覽器升級到 130xxx 版本之後,出現 Content Security Policy 錯誤

一、前言

之前有個老哥找我寫了插件,到現在幾個月過去了,今天早上和我説 Chrome 瀏覽器報錯運行不起來了,但是 edge 瀏覽器沒問題。

就幫忙定位了下問題,發現是 Content Security Policy 的問題導致的報錯;

老哥説最近沒改動這些代碼,我就要了下壓縮文件,在自己的 chrome 瀏覽器上安裝,發現沒問題,可以正常運行也沒有報錯;

我就把我本地 chrome 瀏覽器版本發過去和老哥的瀏覽器版本對比下,發現他的瀏覽器版本是最新版的(自動更新到最新版了,老哥不知道),我也手動更新我的瀏覽器到最新版(版本 130.0.6723.59(正式版本) (arm64)),就也報錯了....

二、報錯內容

1. 錯誤信息

Refused to load the script 'chrome-extension://1b3524a5-1c44-410c-9c6b-3e806a789826/js/index.ts.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.

在這裏插入圖片描述

2. 報錯位置

3. 報錯原因

  1. chrome 瀏覽器升級最新版
  2. 使用 crxjs 打包插件
  3. 相關 Content Security Policy 報錯

三、解決方案

1. 修改 manifest.json 文件

把 web_accessible_resources 中的 use_dynamic_url 改為 false

整個 web_accessible_resources 只改動 use_dynamic_url 一個字段

"web_accessible_resources": [
    {
      "resources": ["coverage/index.html", "content/index.html", "assets/*", "js/*"],
      "matches": ["http://localhost:*/*"],
      "use_dynamic_url": false // 只改動這一行即可,把 true 改成 false
    }
  ]

2. 增加 chalk 和 gulp 包

pnpm i gulp chalk -D 

3. 在根目錄增加 gulpfile.js 文件

.
├── gulpfile.js
import { createRequire } from 'module'
import fs from 'fs'
import path from 'path'
import { dirname } from 'path'
import { fileURLToPath } from 'url'

// Temp fix for CSP on Chrome 130
// Manually remove them because there is no option to disable use_dynamic_url on @crxjs/vite-plugin
function forceDisableUseDynamicUrl(done) {
  const require = createRequire(import.meta.url)
  const __filename = fileURLToPath(import.meta.url)
  const __dirname = dirname(__filename)
  const manifestPath = path.join(__dirname, 'dist', 'manifest.json')

  const manifest = require(manifestPath)

  manifest.web_accessible_resources.forEach((resource) => {
    delete resource.use_dynamic_url
  })

  if (!fs.existsSync(manifestPath)) {
    return done()
  }

  fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))

  done()
}

export { forceDisableUseDynamicUrl }

4. 在根目錄增加 vite-plugins/vite-plugin-run-command-on-demand.ts 文件

在根目錄增加 vite-plugins 文件夾和 vite-plugin-run-command-on-demand.ts 文件

.
├── vite-plugins
│   └── vite-plugin-run-command-on-demand.ts
import chalk from "chalk";
import { exec } from "child_process";
import { HmrContext, Plugin } from "vite";

const pluginName = "vite-plugin-run-command-on-demand";

const log = (message: string) =>
  console.log(chalk.blue(`\n[${pluginName}]`), chalk.green(message));
const logError = (message: string) =>
  console.error(chalk.blue(`\n[${pluginName}]`), chalk.red(message));

const runCommand = (command: string): Promise<void> =>
  new Promise((resolve, reject) => {
    exec(command, (error, stdout, stderr) => {
      if (error) {
        logError(`Error executing command: ${command}\n${stderr}`);
        reject(error);
      } else {
        log(`Command executed successfully: ${command}\n${stdout}`);
        resolve();
      }
    });
  });

type CustomCommandsPluginOptions = {
  beforeServerStart?: string;
  afterServerStart?: string;
  onHotUpdate?: string;
  beforeBuild?: string;
  afterBuild?: string;
  closeBundle?: string;
};

const executeCommand = async (
  command: string | undefined,
  errorMessage: string,
) => {
  if (command) {
    try {
      await runCommand(command);
    } catch {
      logError(errorMessage);
    }
  }
};

export default function customCommandsPlugin(
  options: CustomCommandsPluginOptions = {},
): Plugin {
  return {
    name: pluginName,
    configureServer(server) {
      server.httpServer?.once("listening", async () => {
        await executeCommand(
          options.beforeServerStart,
          `Error running beforeServerStart command: ${options.beforeServerStart}`,
        );
        await executeCommand(
          options.afterServerStart,
          `Error running afterServerStart command: ${options.afterServerStart}`,
        );
      });
    },

    async handleHotUpdate(ctx: HmrContext) {
      const isPageReload = ctx.modules.some(
        (module) => !module.isSelfAccepting,
      );
      if (!isPageReload) {
        await executeCommand(
          options.onHotUpdate,
          `Error running onHotUpdate command: ${options.onHotUpdate}`,
        );
      }
      return ctx.modules;
    },

    async buildStart() {
      await executeCommand(
        options.beforeBuild,
        `Error running beforeBuild command: ${options.beforeBuild}`,
      );
    },

    async buildEnd() {
      await executeCommand(
        options.afterBuild,
        `Error running afterBuild command: ${options.afterBuild}`,
      );
    },

    async closeBundle() {
      await executeCommand(
        options.closeBundle,
        `Error running closeBundle command: ${options.closeBundle}`,
      );
    },
  };
}

5. vite.config.ts 中引入

引入上面新增的文件

import vitePluginRunCommandOnDemand from "./vite-plugins/vite-plugin-run-command-on-demand";

在 plugins 中使用

plugins: [
  vitePluginRunCommandOnDemand({
    afterServerStart: "pnpm gulp forceDisableUseDynamicUrl",
    closeBundle: "pnpm gulp forceDisableUseDynamicUrl",
  }),
]

6. 修改 tsconfig.node.json 文件

"include": ["vite.config.ts", "./vite-plugins/**/*.ts"]

7. 重新構建打包

pnpm run build

四、方案執行結果

五、總結

  • 此次報錯是由於 chrome 瀏覽器升級之後,安全策略變更導致的;
  • 使用 CRX JS 打包 chrome 插件都會遇到這個報錯,已經有老哥在 crxjs 的 github 上提交 issue 了;
  • 此次解決方案也是從這個 issue 上面找的;
  • 之前做過 chrome 瀏覽器版本發行説明,但是後面有事就耽擱了,現在覺得還是得提起來,這樣能有效跟進版本迭代和一些坑,不至於出現問題手忙腳亂。

引用

  • chrome 瀏覽器版本發行説明
  • crxjs issues
  • ce9242a
user avatar u_17443142 頭像 steven_code 頭像 gssggssg 頭像 wqjiao 頭像 magnesium_5a22722d4b625 頭像 shixiansheng_67ea5ae9c45b7 頭像 wx709294 頭像 gfeteam 頭像 ifat3 頭像 yangge_5c6804373b5a0 頭像 xianglvxingdexianrenqiu 頭像 linyuyizhizou_678b9fdc436f1 頭像
點贊 13 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.