
「안전 후크를 넣었으니 rm 사고는 막을 수 있다」고 생각했는데, 홈 디렉터리로의 삭제가 그대로 통과되어 버렸다——실제 사고를 통해 직접 만든
요약
Claude Code 사용 중 쉘 환경 변수 해석 오류로 인해 홈 디렉터리가 통째로 삭제된 실제 사고 사례를 분석합니다. 안전 후크(cc-safe-setup)의 정규 표현식 허점으로 인해 특정 홈 변수 형태의 삭제 명령이 통과되었던 원인과 해결 과정을 다룹니다.
핵심 포인트
- 쉘 환경 변수 미지속으로 인한 경로 해석 오류 발생
- 안전 후크의 정규 표현식 누락으로 인한 보안 허점 확인
- 에이전트의 파괴적 명령 실행 시 변수 해석 방식 주의 필요
- 도구의 신뢰성을 위한 일관된 위험 경로 차단 로직의 중요성
- Claude Code에서 보고된, 홈 디렉터리가 통째로 삭제된 실제 사고의, 미미하지만 무서운 메커니즘.
- 「무료 안전 후크를 넣었으니 rm 사고는 괜찮을 것」이라는 믿음의 함정. 내가 배포하고 있는 무료 도구(cc-safe-setup)의 기본 후크에는, 바로 이 사고를 그대로 통과시켜 버리는 허점이 있었다.
- 그 허점을 자신의 환경에서 직접 실측하여 찾아내고, 수정하여, 다음 공개 버전에 반영하기까지. 그리고 독자가 지금 바로 할 수 있는 방어법.
작가는 비엔지니어이며, Claude Code에게 장시간 작업을 맡기며 생활하고 있다. 돌이킬 수 없는 삭제 사고가 가장 무섭기 때문에, 무료 안전 후크를 배포하며 스스로도 사용해 왔다. 그렇기에 이 허점을 스스로 발견하고 솔직하게 기록할 책임이 있다고 생각한다. 전언(들려오는 이야기)과 자신의 환경에서 직접 실측한 것은 명확히 구분하여 작성한다.
이것은 다른 이용자의 보고이므로 전언으로서 작성한다. 어떤 사용자가 Claude Code에게 수 시간의 작업을 맡기고 있었다. 에이전트(Agent)는 테스트용 임시 디렉터리를 만든 후, 정리 작업으로서 파괴적인 삭제 명령을 실행했다. 목적은 해당 임시 디렉터리를 지우는 것이었다.
그런데 삭제된 것은 진짜 홈 디렉터리의 내용물이었다. 서류, 다운로드, 이미지 폴더, 그리고 rclone 설정 파일까지.
왜 이렇게 되었는가. 보고에 따르면 원인은 한 가지 미미한 사실에 있다. 쉘(Shell)의 환경 변수는 Bash 도구의 호출 사이에는 지속되지 않는다. 이전 호출에서 임시 디렉터리를 가리키도록 변수를 설정했더라도, 다음의 다른 호출 시에는 그 설정이 남아 있지 않다. 정리 명령 안의 변수는 임시 디렉터리가 아니라 진짜 홈 디렉터리로 해석(Resolve)되었다. 에이전트는 처음에 명령이 무효화되어 아무것도 하지 않았다고 생각했지만, 실제로는 타임아웃 2초 사이에 삭제가 중간까지 진행되었다.
명령을 입력한 본인(이 경우에는 에이전트)은 그것이 위험한 명령이라고 생각하지 않았다. 「방금 만든 임시 디렉터리를 지울 뿐」이라고 생각했을 것이다. 위험한 단어가 명령의 외형이 아니라, 변수가 해석되는 방식에 숨어 있는 유형이다. 일치하지 않는 mv 이후의 삭제가 폭주하는 사고나, 공백으로 인해 갈라진 경로가 다른 장소를 삭제하는 사고와 뿌리는 같다.
작가는 무료 cc-safe-setup을 배포하고 있다. 그 핵심인 destructive-guard(기본 설치 시 포함되는 후크 중 하나)는 위험한 삭제를 실행 직전에 막는 것이 역할이다. 그렇다면 이 보고된 형태를 막을 수 있을까? 전언으로 남기지 않고, 자신의 환경에서 직접 실측하여 확인했다.
확인 방법은 안전하다. 후크는 명령 문자열을 받아 위험한 형태인지 대조할 뿐, 명령 자체를 실행하지는 않는다. 따라서 사고가 난 명령과 동일한 형태의 문자열을 JSON으로 만들어 후크에 전달하고, 종료 코드(막는다면 2, 통과시킨다면 0)를 확인하는 것만으로, 위험한 조작을 한 번도 실행하지 않고 동작을 파악할 수 있다. 격리된 환경에서 실측한 결과는 다음과 같았다.
- 틸다(
~)에 대한 파괴적 삭제 → 차단됨 (종료 코드 2) - 전개 후의 절대 경로(
/home/사용자명)에 대한 삭제 → 차단됨 (2) - 리터럴 홈 변수에 대한 삭제 (따옴표 유무, 중괄호 유무, 하위 디렉터리 포함 여부와 상관없이) → 그대로 통과됨 (0)
즉, 틸다는 차단되는데 홈 변수는 차단되지 않았다. 보고된 사고의 명령은 바로 이 홈 변수의 형태였다. 기본 설치 상태에서는 이 사고를 막지 못하고 있었다.
왜일까? 핵심 후크의 검사 정규 표현식(Regular Expression)을 직접 읽어보니, 위험한 경로 목록에 틸다도 /home도 절대 경로도 포함되어 있었지만, 홈 변수만은 포함되어 있지 않았다. 틸다 하위는 막으면서 홈 변수 하위는 막지 않는, 일관성의 결여였다.
안전 도구가 지켜야 할 대상을 놓치고 있었다는 것은 도구로서 치명적인 문제다. 신뢰와 직결된다. 그래서 수정했다.
검사 정규 표현식에 틸다와 동일한 위치에 홈 변수의 형태를 추가했다. 앞부분의 임의의 이중 따옴표도 허용하여 따옴표가 붙은 형태도 잡아낼 수 있도록 했다.
수정 후, 21가지의 경우를 직접 실측하여 검증했다.
- 기존 셀프 테스트 8건(루트 삭제,
git reset --hard,sudo가 붙은 파괴 작업, PowerShell 삭제 등)은 모두 변함없이 통과 (회귀 발생 0건). - 보고된 사고의 5가지 형태는 모두 차단됨 (2) 상태가 되었다.
- 그리고 오탐(False Alarm)은 0건이다. 이름이 유사한 다른 변수(
$HOMEBREW_DIR이나$HOME_TMP)나, 안전한 디렉터리 예외(홈 하위의node_modules등)
등), 상대 경로의 build 삭제, echo는 그대로 통과되었다.
이 수정 사항은 cc-safe-setup의 메인 브랜치에 머지되었다 (버전 30.0.2). 다음 공개 시 기본 설치 항목에도 포함될 예정이다. 그때까지는 아래에 기술할 추가 계층을 통해 지금 바로 보호할 수 있다.
이 부분이 실용적인 핵심이다.
첫째, 메커니즘을 기억할 것. 파괴적인 삭제(destructive deletion)를 내용이 비어 있을 수 있는 변수나, 이전 호출의 설정에 의존하는 변수로 향하게 하지 마라. 정리 대상은 절대 경로(absolute path)로 명확하게 작성한다. "임시 디렉터리 용도의 변수"가 사실은 진짜 홈 디렉터리였다는 것이 이번 사고의 핵심이다.
둘째, 추가 계층을 한 겹 더할 것. 무료로 제공되는 cc-safe-setup에는 핵심 기능인 destructive-guard를 넘어서는 추가적인 rm 보호 후크(hook)인 rm-safety-net이 있으며, 이는 홈 디렉터리 변수의 형태를 포함하여 모든 것을 차단한다 (직접 테스트하여 확인 완료). 다음 한 줄로 설치할 수 있다.
npx cc-safe-setup --install-example rm-safety-net
방어는 한 겹보다 두 겹일 때 뚫리기 어렵다.
사고는 발생한 뒤에는 되돌릴 수 없다. 벽은 발생하기 전에만 세울 수 있다. 무료 후크는 그 벽의 첫 번째 층이다. 이번처럼 벽에서 구멍이 발견될 수도 있다. 그럴 때는 숨기지 않고, 찾아내고, 수정하고, 정직하게 기록한다. 그것이 도구를 배포하는 측의 책임이라고 생각한다.
돌이킬 수 없는 삭제나 덮어쓰기 사고를 유형별로 모으고, 실제로 직접 검증하여 방지법까지 정리한 실전서(Anthropic 공식 가이드에는 없는 사고 방지 —— Claude Code 800+시간의 모든 기록 · 800엔)를 별도로 준비해 두었다. 제3장까지 무료로 미리 읽어볼 수 있다. 이 글과 같은 사고 사례를 매달 추가하고 있으며, 기존 구매자에게는 무상 업데이트로 제공된다. 우선은 무료 후크부터 사용해 보길 권한다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기