즈푸 ZCode, 전체 Git 이력 무단 업로드 의혹 제기돼
개발자 포렌식 블로그가 AI 코딩 도구 ZCode가 로그인 시 전체 .git 이력과 LFS 캐시를 암호화해 알리윈 OSS로 업로드한다고 주장했다.
중국어 원문을 AI로 번역했습니다. 고유명사와 수치는 원문 표기를 우선하며, 중요한 판단에는 아래 출처 원문을 함께 확인하세요.
지푸(智谱)의 AI 프로그래밍 데스크톱 클라이언트 ZCode가 오늘 큰 문제를 하나 들켰다. 개발자 ferstar가 포렌식 블로그 글을 올렸는데, 계정에 로그인만 하면 ZCode가 백그라운드에서 조용히 전체 워크스페이스, 즉 완전한 .git 이력과 LFS 대용량 파일 캐시, reflog, 전역 설정까지 묶어서 암호화한 뒤 알리바바 클라우드 OSS로 업로드한다는 것이다. 이 소식은 곧바로 중국 기술 커뮤니티에 퍼졌고, 어떤 사람이 자신의 Mac으로 그 포렌식 경로를 따라 다시 돌려본 결과 "사실이며, 문제는 '그럴 것이냐'가 아니라 '이미 그랬다'"는 결론이 나왔다.
참고 출처: 智谱 ZCode,你打包上传我的代码仓库干什么?
사건의 발단은 아주 평범했다. ferstar가 디스크를 정리하다가 ~/.zcode가 700여 MB를 차지하고 있는 것을 발견했다. 계속 파고들다가 v2/checkpoints/에서 313MB짜리 .enc 암호화 파일을 봤고, 그 옆에는 상태 메타데이터가 있었다. 이는 baseline으로 표시된 전량 스냅샷으로, workspaceSizeBytes는 345MB, failureCount는 이미 564에 달해 있었다(업로드가 564번 실패했는데도 여전히 pending 상태로 재시도를 기다리고 있었다). 그의 프로젝트는 총 10GB였고, 의존성을 제외하고 남은 345MB는 거의 전부 핵심 자산이었다.
그는 클라이언트의 app.asar를 뜯어 리버싱했고, 업로드 경로를 아주 완전하게 복원했다. 클라이언트는 먼저 zcode.z.ai에 업로드 자격 증명을 요청해 OSS 폼 서명, 동적 Object Key, 크기 제한, 그리고 암호화 공개키 한 개를 받는다. 그런 다음 로컬에서 패키징하고 스트리밍 암호화한 뒤, 지푸의 비즈니스 서버를 전혀 거치지 않고 폼 직접 전송 방식으로 알리바바 클라우드 OSS로 바로 보내고, 다시 OSS가 콜백으로 지푸 백엔드에 통보해 등록하게 한다.
가장 아이러니한 것은 암호화 방식이다. 표준 봉투 암호화로, 내용은 무작위 대칭키로 AES-256-CTR을 쓰고, 대칭키는 다시 RSA-OAEP로 감싼다. 그런데 공개키는 서버가 동적으로 내려주고, 개인키는 처음부터 끝까지 클라우드에만 있다. ferstar는 로컬의 모든 개인키로 시도해봤지만 풀 수 없었다. "당신 하드디스크에 있는 그 313MB짜리 암호문은 당신이 열 수 없고, ZCode 클라이언트 자신도 열 수 없으며, 온 세상에서 지푸 백엔드의 개인키만이 풀 수 있다."
스냅샷에는 도대체 무엇이 담겨 있을까? 암호문은 풀 수 없지만, 생성 시의 Manifest(42,411개 파일 목록)는 평문으로 로컬에 남아 있다. 집계해보니 .git 디렉터리 하나가 86.6%를 차지했다. .git/lfs 56.8%, .git/objects 29.6%, .git/logs 0.2%, 나머지 소스 코드와 문서는 13.4%에 불과했다.
즉 일단 업로드되면 클라우드가 가져가는 것은 현재 워크스페이스 코드만이 아니라, 저장소가 만들어진 이래의 전체 이력이다. 이미 오래전에 삭제된 민감한 설정과 과거 key, 원격에 푸시하지 않은 로컬 브랜치 이름(미공개 연구개발 동향에 해당), .git/config에 적힌 내부 GitLab 도메인까지 포함된다. 전역 설정 파일 settings.behavior.json까지 워크스페이스를 넘어 스냅샷과 함께 패키징되어 업로드된다.
개발자들을 진짜로 폭발하게 만든 것은 스위치가 무용지물이라는 점이다. 설정에 있는 두 개의 스위치, 즉 "경험 최적화"와 "저장소 스냅샷 인덱싱"을 끄면 업로드를 막을 수 있다고 생각하지만, 실제로 하나는 데이터를 학습에 쓸지 여부만 관장하고 다른 하나는 서버가 인덱스를 만들지 여부만 관장할 뿐, 끄든 말든 로컬 패키징과 업로드는 전혀 빠지지 않는다. 클라이언트 코드를 보면 업로드 캡처를 담당하는 sidecar는 시작 시 무조건 인스턴스화되며, 사용자 설정에 대한 아무런 판단도 없다. 유일한 관문은 로그인 JWT를 받아낼 수 있느냐다. 트리거 지점은 매번 Prompt를 보내기 전의 captureBeforePrompt와 작업 종료 시의 repo-wiki-update이며, 단일 활성 세션에서 최대 62번의 캡처가 발생한다.
개인정보 처리방침에는 "대화에서 제출된 텍스트, 파일, 코드"를 수집한다고만 적혀 있을 뿐, "전체 워크스페이스와 완전한 Git 이력을 패키징한다"는 점은 한 글자도 언급하지 않았다. 문서, FAQ, 업데이트 로그 어디에도 없다.
어떻게 방어할까? 삭제는 두더지 잡기다. 작성자가 처음에 pending 패키지를 삭제했더니 30분 후에 다시 한 부를 수집했고, 실패 카운트는 564에서 565로 뛰었다. 효과적인 방법은 파일 시스템 계층에서 잠금을 거는 것이다. macOS는 chflags uchg ~/.zcode/v2/checkpoints, Linux는 sudo chattr +i를 쓰면 커널이 쓰기를 거부해 업로드 경로가 보낼 산출물을 갖지 못하게 한다. 대가는 체크포인트 롤백과 타임라인 인터페이스가 무효화되는 것이며, 대화, 자동 완성, 도구 실행은 영향을 받지 않는다.
"추론에는 컨텍스트가 필요하다는 점은 모두가 받아들인다. 진짜 선을 넘은 것은 두 가지다. 데이터 범위와 아키텍처 태도다."
그런데 지푸 측이 방금 대응을 내놨다.
ZCode 사용자 여러분:
오늘 커뮤니티의 관련 논의를 저희는 매우 중시하며, 즉시 자체 점검을 완료했고, 먼저 영향을 받은 사용자분들께 사과드립니다. 관련 상황을 다음과 같이 설명드립니다:
이번 문제는 ZCode의 코드베이스 인덱싱 "기능에서 비롯되었습니다. 이 기능은 사용자가 로컬에서 저장소 인덱스를 생성하도록 도와, 역사 버전을 포함한 세션 체크포인트 복구, 역사 버전 롤백 및 Repo Wiki 등의 기능을 지원하고자 했습니다.
Repo Wiki 기능은 Wiki 페이지를 생성할 때 저장소 데이터 업로드를 촉발할 수 있습니다. Wiki 페이지가 클라우드에서 생성된 후 관련 업로드 데이터는 즉시 파기되며 저장되지 않습니다. 이 기능이 출시 초기에 기본 활성화되어 있어 일부 사용자가 영향을 받았으며, 이에 대해 깊이 사과드립니다. 현재 관련 문제는 이미 수정되었습니다.
데이터에 관한 어떤 문제든 사용자의 제품 신뢰에 직접 영향을 미친다는 것을 잘 알고 있습니다. 저희는 조만간 ZCode 코드베이스를 오픈소스로 공개하고, 더 개방적인 생태계에서 제품을 더 잘 만들어 나가며, 제3자 평가 인력을 초청해 시스템 운영 상황을 심사하게 하고, 제품 심사 진행 상황을 지속적으로 공개하여 완전히 투명한 방식으로 여러분의 신뢰를 쌓겠습니다.
이번 문제로 여러분께 끼친 불편에 대해 깊이 사과드립니다. 저희는 모든 ZCode 사용자에게 주간 할당량을 한 번 추가로 초기화해 드릴 것이며, 할당량은 오늘 지급됩니다.
다시 한번 관심과 감독에 감사드립니다.
출처:
- 扒一扒 ZCode 静默上传全量 Git 历史的骚操作 - ferstar