AI 에이전트가 rm -rf를 실행하는 것을 막는 방법 (그리고 문자열 매칭만으로는 충분하지 않은 이유)
요약
AI 에이전트가 시스템 명령을 통해 위험한 삭제(rm -rf)를 실행하는 것을 방지하는 방법을 논합니다. 단순히 문자열 매칭 규칙에 의존하는 것은 우회되기 쉬우므로, 권한 제한과 환경 설정을 통한 다층적 접근 방식이 필수적입니다.
핵심 포인트
- 단순 문자열 매칭은 안전장치가 아닌 경고등일 뿐이다.
- 컨테이너/VM에서 에이전트를 특권 없는 사용자로 실행해야 한다.
- 읽기 전용 마운트와 커밋을 통해 피해 범위를 최소화하라.
- 에이전트 수준의 거부 규칙(deny)을 활용하되, 그 한계를 이해해야 한다.
코딩 에이전트에게 셸 명령을 실행하도록 허용하면, 언젠가는 무언가를 삭제하려고 할 것입니다. 보통은 해롭지 않습니다. 재빌드 전에 dist/를 정리하는 정도입니다. 가끔은 변수가 비어 있는 상태에서 rm -rf "$BUILD_DIR/"이거나, 에이전트가 절대 신뢰해서는 안 되는 README에 의해 제안된 정리 단계일 수 있습니다. 이 글은 사소한 경우를 제외하고, 정말 중요한 상황에서 AI 에이전트가 rm -rf를 실행하는 것을 막는 방법에 관한 것입니다.
핵심 교훈: 문자열 rm -rf를 찾는 규칙은 안전장치(guardrail)가 아니라 경고등(tripwire)입니다. 제가 테스트 결과와 함께 정확히 어디서 실패하는지, 그리고 그것을 어떻게 보강할 수 있는지 보여드리겠습니다.
에이전트가 rm -rf를 사용하는 이유
세 가지 일반적인 경로가 있습니다:
- 정당한 정리. "빌드를 깨끗하게 하고 다시 시도하세요"는 완벽하게 합리적인 지시이며,
rm -rf build는 이를 수행하는 완벽하게 합리적인 방법입니다. - 변수 확장.
rm -rf "$OUT_DIR/"은OUT_DIR가 비어 있어서 명령이rm -rf "/"가 될 때까지는 괜찮습니다. 에이전트는 명령을 빠르게 조합하며,set -u같은 보호 장치를 거의 추가하지 않습니다. - 주입된 지침. README나 이슈 댓글 또는 도구 결과에 "환경을 재설정하려면 다음을 실행하세요…"라고 적혀 있습니다. 모델은 도움을 주려고 노력하고 있는 것입니다.
삭제를 금지하는 것으로 첫 번째 경우를 해결할 수 없으며, 모델에게 조심하라고 요청하는 것만으로는 세 번째 경우를 해결할 수 없습니다. 당신은 모델의 판단에 의존하지 않는 계층(layers)이 필요합니다.
레이어 1: 피해 범위를 작게 만들기
규칙을 적용하기 전에, 잘못된 삭제가 도달할 수 있는 범위 자체를 줄여야 합니다:
- 컨테이너나 VM에서 에이전트를 특권이 없는 사용자(unprivileged user)로 실행하고, 프로젝트만 마운트합니다.
- 에이전트가 변경해서는 안 되는 것은 읽기 전용으로 마운트합니다. 읽기 전용 바인드 마운트는 명령어가 어떻게 철자되었는지 신경 쓰지 않습니다.
- 위임하기 전에 커밋(또는 스태시)합니다. 깨끗한
git status는 대부분의 작업 공간 삭제를git checkout .으로 만듭니다. - 에이전트가 건드릴 수 있는 git 외부의 모든 것은 백업을 유지합니다.
이는 명령어 구문에 무관한 유일한 계층입니다. 그 이후의 모든 것은 의도를 더 일찍 포착하는 것에 관한 것입니다.
레이어 2: 에이전트 수준 거부 규칙
대부분의 코딩 에이전트는 명령어 패턴을 거부할 수 있도록 허용합니다. 예를 들어 Claude Code에서는 다음과 같습니다:
{
"permissions": {
"deny": ["Bash(rm -rf *)", "Bash(rm -fr *)"]
...
}
Gemini CLI 등 다른 도구들도 유사한 메커니즘을 가지고 있습니다. 이를 사용하되, 그것이 무엇인지 알아야 합니다. 이전 Gemini CLI 문서는 명확하게 말했습니다: 단순 문자열 매칭 기반의 명령어별 제한은 "쉽게 우회될 수 있다". 이는 어떤 접두사나 부분 문자열 규칙에도 마찬가지입니다.
문제 증명: 리터럴 규칙 대 실제 철자법
다음은 리터럴 문자열에 대한 거부 규칙 하나와 관대한 셸 규칙을 Cirvix 정책 DSL로 작성한 것입니다 (여기서 command = "rm -rf"는 contains 매칭으로 컴파일됩니다):
deny:
name = deny-destructive-shell
tool = shell.exec
...
이에 대해 cirvix policy test를 실행한 결과 (패키지 버전 0.3.0):
✓ rm -rf → deny (deny-destructive-shell)
✗ rm -fr line 15
expected deny
...
철자법 하나는 잡아냈지만, 세 개를 놓쳤습니다. 하지만 risk 열을 주목하세요: 엔진은 이미 rm -fr과 rm -r -f를 CRITICAL로, 그리고 find -delete를 HIGH로 분류했습니다. 리터럴 규칙은 그저 묻지 않았을 뿐입니다.
레이어 3: 명령어를 분류하고, 그 클래스를 결정하다
해결책은 명령어의 철자가 아니라, 그 명령어가 _무엇인지_에 대해 규칙을 작성하는 것이며, 위험한 클래스를 와일드카드가 allow로 덮지 못하게 하는 것입니다:
deny:
name = deny-rm-rf-literal
tool = shell.exec
...
동일한 종류의 테스트 케이스와 몇 가지 더 현실적인 케이스를 추가했습니다:
✓ rm -rf → deny (deny-rm-rf-literal)
✓ rm -fr → deny (deny-critical-shell)
✓ rm -r -f → deny (deny-critical-shell)
...
무엇이 바뀌었는지:
- 재귀적 강제 삭제(Recursive force-deletes)는 플래그 순서와 관계없이 클래스 단위로 거부됩니다.
- 분류기가 안전하다고 인식하지 못하는 명령어들 —
find -delete,python -c "shutil.rmtree(...)",git clean -fdx등은 조용히 허용되는 대신 사용자에게 '보류(held)' 처리됩니다. 이것이 제가 특성화할 수 없는 임의 실행에 대한 올바른 기본값입니다. - 일상적인 명령어들은 여전히 실행됩니다.
npm test는 짧고 고정된 허용 목록(allowlist)에 포함되어 중단되지 않습니다.
분류기는 의도적으로 규칙 기반이며 모델이 아닙니다. 즉, 동일한 명령은 항상 동일한 위험 수준을 가지며, 체이닝 또는 치환 문자(;, &&, |, $()를 사용하는 명령어는 안전 허용 목록에서 제외됩니다. 따라서 npm test; rm -rf ~는 npm test에 편승할 수 없습니다.
레이어 4: 정책을 코드로 테스트하기
어떤 엔진을 사용하든, 악성 스펠링 목록을 유지하고 CI(지속적 통합) 환경에서 실행해야 합니다. 시작 세트 예시입니다:
rm -rf ./build rm -fr ./build rm -r -f dist
rm -rf "$EMPTY/" find . -delete git clean -fdx
python3 -c "import shutil; shutil.rmtree('x')"
...
만약 새로운 규칙이 원래는 통과해서는 안 될 명령을 통과시킨다면, 이는 사고(incident)가 아닌 풀 리퀘스트(pull request)에서 발견하게 됩니다.
이 정책이 다루지 않는 것들
정책은 자신을 통해 라우팅되는 호출만을 볼 수 있습니다. 만약 에이전트가 다른 방식으로 셸(shell)을 생성하거나, 사용자가 내용을 검사한 적 없는 스크립트를 실행한다면, 정책은 외부 명령어(./scripts/clean.sh)를 평가합니다 — 이 경우 위의 규칙에 따라 인식되지 않은 것으로 보류됩니다. 이것은 좋은 기본값이지만, 스크립트 자체의 가시성을 제공하지는 않습니다. 레이어 1을 유지해야 합니다.
Cirvix로 테스트하기
Cirvix AgentControl은 거버넌스된 AI 에이전트 도구 호출(governed AI agent tool calls)을 위한 오픈 소스 정책 엔진입니다. 이는 Claude Code 훅을 통한 셸 명령어, 게이트웨이를 통한 MCP 호출, 또는 Node/Python SDK로 래핑된 함수를 대상으로 합니다. 위의 정책들은 다음 명령으로 검증하고 실행됩니다:
npm install -g @cirvix_ai/agent-control
cirvix policy check --policy shell.policy
cirvix policy test --policy shell.policy
해당 레포지토리의 policies/default.policy에는 더 광범위한 기준선(파괴적 셸, 히스토리 재작성, 패키지 설치, 프로덕션 배포 등)과 자체 테스트 케이스가 포함되어 있습니다. Cirvix는 프롬프트 인젝션을 막는 것이 아니라, 이를 통해 라우팅되는 호출만을 제어합니다. 즉, 명령이 실행되기 전에 검증되고 설명 가능한 결정을 제공하는 것입니다.
레포: https://github.com/CIRVIX/agent-control
문서 및 가이드: https://cirvix.com
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기