使用 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 浏览器版本发行说明,但是后面有事就耽搁了,现在觉得还是得提起来,这样能有效跟进版本迭代和一些坑,不至于出现问题手忙脚乱。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。