문제
사용자가 모바일 SSH 세션에 rm -rf /를 붙여넣는 것은 데스크톱만큼이나 쉽습니다. 어쩌면 더 쉬울지도 — 자동 완성, AI 제안, 굵은 손가락의 탭. 키 입력이 서버에 도달하기 **전에** 작동하는 가로채기 계층이 필요했습니다.
순진한 답은 위험한 문자열의 차단 목록입니다. 하지만 셸은 언어이지 고정된 구문의 집합이 아닙니다. rm -rf /, rm -fr /, rm --recursive --force /, r''m -rf /는 모두 같은 의도를 다른 옷을 입고 있는 것입니다. 문자 그대로의 텍스트만 일치시키는 것은 처음 따옴표를 추가하는 사람에게 무너집니다.
고려한 세 가지 접근
- 클라이언트 측 정규식. 전송 전에 위험한 패턴 일치. 빠르지만, 사소하게 우회됨 —
r''m -rf /는 순진한 일치를 피합니다. - 서버 측 래퍼. 모든 서버에 래퍼 스크립트 설치. 신뢰성 있지만, 모든 호스트를 수정해야 함 — 사전 구성하지 않은 서버를 다루는 것이 핵심인 모바일 우선 클라이언트에게는 시작도 못합니다.
- 하이브리드: 클라이언트 측 파싱 + 정규화. 명령을 토큰화, 정규화한 후 엄선된 규칙 집합에 일치시키기. 이것이 우리가 출시한 것입니다.
탐지 파이프라인
탐지는 기기에서 세 단계로 실행됩니다: 정규화, 토큰화, 일치. 작업의 대부분은 정규화기에 있습니다 — 따옴표를 확장하고, 명확한 이스케이프를 해결하고, 명령 구분자로 분할하여 각 세그먼트를 독립적으로 판단합니다.
def is_dangerous(command: str) -> tuple[bool, str | None]:
tokens = shlex.split(canonicalize(command))
for rule in DANGEROUS_RULES:
if rule.matches(tokens):
return True, rule.reason
return False, None canonicalize()는 일치하는 따옴표 쌍(r''m → rm)을 제거하고, 따옴표 외부의 백슬래시 이스케이프를 축소하고, 알려진 안전한 환경 변수 확장의 작은 집합을 치환하며, &&, ;, |로 복합 명령을 분할하여 위험한 세그먼트가 파이프라인 중간에 숨을 수 없게 합니다. 각 세그먼트는 독립적으로 규칙 집합을 통과합니다. 규칙은 코드가 아닌 데이터입니다 — 토큰 패턴과 사람이 읽을 수 있는 이유 문자열을 가진 YAML 팩으로, 무언가 차단될 때 사용자에게 표시됩니다.
여기서 패턴 매칭이 LLM을 이긴 이유
위험 명령을 분류하기 위해 LLM 사용도 고려했습니다. 작동합니다 — 하지만 느리고(키 입력당 300〜800ms 지연), 그 호출 양에서 비싸고, 지나치게 신중합니다: "delete"가 위험해 보인다는 이유로 find . -delete를 차단합니다. 수동으로 조정된 규칙을 사용한 패턴 매칭은 1밀리초 미만의 지연 시간, 완전히 오프라인, 감사 가능 — 명령이 표시된 정확한 이유를 읽을 수 있습니다. 그래서 작업을 나눴습니다: 패턴 매칭은 **탐지**, LLM은 **설명**. 명령이 표시된 후 AI 도우미가 평이한 언어로 이유를 설명할 수 있지만, 모든 키 입력의 핫 패스에 절대 앉지 않습니다.
Y/n 자동 응답
ShellMon이 해결하는 별도의 문제: apt upgrade가 Do you want to continue? [Y/n]를 묻고 사용자는 화면을 켜둔 채로 답하기를 원합니다. 어려운 부분은 Y를 입력하는 것이 아닙니다 — 세션이 **입력 대기 중**인지 아직 작업 중인지 아는 것입니다.
# 시작하는 각 명령에 추가되는 센티넬
cmd; __ec=$?; printf '\n__TERMAI_END_%s__%d__\n' "<hex>" "$__ec" 센티넬은 이중 역할을 합니다. 출력 스트림에 나타나면 명령이 끝났고 프롬프트를 파싱하지 않고도 종료 코드를 캡처할 수 있음을 압니다. **나타나지 않고** 출력이 알려진 프롬프트 패턴([Y/n], (yes/no))으로 끝나는 줄에서 조용해지면, 세션이 입력에서 차단되었다고 확신합니다 — 그때만 자동 응답 규칙이 작동합니다. 무작위화된 16진수는 우연히 "END"를 포함하는 프로그램 출력에 대해 마커가 충돌하지 않게 합니다.
잡을 수 없는 것
솔직한 한계:
- 사용자 정의 바이너리.
./my-script.sh가 무엇을 하는지 검사할 수 없습니다. 호출만 확인하고 내용은 확인하지 않습니다. - 파이프 체인. 중간에
grep/awk/jq를 끼운 긴 파이프라인은 위험한 패턴을 통과시킬 수 있습니다. 명백한 경우는 차단하지만 모든 적대적 예를 잡지는 못합니다. - 의도적 오용. 서버를 지우고 **싶어 하는** 사용자는 방법을 찾습니다. ShellMon은 안전망이지 접근 제어가 아닙니다.
출시한 것, 다음 할 것
ShellMon은 TermAI v0.9에서 출시되었습니다. 그 이후 사용자 정의 규칙 팩, 긴 작업 완료 시 Pro 등급 푸시/이메일 알림, 명령 설명을 위한 AI 도우미 통합을 추가했습니다.
다음: 스니펫 인식 모드 — 자신의 라이브러리에서 검증된 스니펫을 실행하는 경우, ShellMon은 더 신뢰하고 더 조용해집니다. 그리고 사용자가 감사하고 우리가 놓친 패턴에 기여할 수 있도록 GitHub에서 규칙 집합을 공개합니다.
Free on iOS and Android. 3 SSH connections + 20 AI calls/day on the free tier.