安全模型:sim 如何因應每一種威脅
sim 圍繞一個問題而建:一個不是你寫的檔案、資料夾或儲存庫,能對你做什麼?本頁是威脅模型,以及 sim 對每種威脅的因應方式。
威脅模型
你會開啟並非自己撰寫的檔案與儲存庫:複製下來的儲存庫、解壓縮的壓縮檔、檢出的 pull request、另一個行程寫入的檔案。攻擊者控制的是它們的內容,而不是你的機器。sim 努力確保這些內容無法對你隱藏、無法重新設定編輯器,也無法讓編輯器執行程式碼。
前提假設
- 你信任自己的終端機、自己的使用者帳號與自己的全域 git 設定。
- 攻擊者可以修改儲存庫中的任何檔案,包括 .git/config,以及與你所開啟檔案放在一起的其他檔案。
- 攻擊者無法修改 sim 本身或你的家目錄。
威脅與 sim 的因應
雙向控制字元(Trojan Source)
sim 的因應
U+200E–200F、U+202A–202E、U+2066–2069 與 U+061C 會以洋紅色色塊顯示為
<U+202E>這樣的形式,絕不會原封不動地送給終端機。無論儲存格處於什麼狀態,色塊都不會消失:被選取、被搜尋命中、位於游標所在行、處於外部變更之內、在遠處行的霧化之中、位於標題或程式碼區塊上。檔案中輸入的字面文字<U+202E>是一般文字,沒有色塊。規格: SAFE-8, UI-15
不可見字元與空白
威脅
Unicode 中有許多字元什麼也不畫。一串標籤字元(對應 ASCII)可以攜帶給讀取該檔案的 AI 助理的隱藏指令(「ASCII smuggling」);標準本身就說,不認識標籤字元的渲染器會 “display any sequence of tag characters as invisible”(把任何標籤字元序列顯示為不可見)。一串變體選擇符可以攜帶程式:曾有一例把酬載藏在已發布的 VS Code 擴充功能中(GlassWorm)。填充字元、軟連字號、連接符和行分隔符號即使出現在識別字或註解裡,也不會改變你看到的任何東西。零寬空格、檔案中間的位元組順序標記、不換行空格或行尾空白,同樣會在不改變外觀的情況下改變檔案的含義或差異的讀法。[4] [5] [6] [7] [8] [9]
sim 的因應
U+200B,以及檔案開頭以外任何位置的 U+FEFF,顯示為
<U+200B>與<U+FEFF>。不換行空格顯示為⍽,行尾空白顯示為·,Tab 顯示為»。所有不顯示任何內容的字元,都以同樣的方式顯示為洋紅色色塊上的
<U+E0041>:在文字、狀態列、訊息和命令列裡,也在來自檔案的名稱和行裡(檔案樹、分頁、麵包屑和搜尋結果)。包括格式字元、標籤字元、變體選擇符、諺文填充字元、軟連字號、組合字素連接符、U+2028 和 U+2029,以及 Unicode 18.0.0 尚未指派的每一個碼點。以下情形維持原樣:兩個表情符號之間的零寬連接符、象形符號後的變體選擇符、英格蘭、蘇格蘭和威爾斯的三面旗幟,以及私用區字元(Nerd Font 的圖示);同樣的字元出現在別處則會顯示。代價是:漢字後面接著表意文字變體選擇符(一種為選定漢字的某個字形而登記的機制)時,會顯示選擇符,如葛<U+E0100>,因為同一種字元也是隱藏資料的管道;這主要影響日文文字。Unicode 針對原始碼編輯器的指引建議提供 “an option to make visible any default ignorable code points”(一個讓預設可忽略碼點可見的選項);sim 沒有這個選項,而是一律顯示。[10] [6] [11] [12] [13]規格: SAFE-8, SAFE-10, SAFE-11, UI-6
檔案與檔名中的終端機跳脫序列
威脅
檔案中或檔名中的控制字元,可能以跳脫序列的形式到達終端機。
sim 的因應
C0 字元和 DEL 顯示為
^[,C1 字元顯示為<U+0085>。不會向終端機寫出任何原始字元,測試會檢查實際送出的位元組。檔名中的控制字元和雙向控制字元,在檔案樹、分頁、麵包屑、搜尋結果和選擇器中,以同樣的形式顯示,如
^[和<U+202E>;v0.1.0 則顯示為 U+FFFD。規格: SAFE-8, SAFE-10, TREE-1, GREP-7
藏在檔案裡的設定(modeline)
威脅
檔案中的一行註解就能設定編輯器選項,而這個機制中的缺陷曾導致程式碼執行:CVE-2019-12735(Vim 與 Neovim)與 CVE-2026-34982(Vim)。[14] [15] [16]
sim 的因應
設定與指令絕不會從檔案內容中讀取。沒有可繞過的 modeline。
規格: SAFE-9
專案本地設定
sim 的因應
sim 沒有設定檔,沒有外掛,也沒有可載入的專案本地設定。只記住你選擇的外觀、圖示與 Markdown 檢視,各一個詞,存放在 sim 的狀態目錄中。
規格: SAFE-9, LOOK-1
會執行指令的儲存庫設定
威脅
git 會執行儲存庫自身的
.git/config中指定的指令。core.fsmonitor指定了一個掛鉤(hook),git status等 git 指令會呼叫它;過濾器驅動程式則指定了對檔案內容執行的程式。如果編輯器向 git 詢問一個剛解壓縮的儲存庫的狀態,就可能執行它們。[20] [21] [22]sim 的因應
sim 會先向 git 詢問儲存庫的設定。命令列能擋住的都會被關閉(
core.fsmonitor、hooks、過濾器、外部 diff、文字轉換),然後 git 照常回答。如果儲存庫自身的範圍裡還定義了其他可能執行指令的項目(core.sshCommand、可能引入這類項目的 include),就不執行 git。git 執行時不經過 shell,帶--no-optional-locks,環境中也禁止任何擷取。sim 不執行 git 的任何寫入類指令,所以不會觸發 hooks。檔案樹會寫出n git settings off或git not run,狀態列會指出相關的鍵及其位置。規格: MARK-1, GIT-1, GIT-2, GIT-3, GIT-5
執行儲存庫程式碼的搜尋
威脅
ripgrep 會從環境變數指定的設定檔讀取額外的旗標,並且可以對它讀取的每個檔案執行指令(
--pre)。[23]sim 的因應
sim 在
PATH的絕對路徑項目中尋找 ripgrep,以完整路徑、空環境與--no-config執行它,絕不給它傳--pre。查詢內容與檔案排在-e與--之後,因此它們都不會被當成選項。規格: GREP-7, GREP-8
通往專案之外的連結
威脅
符號連結可以指向專案之外,共用目錄裡也可能被人放置一個等著儲存操作去跟隨的連結。
sim 的因應
搜尋與預覽只讀取真實路徑位於專案根目錄之下的一般檔案。儲存時,路徑中任何一段如果是既不屬於你也不屬於 root 的連結,就拒絕透過它寫入。
規格: SAFE-1, GREP-7
被外部改寫的檔案
威脅
建置、格式化工具或其他工具改寫了你開啟的檔案。此時儲存會覆蓋它們的成果,或者它們的成果覆蓋你的。
sim 的因應
sim 會察覺這一點,標出變更之處,並在 rename 之前,把檔案的雜湊值與載入時的雜湊值做比對。如果不同,就不寫入任何內容。
儲存過程中到來的變更,現在會等待儲存的回應,因此不會被合併到正在儲存的文字之上。2026-10-18 發現的一個罕見競態,可能使 v0.1.0 遺失一行。
規格: DISK-1, DISK-2, SAFE-5, SAFE-7
當機與被關閉的終端機
威脅
當機、
kill -9或關閉終端機會遺失未儲存的文字。sim 的因應
未儲存的文字最多每秒產生一次快照,存入私有的狀態目錄,並作為一次復原步驟提示還原。該目錄必須是歸你所有、其他人無權存取的真實目錄;否則快照會關閉,sim 也會明確告知。
規格: SAFE-6
不是文字的檔案
威脅
把二進位或非 UTF-8 的內容當成文字顯示,可能掩蓋檔案裡的真實內容,儲存時還可能把它毀損。
sim 的因應
這類檔案以唯讀方式開啟並顯示原因。無效位元組顯示為 U+FFFD,所有種類的寫入都會被拒絕。
規格: SAFE-4
sim 不做什麼
- sim 不是沙盒。它以你的權限執行。讓你看到一個隱藏字元,並不能阻止你去執行它所隱藏的程式碼。
- 它的字元清單是固定的:Unicode 18.0.0 的表。後續版本指派的碼點,在表重新產生之前都會被當作隱藏字元顯示。它不是對 Unicode 易混淆字元(UTR #36)的完整檢查,來自其他文字系統的形近字母(同形異義字,Trojan Source 的另一半)不在其文件所述的範圍內。
- 儲存前的最後一次檢查會比較檔案的時間戳記等標記,只有標記變了才比較雜湊。同一微秒內的寫入,或在時間戳記粒度很粗的檔案系統上、一個時間刻度內發生的同樣大小的改寫,仍可能漏過。
- 本頁的一切內容都來自專案自己的規格與測試。其文件中沒有引用任何獨立稽核。
- 語言伺服器尚未實作。下方的路線圖中有一個沙盒化的語言伺服器。
路線圖:語言伺服器
規劃中
語言伺服器是專案可以要求你的編輯器啟動的程式,因此它是儲存庫執行程式碼的最大途徑之一。sim 目前還沒有。計畫是做一個沙盒化的用戶端,並在狀態列加一個指示項,顯示專案根目錄是否受信任。這是計畫,尚未推出。
來源
- NVD: CVE-2021-42574 (bidirectional control characters, “Trojan Source”)
- Boucher and Anderson, Trojan Source: Invisible Vulnerabilities (USENIX Security 2023)
- Unicode Standard Annex #9: Unicode Bidirectional Algorithm
- Unicode Technical Report #36: Unicode Security Considerations
- Johann Rehberger, ASCII Smuggler Tool: Crafting Invisible Text and Decoding Hidden Codes (Embrace The Red, 14 January 2024)
- Unicode Technical Standard #51: Unicode Emoji, revision 31 (ZWJ sequences, emoji tag sequences, Annex C)
- Paul Butler, Smuggling arbitrary data through an emoji (8 February 2025)
- Koi Security, GlassWorm: First Self-Propagating Worm Using Invisible Code Hits OpenVSX Marketplace (18 October 2025; the original address now redirects elsewhere, so this is the Internet Archive's copy)
- Endor Labs, Invisible Threats and the Blind Spots of Security (an analysis of GlassWorm)
- The Unicode Standard 18.0, chapter 23, Special Areas and Format Characters (§23.4 Variation Selectors, §23.9 Tag Characters)
- Unicode Technical Standard #55: Unicode Source Code Handling, revision 5 (§4.2 Blank and Invisible Characters)
- Unicode Technical Standard #37: Unicode Ideographic Variation Database, revision 16 (ideographic variation sequences)
- Unicode Ideographic Variation Database: the registered collections (Adobe-Japan1, Hanyo-Denshi, Moji_Joho and others)
- NVD: CVE-2019-12735 (Vim before 8.1.1365 and Neovim before 0.3.6, modeline)
- NVD: CVE-2026-34982 (Vim before 9.2.0276, modeline sandbox bypass)
- Vim security advisory GHSA-8h6p-m6gr-mpw9 (modeline sandbox bypass)
- Vim 9.2.1202, runtime/doc/options.txt ('modeline', 'modelinestrict', 'exrc', 'isprint', 'fsync')
- Neovim 0.12.6, runtime/doc/options.txt ('modeline', 'modelineexpr', 'exrc', 'isprint', 'fsync')
- Helix 25.07.1, book/src/configuration.md (a config.toml local to a project)
- git-config documentation (core.fsmonitor, filter.<driver>.*)
- githooks documentation (fsmonitor-watchman, post-index-change)
- Justin Steven, Git honours embedded bare repos, and exploitation via core.fsmonitor affects IDEs (2022)
- ripgrep user guide (the config file named by RIPGREP_CONFIG_PATH, --pre)