使用 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. 報錯原因
- chrome 瀏覽器升級最新版
- 使用 crxjs 打包插件
- 相關 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