處理遠端代管程式碼違規問題

遠端代管程式碼 (RHC) 是 Chrome 線上應用程式商店的用語,指瀏覽器執行的任何項目,但這些項目並非從擴充功能本身的檔案載入。例如 JavaScript 和 WASM。包含資料或 JSON/CSS 等項目。

為什麼現在不能再使用 RHC?

現在,Manifest V3 擴充功能必須在擴充功能本身內封裝所有使用的程式碼。過去,您可以從網路上任何網址動態插入指令碼標記。

我聽說擴充功能有 RHC,為什麼會發生這種情況?

如果您的擴充功能在審查期間因 Blue Argon 錯誤遭到拒絕,審查人員認為您的擴充功能使用了遠端代管的程式碼。這通常是因為擴充功能嘗試新增含有遠端資源的指令碼標記 (即來自開放網路,而非擴充功能內含的檔案),或是擷取要直接執行的資源。

如何辨識 RHC

只要知道該注意哪些事項,就不難發現 RHC。首先,請在專案中檢查是否有「http://」或「https://」字串。如果發生 RHC 違規情形,您應該就能找到相關資訊。如果您有完整的建構系統,或是使用 npm 或其他第三方來源的依附元件,請務必搜尋程式碼的已編譯版本,因為商店會評估這個版本。如果還是找不到問題,請與 One Stop Support 聯絡。他們會說明具體違規事項,以及盡快發布擴充功能的必要條件。

如果圖書館要求提供代碼,該怎麼辦?

無論代碼來源為何,都不允許使用 RHC。這包括您並非作者,但剛好在專案中做為依附元件使用的程式碼。部分開發人員在使用 Firebase 時,如果納入遠端程式碼以供 Firebase 驗證 使用,就會發生這個問題。即使這是第一方 (即 Google 擁有) 程式庫,RHC 也不會例外。您需要設定程式碼,移除 RHC 或更新專案,一開始就不要加入程式碼。如果問題是您的程式碼並未載入 RHC,而是您使用的程式庫,建議您與程式庫作者聯絡。告知對方發生這種情況,並要求提供解決方法或更新程式碼來移除。

如果不想等待程式庫更新

部分程式庫會在收到通知後立即發布更新,但其他程式庫可能會遭到棄用,或需要一段時間才能解決問題。視特定違規事項的情況而定,您可能不需要等待違規事項移至未封鎖狀態,即可完成審查並通過。你可以選擇多種方式,快速恢復正常運作。

稽核程式碼

確定需要導致這項要求的程式碼嗎?如果可以刪除該程式庫,或移除導致問題的程式庫,請刪除該程式碼,這樣就完成了。

或者,是否有其他程式庫提供相同功能?請前往 npmjs.com、GitHub 或其他網站,尋找可滿足相同用途的其他選項。

tree shaking (移除沒用到的程式碼)

如果導致 RHC 違規的程式碼實際上並未使用,工具可能會自動刪除該程式碼。webpackRollupVite 等現代建構工具 (僅列舉部分) 都有稱為「樹狀結構修剪」的功能。在建構系統中啟用 tree shaking (移除沒用到的程式碼) 後,系統應會移除所有未使用的程式碼路徑。這表示您不僅能獲得更符合規範的程式碼版本,還能獲得更精簡快速的版本!請注意,並非所有程式庫都能進行 tree shaking (移除沒用到的程式碼),但許多程式庫可以。部分工具 (例如 Rollup 和 Vite) 預設會啟用樹狀結構修剪功能。如要啟用 webpack,則需要進行設定。如果您並未在擴充功能中使用建構系統,但使用程式碼程式庫,強烈建議您在工作流程中加入建構工具。建構工具可協助您編寫更安全、可靠且易於維護的專案。

具體實作方式取決於您的專案。但以 Rollup 為例,您只要編譯專案程式碼,即可新增樹狀結構修剪功能。舉例來說,如果您有名為 main.js 的檔案,只會登入 Firebase Auth:

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 的單一檔案。

彙整作業可以很簡單,但也可以非常容易設定。您可以新增各種複雜的邏輯和設定,只要查看說明文件即可。加入這類建構工具後,程式碼會變得更小、更有效率,而且在本例中,還能修正遠端代管程式碼的問題。

自動編輯檔案

遠端代管的程式碼越來越常以您納入的程式庫子依附元件形式,進入程式碼集。如果程式庫 X 要從 CDN 載入程式庫 import,您仍需更新程式庫 X,才能從本機來源載入。Y使用現代建構系統,您可以輕鬆建立外掛程式來擷取遠端參照,並直接內嵌至程式碼。

也就是說,如果程式碼如下所示:

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。如需完整教學課程,請參閱專案的 README 檔案。如果您要修補專案,強烈建議您與專案聯絡,要求上游進行變更。雖然 patch-package 可大幅簡化修補程式管理作業,但最好還是不要有任何修補程式。

如果代碼未使用

隨著程式碼集不斷擴增,依附元件 (或依附元件的依附元件,或依附元件的依附元件的依附元件…) 可能會保留不再使用的程式碼路徑。如果其中一個區段包含載入或執行 RHC 的程式碼,則必須移除。無論是已停用或未使用,如果未使用,就應移除,方法是進行樹狀結構篩除,或修補程式庫以移除。

是否有任何解決方法?

一般來說,不可以。RHC 不允許使用。不過,在少數情況下,可以使用。這幾乎都是其他選項無法解決的情況。

User Scripts API

使用者指令碼通常是由使用者提供的小段程式碼,適用於 TamperMonkeyViolentmonkey 等使用者指令碼管理工具。這些管理員無法將使用者編寫的程式碼組合在一起,因此 User Script API 提供執行使用者提供程式碼的方法。這並非 chrome.scripting.executeScript 或其他程式碼執行環境的替代方案。使用者必須啟用開發人員模式,才能執行任何操作。如果 Chrome 線上應用程式商店審查團隊認為您以非預期方式使用這項功能 (即使用者提供的程式碼),您的擴充功能可能會遭到拒絕,或從商店下架。

chrome.debugger

擴充功能可透過 chrome.debugger API 與 Chrome 開發人員工具通訊協定互動。這與 Chrome 開發人員工具使用的通訊協定相同,而且其他工具也大量採用。擴充功能可藉此要求及執行遠端程式碼。與使用者指令碼一樣,這項功能無法取代 chrome.scripting,且使用者體驗明顯不同。使用時,視窗頂端會顯示警告列。如果關閉或關掉橫幅,偵錯工作階段就會終止。

Chrome 網址列的螢幕截圖,顯示「偵錯工具擴充功能已開始偵錯這個瀏覽器」訊息
Chrome 網址列的螢幕截圖,顯示「偵錯工具擴充功能已開始偵錯這個瀏覽器」訊息

沙箱 iframe

如需將字串評估為程式碼,且位於 DOM 環境 (例如內容指令碼,而非擴充功能 Service Worker),則可使用沙箱化 iframe。為確保安全,擴充功能預設不支援 eval() 等項目。惡意程式碼可能會危害使用者安全。但如果程式碼只在已知的安全環境中執行,例如從網頁其餘部分沙箱化的 iframe,這些風險就會大幅降低。在這種情況下,您可解除禁止使用 eval 的內容安全政策,執行任何有效的 JavaScript 程式碼。

如有未涵蓋的用途,歡迎透過 chromium-extensions 郵寄清單與團隊聯絡,取得意見回饋,或開啟新工單,向 One Stop Support 尋求指引。

如果不同意判決結果

政策的執行方式可能因情況而異,且審查需要手動輸入,因此 Chrome 線上應用程式商店團隊有時可能會同意變更審查結果。如果認為審查有誤,可以透過 One Stop Support 提出申訴