安全模型: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>。不间断空格显示为⍽,行尾空白显示为·,制表符显示为»。所有不显示任何内容的字符,都以同样的方式显示为品红色块上的
<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指定了一个钩子,git status等 git 命令会调用它;过滤器驱动则指定了对文件内容运行的程序。如果编辑器向 git 询问一个刚解压的仓库的状态,就可能运行它们。[20] [21] [22]sim 的应对
sim 会先向 git 询问仓库的配置。命令行能挡住的都会被关闭(
core.fsmonitor、钩子、过滤器、外部 diff、文本转换),然后 git 照常回答。如果仓库自身的作用域里还定义了其他可能运行命令的项(core.sshCommand、可能引入这类项的 include),就不运行 git。git 运行时不经过 shell,带--no-optional-locks,环境中也禁止任何拉取。sim 不运行 git 的任何写入类命令,所以不会触发钩子。文件树会写出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)