課題
モバイル SSH セッションにユーザーが rm -rf / を貼り付けるのは、デスクトップと同じくらい簡単です。むしろもっと簡単かもしれません ── オートコレクト、AI の提案、太い指でのタップ。キーストロークがサーバーに届く**前**に動作する遮断層が必要でした。
素朴な答えは、危険な文字列のブロックリストです。しかしシェルは言語であって、固定のフレーズ集ではありません。rm -rf /、rm -fr /、rm --recursive --force /、r''m -rf / は同じ意図を別の衣装で身につけているに過ぎません。文字どおりのテキストだけをマッチさせる方法は、最初に引用符を加える人によって破られます。
検討した3つのアプローチ
- クライアント側の正規表現。送信前に危険なパターンをマッチさせる。速いが、簡単に回避される ──
r''m -rf /は単純なマッチを潜り抜けます。 - サーバー側のラッパー。すべてのサーバーにラッパースクリプトをインストール。信頼できるが、すべてのホストを変更する必要がある ── 事前設定していないサーバーに触れることが要のモバイル中心クライアントには、これは始められません。
- ハイブリッド:クライアント側パース + 正規化。コマンドをトークン化、正規化し、厳選したルールセットに対してマッチさせる。これが私たちが出したものです。
検出パイプライン
検出はデバイス上で3段階で実行されます:正規化、トークン化、マッチング。仕事のほとんどは正規化器にあります ── 引用符を展開し、明らかなエスケープを解決し、コマンド区切りで分割して各セグメントを独自に判断します。
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 の遅延)、その呼び出しボリュームでは高価、過度に慎重:find . -delete を「delete」が危険そうに見えるからブロックします。 手調整のルールによるパターンマッチングは、サブミリ秒のレイテンシ、完全オフライン、監査可能 ── コマンドがフラグされた正確な理由を読めます。だから仕事を分けました:パターンマッチングが**検出**、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.