处理远程托管代码违规行为

远程托管代码 (RHC) 是 Chrome 应用商店对浏览器执行的任何内容的称呼,这些内容是从扩展程序自身文件以外的其他位置加载的。例如 JavaScript 和 WASM。 __但不包括数据或 JSON 或 CSS 等内容。

为什么不再允许使用 RHC?

现在,使用 Manifest V3 的扩展程序需要将所使用的 所有 代码捆绑到扩展程序本身中。过去,您可以从网络上的任何网址动态注入脚本标记。

我被告知我的扩展程序包含 RHC。这是怎么回事?

如果您的扩展程序在审核期间因 Blue Argon 错误而被拒绝,则 我们的审核人员认为您的扩展程序正在使用远程托管代码。这通常是由于扩展程序尝试添加带有远程 资源(即来自开放式网络,而不是扩展程序中包含的文件)的脚本标记,或提取资源以直接执行而导致的。

如何发现 RHC

一旦您知道要查找的内容,发现 RHC 并不难。首先,检查您的项目中是否存在字符串“http://”或“https://”。如果您违反了 RHC 政策,则很可能可以通过查找这些字符串来找到违规代码。如果您有完整的构建系统,或者使用来自 npm 或其他第三方来源的依赖项,请确保搜索代码的 已编译 版本,因为这是应用商店评估的内容。如果您仍然无法 找到问题,则下一步是与 One Stop Support 联系。他们将能够概述具体违规行为,以及尽快发布扩展程序所需的内容。

如果库请求代码,该怎么办

无论代码来自何处,都不允许包含 RHC。这包括您并非自行编写,而只是在项目中用作依赖项的代码。一些开发者在使用 Firebase 时遇到了此问题,当时正在纳入远程代码以供 Firebase Auth 使用。即使这是一个第一方(即 Google 拥有的)库,RHC 也没有例外情况。您需要配置代码以移除 RHC,或者更新项目以从一开始就不包含该代码。如果您遇到问题,发现加载 RHC 的不是 您的 代码,而是您使用的库,那么最好的做法是与库的作者联系。让他们知道发生了这种情况,并要求他们提供解决方法或代码更新以移除 RHC。

如果您无法等待库更新,该怎么办

有些库会在收到通知后几乎立即发布更新,但有些库可能会被废弃,或者需要一段时间才能解决问题。根据具体违规行为中发生的 情况 ,您可能不需要等待他们采取行动,即可解除封锁并成功完成审核。您可以使用多种选项来快速恢复运行。

审核代码

确定 需要导致请求的代码吗?如果只需删除该代码,或者移除导致该代码的库,那么删除该代码即可完成任务。

或者,是否有其他库提供相同的功能?尝试 查看 npmjs.com、GitHub 或其他网站,了解是否有其他选项可以满足 相同的使用场景。

摇树(优化)

如果导致 RHC 违规的代码实际上未使用,则工具可能会自动删除该代码。现代构建工具(例如 webpackRollupVite,仅举几例)具有一项称为 “摇树 (优化)”的功能。在构建系统上启用后,摇树(优化)应会移除所有未使用的代码路径。这可能意味着您不仅拥有更合规的代码版本,而且还拥有更精简、更快速的版本!请务必注意,并非所有库都能够进行摇树(优化),但许多库都可以。某些 工具(例如 Rollup 和 Vite)默认启用摇树(优化)。您需要配置 webpack 才能启用摇树(优化)。如果您没有将构建系统作为扩展程序的一部分使用,但 确实 使用了代码库,那么我们强烈建议您考虑将构建工具添加到工作流中。构建工具可帮助您编写更安全、更可靠且更易于维护的项目。

如何实现摇树(优化)的具体细节取决于您的具体项目。 但以 Rollup 为例,您只需编译项目代码即可添加摇树(优化)。例如,如果您有一个仅登录 Firebase Auth 的文件,名为 main.js:

import { GoogleAuthProvider, initializeAuth } from "firebase/auth";

chrome.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 的文件中。

Rollup 可能很简单,但它也 非常 可配置。您可以添加各种 复杂的逻辑和配置,只需查看其文档即可。 添加此类构建工具将生成更小、更高效的代码,在这种情况下,还可以解决远程托管代码问题。

自动修改文件

远程托管代码进入代码库的一种越来越常见的方式是作为您包含的库的子依赖项。如果库 X 想要从 CDN importY,您仍然需要更新它,使其从本地来源加载。借助现代构建系统,您可以轻松创建插件来提取远程引用,并将其直接内嵌到代码中。

也就是说,对于如下所示的代码:

import moment from "https://unpkg.com/moment@2.29.4/moment.js"
console.log(moment())

您可以制作一个小型 Rollup 插件。

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
    }
  }]
};

使用新插件运行构建后,系统会发现每个远程 import 网址,无论它是我们的代码、子依赖项、子子依赖项还是其他任何位置。

npx rollup --input main.js --config ./rollup.config.mjs --file compiled.js

手动修改文件

最简单的选择是直接删除导致 RHC 的代码。在您选择的文本编辑器中打开,然后删除违规行。一般情况下, 建议这样做,因为它很脆弱,而且可能会被遗忘。当名为“library.min.js”的文件实际上不是 library.min.js 时,维护项目会更加困难。 除了修改原始文件之外,一种更易于 维护的替代方案是使用 patch-package 等工具。这是一个非常强大的选项,可让您保存对文件的 修改 ,而不是文件本身。它基于补丁文件构建,与 GitSubversion等版本控制系统所使用的补丁文件相同。您只需手动修改违规代码,保存差异文件,然后使用要应用的更改配置 patch-package 即可。您可以在项目的自述文件中阅读完整教程 。如果您要修补项目,我们 强烈 建议您与项目联系,请求在上游进行更改。虽然 patch-package 可以让补丁的管理变得更加轻松,但最好还是不要有任何补丁。

如果代码未使用,该怎么办

随着代码库的增长,依赖项(或依赖项的依赖项,或依赖项的依赖项的依赖项,依此类推)可能会保留不再使用的代码路径。如果其中一个部分包含用于加载或执行 RHC 的代码,则 必须 将其移除。无论它是死代码还是未使用代码,都必须移除。如果未使用,则应通过摇树(优化)或修补库来移除它。

是否有 任何 解决方法?

一般来说,没有。不允许使用 RHC。但是,在少数情况下, 允许 使用 RHC。这些情况几乎总是无法使用任何其他选项的情况。

User Scripts API

用户脚本是通常由 用户提供的小段代码,适用于 TamperMonkeyViolentmonkey 等用户脚本管理器。这些管理器无法捆绑用户编写的代码,因此 User Script API 提供了一种执行用户提供的代码的方式。这 不是 chrome.scripting.executeScript 或其他代码执行环境的替代方案。 用户必须启用开发者模式才能执行任何操作。如果 Chrome 网上应用店审核团队认为此 API 的使用方式与预期用途(即用户提供的代码)不同,则可能会拒绝该扩展程序,或将其商品详情从应用商店中下架。

chrome.debugger

chrome.debugger API 使扩展程序能够与 Chrome DevTools Protocol进行交互。这与 Chrome 的开发者工具以及 大量其他工具所使用的协议相同。借助此 API,扩展程序可以请求和执行远程代码。与用户脚本一样,它不是 chrome.scripting 的替代方案,并且具有更显著的用户体验。 在使用时,用户会在窗口顶部看到警告栏。如果关闭或关闭横幅,调试会话将终止。

Chrome 中地址栏的屏幕截图,其中显示了消息“调试器扩展程序已开始调试此浏览器”
Chrome 中地址栏的屏幕截图,其中包含消息“调试器扩展程序已开始调试此浏览器”

沙盒化 iframe

如果您需要将字符串评估为代码,并且处于 DOM 环境(例如 内容脚本,而不是扩展程序 Service Worker),那么另一种选择 是使用 沙盒化 iframe。出于安全考虑,扩展程序默认不支持 eval() 等内容。恶意代码可能会给用户安全带来风险。但是,当代码仅在已知安全的环境中执行时(例如已从网络其余部分沙盒化的 iframe),这些风险会大大降低。在这种情况下,可以解除阻止使用 eval 的内容安全政策,从而允许您运行任何有效的 JavaScript 代码。

如果您有未涵盖的使用场景,请随时使用 chromium-extensions 邮件列表与团队联系以获取反馈,或打开新 工单以向 One Stop Support 请求指导

如果您不同意判决,该怎么办

执行政策可能很微妙,审核涉及手动输入,这意味着 Chrome 应用商店团队有时可能会同意更改审核决定。如果您认为审核中存在错误,可以 使用 One Stop Support 对拒绝提出申诉