- GitHubで16.1万スターを獲得しているオープンソースのAIコーディングエージェント OpenCode をローカルのQwen3.6-27Bで試したところ、ツール品質とセキュリティ設計のどちらも利用をやめるべき水準だった
- AGENTS.mdの再読み込み、固定距離のコンテキスト枝刈り、現在日付の挿入、モード切り替えが プロンプトキャッシュ を繰り返し無効化し、M4 Maxでも応答生成まで最大10分かかることがある
- セッション圧縮、システムプロンプト、権限確認、サブエージェント制御、TUIがうまく噛み合わず、コンテキストとメッセージが失われ、重要な仕様を忘れたままコードを書いてしまうことがある
- Bash ASTと文字列パターンに依存する 権限フィルター は、間接実行、絶対パス、変数、Python、リダイレクトなどを防げず、外部ファイルアクセス制限や永続権限も容易に回避できる
- リモートモデルへのデフォルト接続、無制限のインターネットアクセス、過去のHTTPサーバーRCE脆弱性を考えるとDockerだけでは不十分であり、実行ファイルのブロック、読み取り専用パス、OSレベルの隔離 が必要
評価範囲と前提
- OpenCodeは開発元が AIコーディングエージェント として紹介しているプロジェクトであり、検証時点のGitHubスター数は16.1万だった
- ローカルLLMである Qwen3.6-27B とOpenCodeのGitバージョン
baef5cd4を基準に試験した - これは独立したセキュリティ開示というより、LLM出力をBashに渡す構造でパイプライン層がどのように失敗するかを見たもの
- LLMの利用そのものと、ユーザーのマシンが簡単に侵害されたり削除されたりし得るかは別の問題として区別する
プロンプトキャッシュを繰り返し壊す構造
- OpenAI
/v1/chat/completions系APIは、これまでの会話全体をJSONとして送り、JSONメタデータ付きのSSEデルタストリームで応答する- セッションが長くなるほどアップロードコストは 二乗で増加 する
- ツール呼び出しは、複数のJSONデルタを再びJSONに組み立てる二重エンコードを使う
- サーバーはステートレス構造だが、性能のために評価結果をキャッシュする
- リクエストと一致する最長のキャッシュ接頭辞を探す
- 接頭辞の末尾から最後のメッセージまでプリフィル(prefill)する
- 終了トークンまで新しいトークンを生成する
- 約0.5TB/sのメモリ帯域幅を持つM4 Maxでは、Qwen3.6-27Bのトークン生成は実用的だったが、長いコンテキストのプリフィルは計算量が非常に大きかった
- 適切な接頭辞キャッシュを見つけられないと、GPUを最大限使いながら 約10分 待った後でようやく応答生成が始まることがある
- OpenCodeはSSEの各ターンごとにファイルシステムをglobし、最初のシステムプロンプトに挿入する
AGENTS.mdを再読み込みする- 次のセッションのために
AGENTS.mdを修正しても、現在のセッション全体が再評価される
- 次のセッションのために
- エージェントからユーザーへ切り替わるときにツール呼び出しコンテキストを枝刈りし、大きなキャッシュ区間を無効化する
PRUNE_PROTECT = 40_000より古いツール結果を捨てるため、最善の場合でも 4万トークンのキャッシュミス が発生する- 中断もユーザーへの切り替えとして扱われるため、誤った進行を修正するとキャッシュが破棄され、再び待たされる
- 最初のシステムプロンプトに 現在日付 を入れ、SSEの各ターンで再評価するため、日付が変わると全体のキャッシュミスが発生する
枝刈りとセッション圧縮
- 枝刈りは
skillを除くすべてのツール結果に同じように適用され、初期に読んだ重要な資料を別途保護しない- 新しいセッションで仕様を最初に読む
- 関連コードを追加で読み、4万トークンの基準を超える
- モデルが不要な推論や誤った方向に入り込み、ユーザーが中断する
- 中断により仕様がコンテキストから削除される
- モデルは元の仕様を参照できないまま実装する
- セッション圧縮(compaction) は、既存セッションの前に新しいプロンプトを付け、全体を再びプリフィルした後、いくつかの箇条書きに要約する
- 要約プロンプトをセッション末尾に入れれば、全体のプリフィルを避けられる
- モデルに引き継ぎメモをファイルとして直接書かせる方式のほうがうまく機能し、結果を編集したり複数セッションで再利用したりもできた
- 圧縮は有限のコンテキストウィンドウを無限であるかのように見せる漏れのある抽象化であり、枝刈りと併用すると問題が大きくなる
- コンテキストウィンドウとプロンプトキャッシュを基本制約として認め、管理手段を提供するほうがよく、Piのセッションツリー はプロンプトキャッシュを意図的に活用している
システムプロンプトとPlanモード
- デフォルトのシステムプロンプトは非常に長く、かなりの分量がモデルに簡潔に答えるよう指示するために使われている
- サブエージェントに
ABSOLUTELY NO COMMENTSと指示させるなど、強いコーディング嗜好も含まれている - PlanからBuildへ渡す過程が滑らかではなく、計画を十分に具体化するとコンテキストウィンドウの終端に近づくことがある
- 議論の結果をファイルに書き、編集したうえで新しいセッションに渡す方式を好む
- Planモードの通知はどのディレクトリにも書き込めないと言うが、実際には
.opencode/plansに書き込める- 指示していないのにそのディレクトリへ書いたり、明示的に要求しても書き込みを拒否したりする、両方の失敗を経験した
- デフォルトのシステムプロンプトをグローバルに修正できず、プロジェクトごとにコピー する必要がある
- Buildモードのプロンプトだけを再定義すると、Planモードへ切り替える際に全体のプロンプトキャッシュミスが発生する
- モデル別プロンプトは内容と品質の差が大きい
- GPT-4、o1、o3向けの Beast Mode は、サードパーティパッケージや依存関係を理解するにはGoogleでの検証が必須だと指示している
権限確認が生む判断疲れ
- プロジェクト外のファイルアクセスをその場しのぎの文字列分析で検知すると、
Yes、No、Alwaysの権限ウィンドウを表示し、応答があるまで実行を停止する- 今後同じ作業を拒否し続ける
Neverの選択肢はない
- 今後同じ作業を拒否し続ける
- サブエージェントが
/tmpのスクリプト出力を読もうとしたときにNoを選ぶと、エージェントが終了し、作業コンテキストも消える- 進行内容を保存するには、望まない外部アクセスでも
Yesを選ばなければならない状況が生じる
- 進行内容を保存するには、望まない外部アクセスでも
- 許可要求が繰り返され、生産性を維持する選択肢が
Yesしかなければ、ユーザーは危険な要求まで承認してしまう可能性がある - ディレクトリ外への書き込みを防ぐ基本的な安全機構が、人間の継続的な注意力 に依存してはならない
メッセージとサブエージェントの相互作用
- SSEストリーミング中に送ったメッセージはキューに入るが、実際の送信タイミングが不明確
- コード上はツール呼び出しターンの終了時に送信するように見えるが、待機中のメッセージを送らないままツールから思考過程へ移る状況も経験した
- ユーザーが中断すると、メッセージは待機状態から外れてログにだけ残り、送信できなくなるため、新しいストリームを開始するための2つ目のメッセージが必要になる
- メッセージの取り消しがログからそのメッセージを削除できない場合もある
- サブエージェントと直接会話したり、進行を中断させたりできない
- 誤った方向へ進むと、終了させてコンテキストを失うか、トークン消費を見守るしかない
- 過去にはこの機能があったようだが、現在はなくなっている
- 基本チャットでサブエージェントを
@mentionしても有用には機能せず、中断もできない
- Qwenが思考過程にツール呼び出しを入れるように、サブエージェントのツール呼び出しが失敗すると致命的エラーが発生し、それまでのコンテキストが消える
- サブエージェントの再利用は、小さなコンテキストに作業を分離する目的と衝突する
- 無関係な作業に既存のサブエージェントが再利用されることがある
- 大きなコンテキストを持つメインエージェントとサブエージェントの間を行き来し、キャッシュミスを引き起こす
- 人間向けの相互作用は豊かでもよいが、モデルに提供する選択肢は減らすべき
- サブエージェントの動作に関する GitHub issue もある
エージェントツール設計
editは基本的に、一意に一致するテキストを 正確検索・置換 する- モデルはファイル内容を正確に覚えていても、複数回の編集後に変わった行番号は見落とすことがあり、この方式に適している
- グローバル置換オプションは複数回の後続修正を誘発しており、これを取り除くとPiの
edit設計と同じになる
- Planモードの選択式
questionツールは、システムプロンプトで自然言語で質問させるより不便 grepとglobはbashで代替でき、実際にモデルもgrepやrgをBashで実行するExploreのような読み取り専用エージェントがBashを使えないよう制限する目的かもしれない- これはBashコマンドを実行せずに副作用を判定するのは難しいという問題につながる
todoは概ね有用だが、モデルがTODO確認そのものを忘れる
TUIとドキュメント品質
- OpenCodeのTUIはテキストレンダリングに 約1GBのRAM を使う
- メッセージ入力欄でShift+Enterの改行が動作せず、既存issueは「自分のコンピュータでは動く」という返答後に終了された
- 長いメッセージが自動改行されると、入力欄とカーソルは移動するが、新しい行の文字が見えないことがある
- ストリーミング中にテキストを選択すると、自動スクロールで選択が解除される
Ctrl-Cが実行中のコマンドを中断するのではなく、セッションを即座に閉じる- 対話型シェルの慣例なら、
Ctrl-Cはコマンドを中断し、実行中のコマンドがないときにCtrl-Dがセッションを終了するべき
- 対話型シェルの慣例なら、
- MacのOption+左右矢印のような一般的な単語単位移動ショートカットをサポートしていない
- メッセージや思考過程が長くなるとMarkdownの再レンダリングなどに数秒かかり、二乗時間複雑度 に見える性能問題がある
- 入力の問題のため、外部エディタでメッセージを書いて貼り付ける必要があった
- ドキュメントは一貫性がなく、人間ではなくモデルに読ませるために書かれた形に近い
リモート優先接続とデータ露出
- OpenCodeはデフォルトで リモートモデル に接続する
- ローカルモデル設定の簡単な例がドキュメントになく、設定を誤るとリモートモデルに接続される
- ローカルモデルを正しく指定しても、プログラム起動後に対話的に選択する必要があり、その間にリモートモデルとローカルシェルはすでに接続された状態になる
- デフォルトのモデルURLは配布物に固定されておらず、OpenCodeと連携する models.dev からダウンロードする
- 関連コードは
opencode/src/provider/provider.tsの1684行目にある
- 関連コードは
- 新規インストール後に
opencodeを実行し、1文字とEnterを入力するだけで、ユーザー設定なしにリモートモデルがローカルシェルに接続され得る - 最初のメッセージが空または曖昧だと、エージェントモデルは現在ディレクトリをglobし、ファイルを読むことが多く、読まれたデータは次のPOSTリクエストに含まれる
インターネットアクセスとシステムプロンプト
- OpenCodeは
WebFetchツールを提供し、システムプロンプトでそれを使うよう明示的に指示している - デフォルトプロンプトは、ユーザーが提供したメッセージやローカルファイル内のURLを使えるようにしつつ、プログラミング支援URLだと確信できる場合にはURLを生成または推測できるよう曖昧に許可している
- Bashにはネットワークサンドボックスがないため、
WebFetchより大きな問題は、モデルがcurl | bashのようなコマンドを実行しないだろうと期待する構造にある
Bash権限フィルターを回避する方法
opencode.jsonの"bash": {"git *": "deny"}は、git statusやecho hello && git push --forceをブロックする- 実装はtree-sitterのBash・PowerShell文法でコマンドをASTとして解析し、コマンドノードを走査して設定から作った正規表現と比較する
- しかしテキストベースの検査は、さまざまな間接実行を許してしまう
echo 'git clean -fdx .' | bashenv git status- aliasで
gitを別のコマンド名に結び付ける方法 /usr/bin/git status,$(which git) statusGIT=git && $GIT status- Base64でエンコードした
git reset --hardをデコードしてBashに渡す方法 - heredoc内の
git push --force - Python
subprocess.runでgit checkout .を実行
- モデルが通常は悪意を持たなくても、失敗を粘り強く回避するよう訓練されているため、自然に 敵対的入力のように振る舞う ことがある
- 文字列コマンドフィルターは安全機構ではなく、誤った安心感を与える
永続権限とCWD例外
python3 -c 'print("hello")'でAlwaysを選ぶと、python3接頭辞全体が永続的に許可される- その後PythonでSSH秘密鍵を読むコマンドも、すでに承認済みとして扱われる可能性がある
- 権限はディスクに保存され、以後のセッションにも維持される
cd、chdir、popd、pushd、push-location、set-locationは副作用がないと仮定する CWD例外リスト に含まれる- これらのコマンドは、すべてのBashコマンドを拒否するよう設定しても、権限検査を明示的に回避する
ファイルアクセス検査の穴
- デフォルト設定は、OpenCodeを実行したディレクトリとGitリポジトリのうち短いほうのパスの外にあるファイルへのアクセスを防ごうとする
- Bashツールではtree-sitter ASTを走査して、パスらしき値を解釈し検査する
cat /tmp/logfileは権限を要求するpython3 -c 'import shutil; shutil.rmtree("/")'は検査できない
cargoはグローバルな~/.cargoで読み書きや実行を自由に行うが、モデルが~/.cargo/registry/srcのパッケージソースを直接読もうとすると権限を要求する- ファイルにアクセスできると見なすコマンドは、固定された
FILESリスト に限られているrm、cp、mv、mkdir、touch、chmod、chown、catと一部のPowerShellコマンドなどが含まれる- リストにないコマンドはファイルにアクセスしないと仮定されるため、渡されたパスも検査されない
リダイレクトと許可済みコマンドの組み合わせ
echo "hello world!"でAlwaysを選ぶと、その後echoを使ったファイルやデバイスへの書き込みも許可される- GPIO関連の
/sys/class/gpioパスへリダイレクトするコマンドも実行できる
- GPIO関連の
echo foo > bar.txtのASTでは、redirectionはcommandの子ではなく兄弟ノードである- パス検査は
commandの子だけを対象にするため、リダイレクト先を検査しない echo自体もFILESリストにないため、パス検証が開始されない
- パス検査は
自己アップグレードとリモートコード実行の事例
- OpenCodeには複数の自己アップグレード経路があり、curlベースのインストール版で
opencode upgradeを実行すると、https://opencode.ai/installの応答をダウンロードして Bashの標準入力として実行 する - curlインストーラを使った時点のリスクと大きくは変わらないが、本番環境でリモートスクリプトを直接実行する事例である
- CVE-2026-22812 当時、OpenCodeはデフォルトのHTTPサーバーで次の機能を公開していた
- 完全に許可的なCORSヘッダー
- 任意のシェルコマンドを実行するPOST API
- 任意のファイルを読むGET API
- ユーザーが訪問したWebサイトが既知のデフォルトポートへリクエストし、ユーザー権限レベルのシステムアクセスを得られた
- 開発チームはサーバーをデフォルトで無効化し、
opencode.aiがマシン上でリモートコードを実行できるようCORS例外が必要だと回答した後、追加対応を続けず、issueはstale botにより閉じられた - 別の issue は、認証コマンドがユーザーから渡された任意のURLからコンテンツを取得して実行すると報告しており、これもstale botで終了された
Dockerだけでは解決できない理由
- 開発依存関係を新しいマシンにインストールするのが難しいほど複雑にしたうえでDockerに依存する方式は望まない
- Docker自体もセキュリティ問題を生み得る
- rootで動作する強力なサービスを作る
ufwファイアウォールに意図的に抜け道を作る
- 保護すべきすべてのデータがコンテナ内にあり、内部のローカルシェルがインターネットに接続されているなら、保護範囲は不明確になる
- 目的がルートファイルシステムの再帰削除防止なら、Landlock、Seatbelt、Restricted Tokens のような、より直接的なOS手段を使える
- コーディングエージェントのセキュリティは別コンテナに責任を渡す問題ではなく、ハーネスの最優先事項であるべき
- Gitのブロックはコマンド文字列ではなく、
git実行ファイル自体を防ぐべき .gitディレクトリは読み取り専用にすべき- Bashコマンドをテキストとして無害化するのではなく、ネイティブなOS隔離を使うべき
- Gitのブロックはコマンド文字列ではなく、
ローカルLLMの利用体験
- Qwen3.6-27Bのようなローカルモデルも、フロンティアモデルのようにコードベースの安定性と概念的一貫性を損ない得るが、3つの違いがある
- 知的に見えた後で愚かに振る舞う不気味の谷が少なく、限界が明確なため相互作用を調整しやすい
- 重みの数が訓練データをそのまま再現するには少なく、出力物の汚染有無に対する判断が変わる
- クラウドプロバイダーを支援したり依存したりしなくて済む
- コードと症状、推定原因を与え、関連コードを読ませた後で 呼び出し経路とコード引用 を求める入力中心の検索作業では、有用な結果が得られた
- 検索問題として範囲を定めると、モデルが事実を作り上げる傾向を減らせる
- コード生成はアーキテクチャ計画を繰り返し崩す
- 複数のコンポーネントが共有するよう、設計途中で可変状態を移すといった近道を取る
- 自分で書いていないという問題を超えて、コード理解能力そのものを損なう
- モデル重みの知識から直接答えを取り出す方式は、数兆パラメータのモデルでも幻覚を起こす
- LLMが普通のツールになるには、周辺ソフトウェアに実際の システムエンジニアリング を適用してセキュリティの空白をなくす必要があり、その作業は人間が行わなければならない
1件のコメント
Hacker News の意見
この記事のより良いタイトルは「修正すれば OpenCode が改善される些細な不便」くらいに見える
AGENTS.mdを毎回読み直したり、日付変更でプロンプトキャッシュミスが起きたりするのは許容できる圧縮と枝刈りがうまく動かない問題は Codex や Claude でも見たし、デフォルトのシステムプロンプトも一貫性のためのものなので、気に入らなければ変えればいい
安定性・性能・メモリ使用量もすべて悪化しており、以前は OpenCode が好きだったが、よく書かれたソフトウェアとは見なしにくい
今は Pi に完全に置き換えたし、OpenCode から学んで必要なところにより抑制の利いた設計を適用した新しい選択肢もかなりある
もうツール呼び出しの枝刈りはしていないが、限られたコンテキストウィンドウで同じ作業を長く続けるには現在の進捗を要約する必要があるので、圧縮は当面は必要悪だ
現在ベータ版の V2 には、
AGENTS.mdや利用可能な技術など、変化するシステム指示を最新に保ちつつ、キャッシュミスをできるだけ避ける新しい方式が入っているhttps://x.com/kitlangton/status/2075749116760457346/video/1
別の「Alarming Things」節には「It’s Fucking Full of RCEs」という小節もあり、前の節で明らかになった問題から生じるもの以外にも、複数のリモートコード実行脆弱性がある
エージェント型 CLI の危険をよく整理しているが、OpenCode だけに焦点を当てたタイトルは二つの理由で変だ
第一に、明確な代替案を提示していない。多くの問題が根本的なので、ほぼ最初から再設計して書き直す必要があるかもしれず、単に OpenCode の修正案を出すだけでも十分ではないが、建設的な提案がまったくなく、事実上「LLM の使用をやめろ」という記事のように見える
第二に、「Alarming Things」の主要な問題は OpenCode 固有のものではなく、Claude CLI や、おそらく他の最先端モデル提供企業のエージェントにもすべて当てはまる
それでも、より良いツールを一から作るよう促す記録としては大きな価値があるので、ブックマークして広く共有するつもりだが、本文が優れている分、タイトルと焦点はいっそう的外れに感じる
特に
echo git | bashがまだ実行されるという不満は、ばかげているように見える「OpenCode を知らないなら、人間の顔を永遠に踏みつけるブーツを想像してほしい。そのブーツは TypeScript で作られており、その顔は 1940 年代の電子式コンピュータ発明以降、私たちがセキュリティとシステムソフトウェアについて学んできたすべてだ」という文は、こじつけの比喩部門の Bulwer-Lytton 賞候補だ
文章の文体が過度に怒りに満ち、容赦がない
多くの論点にはおおむね同意するが、OpenCode を「セキュリティ態勢が『パパ、かがんであげるよ』レベルのピエロ車ターボごみ」と呼び、全員に使用をやめろと言うくだりからは読みたくなくなった
このソフトウェアも普通の人々が作ったものなのに、オープンソースをこんなふうに攻撃するのがいつから当然になったのか分からないし、自分が作ったソフトウェアがこんな評価を受けたらどう感じるか考えてしまう
昔の
comp.lang.lispでも、象牙の塔の基準に満たないコードを書いた人をけなして楽しむ人たちがいて、去った人もいれば、腕を上げるのに必要な叱責だと勘違いして勲章のように受け取った人もいた関連する昔の HN 議論: https://news.ycombinator.com/item?id=587045
こうしたレトリックが巻き添えを食う開発者と、その行為を正常化する当人自身にどれほど有害か理解していないようだ
python3の例に異議を唱えたかもしれない」というくだりは面白かった顧客先の技術スタックの都合で Claude Code を使い、個人作業には特定バージョンの OpenCode を使っていたが、OpenCode のほうがずっと良かったので、この記事は悲しく感じる
これまで見ても見過ごしてきた異常な挙動がすべて記事と一致し、原因まで説明されている。誇張表現や感情的に同意できない部分を除けばおおむね正しいので、別の実行ツールを探す必要がありそうだ
Pi のアーキテクチャが実際により良いのか、あるいはもっと良い代替があるのか、おすすめが知りたい
欠陥とは関係なく、複数のツールを一通り使った中で、OpenCode が最も生産性が高かった
記事に出ている内容の大半は些細な不便や見解の違いであり、特にコマンドフィルタリングの目的を根本的に誤解している。これはセキュリティ機構ではなく、モデルの振る舞いを誘導するためのものだ
筆者が OpenCode で実際に何かを作ったようには見えず、使ったのなら最も重要な成果物の品質にまったく触れていない
特に計画モードを簡単に使って素早く作業を終えられる
OpenCodeからPiに乗り換えたところ、ツール呼び出しの性能が大幅に良くなり、バグも少なく感じられるようになった
OpenCodeはhttps://openrouter.ai/apps/category/codingからも消えたように見える
記憶では、一方は設定で確認を有効にでき、もう一方はプラグインが必要だった
この挙動はサプライチェーン攻撃のリスクをさらに高める
テキストを表示するだけのTUIデスクトップアプリが、ネイティブアプリはもちろん、ほとんどのブラウザベースのデスクトップアプリより重いというのは馬鹿げており、RAM・CPU・エネルギー・バッテリーを無駄にしている
C++ Qt6で独自のAI実行ツール兼チャットアプリを開発中だが、サブエージェント、コード差分、ターミナルエミュレーター、簡単なエディター、Markdownプレビュー、半透明背景、ユーザーテーマ、権限、MCP、Git統合、ドッキングシステム、プロジェクトタブまで備えていても、ほかのツールより軽い
まだいくつかのバグを取り除き、UIを単純化して磨き込んでいるところなので公開していない: https://zeteo.krysoph.com/preview.html
OpenCodeがコメントを削除していた理由が、デフォルトのシステムプロンプトにある「Use ABSOLUTELY NO COMMENTS」だったと今わかり、非常に腹立たしい
ただし、些細な不便ではなくセキュリティリスクという点では、ほかの実行ツールにも当てはまる。これらのツールは膨大なデータにアクセスし、ほぼ毎日更新され、雰囲気コーディングされた性質上、取り込んでいる多数のnpm依存関係をきちんと監査している人がいない可能性が高い
left-padのような事件が一度でも起きれば、サプライチェーン全体にとって災厄になり得るシステムプロンプトに日付を入れて、午前0時にキャッシュが無効化されるのは合理的な判断であり、ほかの実行ツールもほとんど同じ方式を使っている
完全な日付と時刻を入れるなら無責任だろうが、OpenCodeはそうしていない
日付をセッションごとに一度だけ、または長時間実行されたセッションが過去の日付に留まり続けるのを防ぐために、
opencodeバイナリを実行するたびに一度だけ評価すれば簡単に解決できる