Web 外掛市集
這個網頁應用擁有一個外掛市集,可將 SheetForge 外掛安裝進瀏覽器,另外還有一條獨立路徑,能從 GitHub URL 側載一個未經審查的外掛。同一個市集,以 UIToolkit 重新建構後,也隨附於 Unity 之中。
登錄庫——單一靜態檔案,兩個讀取端
市集的清單來自一個單一的靜態登錄庫檔案(public/registry/plugins.json),網頁應用與 Unity 內建的市集視窗皆會讀取它。因為只有這一份清單,兩個介面永遠會顯示相同的目錄內容。
SheetForge 不會代管外掛的二進位檔案,而是由每位作者自己的 GitHub release 代管。登錄項目本身是中繼資料加上一個固定的雜湊值——而不是外掛本身的副本。
雜湊固定——在核心看到任何一個位元組之前,先驗證完整性
每個登錄項目都帶有該外掛通過核准時所擷取的 SHA-256 值。取得檔案時,每一個接收到的位元組,都會先與這個雜湊值比對,才會抵達核心。只要有一個位元組不符,就會被拒絕。
正是這一點,讓「二進位檔案存放在別人的 GitHub 上」這件事變得安全:這份 release 可以從任何地方重新下載,但永遠只有經過核准的確切位元組才會被載入。
透過伺服器代理的一鍵安裝
瀏覽器無法直接擷取 GitHub release 的資源——跨來源(CORS)規則會阻擋它。因此取得檔案的動作會經過一條伺服器代理路由(api/market/artifact,透過 lib/market/install.ts 中的 ArtifactProxy 串接),由伺服器端擷取該 release 並以串流方式回傳。因此安裝只需要一鍵完成。
當代理無法連到某個 release 時,仍保留一條手動下載後上傳的次要備援路徑。它會通過完全相同的雜湊檢查——一個上傳的檔案,並不會比一個經過代理的檔案更值得信任。
相容性關卡——在完整性驗證之後、註冊之前
通過雜湊檢查,只能證明這些位元組就是核准過的那一批。它完全無法說明此主機是否能讀懂該外掛的格式,而這是另一個獨立的問題。
答案來自經過驗證的 DLL 本身:一個組件層級的宣告,指名它是依照哪一代外掛格式編譯的,以及它所需要的最低主機版本。
- 登錄項目只是公告,真正決定的是組件本身。 一個登錄項目帶有相同的兩個值(
pluginFormat、minHost),因此目錄能在你下載之前就先顯示它們。而關卡本身讀取的,是剛通過驗證的位元組中的宣告。一筆登錄項目可能已經過時;但一個編譯完成的宣告不會。 - 一道關卡,三條路徑。 市集安裝、側載,以及一個單純的本機檔案,全都詢問同一個判斷式,因此不會有任何一條路徑,悄悄接受另一條路徑所拒絕的東西。
- 每個組件要嘛全有、要嘛全無。 一個被拒絕的組件什麼都不會註冊——絕不會只註冊一半的合約——而拒絕訊息會指名該組件宣告了什麼,對照此主機讀到的又是什麼。
- 沒有宣告也沒關係。 一個在此屬性存在之前建置的外掛,會被視為最早的一代、且沒有主機版本需求,因此它的載入方式完全不變。
快取與卸載
通過雜湊檢查的位元組,會被快取在瀏覽器中(IndexedDB),因此清單中已啟用的外掛,會在之後的造訪中自動載入——你只需要啟用一次。
停用或移除一個外掛會立即生效:應用程式會讓核心把該組件從外掛組合中移除並重建工作階段,因此它的儲存格型別、列舉、範本、色彩預設、工作室動作與畫布疊加層不需重新整理就會消失。(.NET 本身不會卸載組件——位元組仍留在頁面中,但不再起作用;若再次啟用同一個外掛,核心會重新啟用留存的組件,而不是再載入一次。)
從 GitHub URL 側載一個未經審查的外掛
除了經過整理的登錄庫之外,你也可以直接把一個任意的 GitHub URL 交給應用程式,側載一個未經審查的外掛(lib/market/sideload.ts,由 api/market/github 提供服務)。這是用來試用一個尚未列在目錄中的外掛。
由於這條路徑接受的是使用者提供的 URL,而非一筆經過查核的登錄項目,因此它是唯一直接強制執行 SSRF 防護的地方:
- 該 URL 必須能解析到一個 GitHub 家族的主機——
github.com、raw.githubusercontent.com、release-assets.githubusercontent.com等等——並以小寫進行精確比對。 - 任何會外洩進內部網路的重新導向,都會在這道邊界被阻擋。
側載而來的產物,會被快取在一個獨立的擁有者標籤(sideload:)之下。因此,一個恰好與某筆登錄項目位元組相同的未審查外掛,仍會與經過整理的那一份分開存放——側載檔案絕不會冒充成一個已核准的外掛。
以 GitHub 作為匯入來源
兩條路徑都會讓外掛的位元組通過相同的管線:
- 登錄庫安裝會透過伺服器代理,擷取代管在作者 GitHub release 上的產物,然後對照固定的 SHA-256 進行檢查。
- 側載則接受一個原始的 GitHub URL,限制在 GitHub 家族的主機範圍內,接著以相同方式檢查位元組。
在這兩種情況下,瀏覽器對 GitHub release 資源的 CORS 限制,都是在伺服器端處理的,而完整性則是在核心載入任何內容之前,透過用戶端的雜湊檢查來維持。
Unity 內建的市集
Unity 的外掛市集已重新以 UIToolkit 建構,讓外觀貼近網頁版市集——一個卡片網格搭配一個詳細資訊窗格,讀取相同的靜態登錄庫,具有相同的雜湊不符拒絕機制,以及相同的完全信任安裝提示。
登入,以及瀏覽器保存的外掛清單與預設集,皆僅限網頁版使用;Unity 視窗只扮演目錄與安裝介面的角色。
相關頁面
- SheetForge Web——市集所在的瀏覽器應用
- 外掛開發——建構一筆市集項目所指向的外掛