远程托管代码 (RHC) 是 Chrome 应用商店对以下内容的称呼:由浏览器执行的、从扩展程序自身文件以外的位置加载的任何内容。例如 JavaScript 和 WASM。它不包含数据或 JSON/CSS 等内容。
为什么不再允许使用 RHC?
现在,Manifest V3 扩展程序需要将它们使用的所有代码捆绑在扩展程序本身中。过去,您可以从网络上的任何网址动态注入脚本标记。
我被告知我的扩展程序具有 RHC。这是怎么回事?
如果您的扩展程序在审核期间因 Blue Argon 错误而被拒绝,则表示我们的审核人员认为您的扩展程序正在使用远程托管的代码。这通常是扩展程序尝试添加包含远程资源(即来自开放式网络,而不是扩展程序中包含的文件)的脚本标记,或提取资源以直接执行的结果。
如何识别 RHC
一旦您知道要寻找什么,发现 RHC 就不会特别困难。首先,检查项目中是否存在“http://”或“https://”字符串。如果您存在 RHC 违规行为,那么您很可能可以通过查找该违规行为来找到这些内容。如果您有完整的 build 系统,或者使用来自 npm 或其他第三方来源的依赖项,请确保您搜索的是代码的已编译版本,因为商店会评估该版本。如果您仍然无法找到问题,下一步是与 One Stop 支持团队联系。他们将能够说明具体违规情况,以及尽快发布扩展程序所需采取的措施。
如果某个库请求获取代码,该怎么办
无论代码来自何处,都不允许包含 RHC。这包括您未编写但恰好在项目中用作依赖项的代码。部分使用 Firebase 的开发者在包含远程代码以用于 Firebase Auth 时遇到了此问题。即使这是一个第一方(即 Google 拥有的)库,RHC 也不会获得任何例外情况。您需要配置代码以移除 RHC,或者更新项目以从一开始就不包含该代码。如果您遇到不是您的代码在加载 RHC,而是您使用的库在加载 RHC 的问题,那么最好的做法是联系该库的作者。告知他们发生了这种情况,并要求他们提供解决方法或代码更新来移除该问题。
如果您无法等待库更新
有些库在收到通知后会立即发布更新,但有些库可能会被弃用,或者需要一段时间才能解决问题。根据具体违规行为的严重程度,您可能无需等待违规行为被移除即可完成审核并成功解除封禁。您可以选择多种方案来快速恢复正常运行。
审核代码
您确定需要导致此请求的代码吗?如果可以删除该代码,或者可以移除导致该问题的库,则删除该代码,问题即可解决。
或者,是否有其他库提供相同的功能?您可以尝试在 npmjs.com、GitHub 或其他网站上查找可满足相同使用情形的其他选项。
摇树
如果导致 RHC 违规的代码实际上并未被使用,则工具可能会自动将其删除。webpack、Rollup 和 Vite 等现代构建工具(仅举几例)都具有一项称为“tree-shaking”的功能。在构建系统上启用后,摇树优化应该会移除所有未使用的代码路径。这意味着,您不仅可以获得更合规的代码版本,还可以获得更精简、更快速的版本!请务必注意,并非所有库都能进行树状结构抖动,但许多库都可以。某些工具(例如 Rollup 和 Vite)默认启用 tree-shaking。webpack 需要进行配置才能启用 tree-shaking。如果您没有将构建系统作为扩展程序的一部分使用,但确实在使用代码库,那么我们强烈建议您考虑在工作流程中添加构建工具。构建工具可帮助您编写更安全、更可靠且更易于维护的项目。
具体如何实现树状结构抖动取决于您的具体项目。不过,以 Rollup 为例,您只需编译项目代码即可添加树状结构分析。例如,如果您有一个仅登录到 Firebase Auth 的文件(名为 main.js):
import { GoogleAuthProvider, initializeAuth } from "firebase/auth"; browser.identity.getAuthToken({ 'interactive': true }, async (token) => { const credential = GoogleAuthProvider.credential(null, token); try { const app = initializeApp({ ... }); const auth = initializeAuth(app, { popupRedirectResolver: undefined, persistence: indexDBLocalPersistence }); const { user } = await auth.signInWithCredential(credential) console.log(user) } catch (e) { console.error(error); } });
然后,您只需告知 Rollup 输入文件、加载节点文件所需的插件 @rollup/plugin-node-resolve,以及它要生成的输出文件的名称。
npx rollup --input main.js --plugin '@rollup/plugin-node-resolve' --file compiled.js
在终端窗口中运行该命令后,您将收到生成的 main.js 文件版本,所有内容都已编译到名为 compiled.js 的单个文件中。
汇总可以很简单,但也可以非常灵活地配置。您可以添加各种复杂的逻辑和配置,只需查看其文档即可。 添加此类 build 工具可生成更小、更高效的代码,并且在本例中,还可修正远程托管代码问题。
自动修改文件
远程托管的代码进入代码库的一种越来越常见的方式是作为您包含的库的子依赖项。如果库 X 想从 CDN 中获取库 Y,那么您仍需更新它,使其从本地来源加载。import借助现代构建系统,您可以轻松创建插件来提取远程引用,并将其直接内嵌到代码中。
这意味着,如果给定如下所示的代码:
import moment from "https://unpkg.com/moment@2.29.4/moment.js" console.log(moment())
您可以制作一个小型汇总插件。
import { existsSync } from 'fs'; import fetch from 'node-fetch'; export default { plugins: [{ load: async function transform(id, options, outputOptions) { // this code runs over all of out javascript, so we check every import // to see if it resolves as a local file, if that fails, we grab it from // the network using fetch, and return the contents of that file directly inline if (!existsSync(id)) { const response = await fetch(id); const code = await response.text(); return code } return null } }] };
使用新插件运行 build 后,系统会发现每个远程 import 网址,无论该网址是我们的代码、子依赖项、子子依赖项还是其他任何位置。
npx rollup --input main.js --config ./rollup.config.mjs --file compiled.js
手动修改文件
最简单的做法是直接删除导致 RHC 的代码。在您选择的文本编辑器中打开该文件,然后删除违规行。这通常并不建议,因为这种方式不够灵活,而且很容易忘记。如果名为“library.min.js”的文件实际上不是 library.min.js,则会使项目维护更加困难。与编辑原始文件相比,使用 patch-package 等工具是一种更易于维护的替代方案。这是一个非常强大的选项,可让您保存对文件的修改,而不是文件本身。它基于补丁文件构建,与 Git 或 Subversion 等版本控制系统所用的文件类型相同。您只需手动修改违规代码,保存差异文件,然后使用您要应用的更改来配置 patch-package。您可以在项目的 README 中阅读完整教程。如果您要修补某个项目,我们强烈建议您与该项目联系,请求在上游进行更改。虽然 patch-package 可以让补丁管理变得更加轻松,但最好还是不要有任何需要修补的内容。
如果代码未被使用,该怎么办
随着代码库不断发展壮大,依赖项(或依赖项的依赖项,或依赖项的依赖项的依赖项…)可能会保留不再使用的代码路径。如果其中一个部分包含用于加载或执行 RHC 的代码,则必须将其移除。无论该设备是否已停用或未使用,都无关紧要。如果未使用,则应通过树状结构分析或修补库来移除它。
是否有任何解决方法?
一般来说,不可以。不允许使用 RHC。不过,在少数情况下,是允许的。在这些情况下,几乎不可能采用任何其他选项。
User Scripts API
用户脚本是通常由用户提供的小段代码,适用于 TamperMonkey 和 Violentmonkey 等用户脚本管理器。这些管理器无法捆绑用户编写的代码,因此 User Script API 提供了一种执行用户提供的代码的方式。这不是 browser.scripting.executeScript 或其他代码执行环境的替代方案。用户必须启用开发者模式才能执行任何操作。如果 Chrome 应用商店审核团队认为此功能的使用方式与预期用途(即用户提供的代码)不符,则可能会拒绝该应用或从商店中下架其商品详情。
browser.debugger
借助 browser.debugger API,扩展程序可以与 Chrome Devtools Protocol 进行交互。此协议与 Chrome 的开发者工具以及许多其他工具所用的协议相同。借助此 API,扩展程序可以请求并执行远程代码。与用户脚本一样,它不能替代 browser.scripting,但可提供更出色的用户体验。
使用时,用户会在窗口顶部看到警告栏。如果关闭或关闭横幅,调试会话将终止。
沙盒化 iframe
如果您需要将字符串评估为代码,并且处于 DOM 环境中(例如内容脚本,而不是扩展程序 Service Worker),那么另一种选择是使用沙盒 iframe。出于安全考虑,扩展程序默认不支持 eval() 等功能。恶意代码可能会给用户安全带来风险。但如果代码仅在已知的安全环境中执行(例如已与网络其余部分隔离开的沙盒 iframe),则这些风险会大大降低。在此上下文中,可以解除阻止使用 eval 的内容安全政策,从而允许您运行任何有效的 JavaScript 代码。
如果您有未涵盖的用例,欢迎使用 chromium-extensions 邮寄名单与团队联系以获取反馈,也可以提交新工单,向 One Stop Support 寻求指导
如果您对判决结果有异议,该怎么办
政策的执行可能比较细致,审核也需要人工输入,这意味着 Chrome 应用商店团队有时可能会同意更改审核决定。如果您认为审核有误,可以使用一站式支持服务就拒绝决定提出申诉