보안 모델: 위협마다 sim이 하는 일
sim은 하나의 질문을 중심으로 만들어졌습니다. 내가 쓰지 않은 파일, 폴더, 저장소가 나에게 무엇을 할 수 있는가? 이 페이지는 위협 모델과, 위협마다 sim이 하는 일을 설명합니다.
위협 모델
직접 작성하지 않은 파일과 저장소를 열게 됩니다. 클론한 저장소, 풀어낸 아카이브, 풀 리퀘스트 체크아웃, 다른 프로세스가 쓰는 파일이 그 예입니다. 공격자가 통제하는 것은 그 내용이지 사용자의 컴퓨터가 아닙니다. sim은 그 내용이 사용자에게서 숨지 못하고, 에디터의 설정을 바꾸지 못하며, 에디터가 코드를 실행하게 만들지 못하도록 하려고 합니다.
전제
- 자신의 터미널, 자신의 사용자 계정, 자신의 전역 git 설정은 신뢰합니다.
- 공격자는 저장소 안의 모든 파일을 바꿀 수 있습니다. .git/config와, 사용자가 연 파일 옆에 있는 파일도 포함됩니다.
- 공격자는 sim 자체나 홈 디렉터리를 바꿀 수 없습니다.
위협과 sim의 대응
양방향 제어 문자(Trojan Source)
위협
Unicode 제어 문자는 코드가 표시되는 순서를 바꿔서, 리뷰어가 읽는 코드와 컴파일러가 읽는 코드가 달라지게 합니다(CVE-2021-42574). 연구자들이 에디터에 권하는 것은 이 문자들을 눈에 띄게 만드는 것입니다.[1] [2] [3]
sim의 대응
U+200E–200F, U+202A–202E, U+2066–2069, U+061C는 마젠타 블록 위에
<U+202E>처럼 그려지며 터미널에 그대로 전송되지 않습니다. 이 블록은 셀이 어떤 상태이든 유지됩니다. 선택됨, 검색에 일치함, 커서 줄 위, 외부 변경 안, 먼 줄의 안개 속, 제목이나 코드 블록 위가 모두 그렇습니다. 파일에 입력한 문자 그대로의 텍스트<U+202E>는 보통 텍스트이며 블록이 붙지 않습니다.사양: SAFE-8, UI-15
보이지 않는 문자와 공백
위협
유니코드에는 아무것도 그리지 않는 문자가 많습니다. ASCII를 본뜬 태그 문자의 나열은 파일을 읽는 AI 어시스턴트에게 숨은 지시를 실을 수 있고(“ASCII smuggling”), 표준 자신도 태그 문자를 모르는 렌더러는 “display any sequence of tag characters as invisible”(태그 문자의 나열을 보이지 않게 표시한다)라고 말합니다. 변형 선택자의 나열은 프로그램을 실을 수 있어서, 공개된 VS Code 확장에 페이로드를 숨긴 사례가 있습니다(GlassWorm). 채움 문자, 소프트 하이픈, 결합자, 줄 구분자는 식별자나 주석 안에 있어도 눈에 보이는 것을 전혀 바꾸지 않습니다. 너비가 0인 공백, 파일 중간의 바이트 순서 표시, 줄 바꿈 없는 공백, 줄 끝의 공백도 겉모습을 바꾸지 않은 채 파일의 의미나 diff를 읽는 방식을 바꿉니다.[4] [5] [6] [7] [8] [9]
sim의 대응
U+200B와, 파일 시작 이외의 위치에 있는 U+FEFF는
<U+200B>와<U+FEFF>로 그려집니다. 줄 바꿈 없는 공백은⍽, 줄 끝 공백은·, 탭은»로 그려집니다.아무것도 그리지 않는 문자는 모두 같은 방식으로, 마젠타 블록 위의
<U+E0041>꼴로 보여 줍니다. 본문, 상태 줄, 메시지, 명령줄은 물론 파일에서 온 이름과 줄(트리, 탭, 이동 경로 표시줄, 검색 결과)에서도 마찬가지입니다. 대상은 서식 문자, 태그 문자, 변형 선택자, 한글 채움 문자, 소프트 하이픈, 결합 자소 연결자, U+2028과 U+2029, 그리고 유니코드 18.0.0에서 할당되지 않은 모든 코드 포인트입니다. 다음은 그대로 둡니다. 두 이모지 사이의 너비 0 결합자, 그림 문자 뒤의 변형 선택자, 잉글랜드·스코틀랜드·웨일스의 깃발 세 가지, 사용자 정의 영역 문자(Nerd Font의 아이콘)입니다. 같은 문자라도 다른 자리에 있으면 보여 줍니다. 대가도 있습니다. 한자 뒤에 표의 문자 변형 선택자(한자의 자형 하나를 고르기 위해 등록된 방식)가 오면葛<U+E0100>처럼 선택자를 보여 줍니다. 같은 문자가 데이터를 숨기는 통로이기 때문이며, 주로 일본어 텍스트에 영향을 줍니다. 소스 코드 편집기에 대한 유니코드의 지침은 “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
파일에 숨겨 둔 설정(모드라인)
위협
파일 속 주석으로 에디터 옵션을 설정할 수 있고, 이 기능의 버그가 코드 실행으로 이어진 적이 있습니다. CVE-2019-12735(Vim과 Neovim)와 CVE-2026-34982(Vim)입니다.[14] [15] [16]
sim의 대응
설정과 명령은 파일 내용에서 절대 읽지 않습니다. 우회할 모드라인 자체가 없습니다.
사양: SAFE-9
프로젝트별 설정
위협
프로젝트 디렉터리가 에디터가 불러와 실행하는 설정을 담을 수 있습니다. Vim과 Neovim의
exrc파일, Helix의.helix/config.toml이 그 예입니다.[17] [18] [19]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을 실행하지 않습니다. 실행할 때도 셸 없이,--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의 대응
저장하지 않은 텍스트는 최대 1초에 한 번 전용 상태 디렉터리에 스냅샷으로 저장되며, 실행 취소 한 단계로 복원을 제안합니다. 이 디렉터리는 사용자 소유이면서 다른 사용자의 접근 권한이 없는 실제 디렉터리여야 하며, 그렇지 않으면 스냅샷이 꺼지고 sim이 이를 알립니다.
사양: SAFE-6
텍스트가 아닌 파일
위협
바이너리나 UTF-8이 아닌 내용을 텍스트처럼 보여 주면 파일에 든 내용을 숨길 수 있고, 저장하면서 손상될 수도 있습니다.
sim의 대응
이런 파일은 이유를 보여 주며 읽기 전용으로 엽니다. 잘못된 바이트는 U+FFFD로 그려지고, 모든 종류의 쓰기가 거부됩니다.
사양: SAFE-4
sim이 하지 않는 일
- sim은 샌드박스가 아닙니다. 사용자의 권한으로 실행됩니다. 숨은 문자를 보여 준다고 해서, 그 문자가 숨기고 있는 코드를 사용자가 실행하는 것까지 막아 주지는 않습니다.
- 문자 목록은 유니코드 18.0.0의 표로 고정되어 있습니다. 이후 버전이 할당한 코드 포인트는 표를 다시 만들 때까지 숨은 문자로 보입니다. 유니코드 혼동 문자(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)