仕事でたくさんのバイブコーダーと一緒に働いてきて、保守について考えるようになりました。ソフトウェアはリリースした後も手入れが必要です。パッケージは古くなり、脆弱性は見つかります。AIで作り始めたばかりのときは、こうしたことをつい考えずに済ませてしまいがちです。
そこで、同じ考え方を自分のプロジェクトにも当てはめるべきだと気づき、それぞれのgit履歴を確認しました。いろいろ見つかりましたが、予想していたものとは違いました。そしてそのうちの1つは、私が確認した当日もまだ解決していませんでした。
ですから、これはもう他の人が忘れていることについての記事ではありません。私自身の話です。
1件のセキュリティ情報、3つのアプリ
10月14日、Next.jsは2件のcriticalな脆弱性と、1件のhighな脆弱性の修正を公開しました。ここでの「critical」はリモートコード実行を意味します。つまり、特定の条件下では、インターネット上の見知らぬ人がログインせずにあなたのサーバーで自分のコードを実行できるということです。修正はバージョン16.4.1に入りました。
私はNext.jsで3つのアプリを動かしています。どれもAIコーディングツールで作ったもので、責任者も同じ一人、つまり私です。何が起きたかはこうです。
| アプリ | コードがパッチ済みバージョンに移った日 | セキュリティ情報からの日数 | 本番反映 |
|---|---|---|---|
| chandlernguyen.com | 10月18日 | 4日 | 10月18日 |
| DIALØGUE(ダイアログ) | 10月29日 | 15日 | 10月29日 |
| Prova | 11月3日 | 20日 | 11月3日 |
11月3日に確認したとき、Provaはまだ5月から使っているNext.js 16.2.6に固定されたままで、本番でもそれが動いていました。pnpm audit --prodを実行すると、criticalなセキュリティ情報が5件表示され、すべてNext.jsのものでした。
この数字は実際に見つかった状況より悪く聞こえるので、公平に説明しておきます。AIツールと一緒に1件ずつ確認すると、criticalなものの大半はProvaには当てはまりませんでした。use cache関数はなく、Windowsでも動いておらず、攻撃者が制御できる入力がnext/ogのコードに届く経路も見つかりませんでした。画像最適化に関するものもVercel上ではおそらく当てはまりませんが、これはプラットフォームの挙動からの推測で、Vercelがそう明言している記述は見つけられませんでした。明らかに当てはまったのは、Server Actionsのhighなサービス拒否の問題です。誰かがアプリを妨害できたということで、これはパッチを当てるまで残っていました。
私は同じ日に本番をNext.js 16.4.5へ移しました。監査結果はcriticalが5件から0件になり、highの大半も消えました。残っているものはNext.jsの外にあり、まだ対処が必要です。つまり良くはなりましたが、きれいな状態ではありません。
これを良い保守とは呼べません。最悪のセキュリティ情報がProvaをおおむね外れたのは、たまたまそういう作りだったからで、誰かが確認したからではありません。それは運であり、私がそれを知っているのも、ようやく確認したからにすぎません。
表の1行目についても、あまり自分の手柄にはできません。私のサイトの10月18日のコミットはNext.js 16.1.6からの一般的なツールチェーン更新で、そのメッセージはセキュリティ情報に触れていません。単にタイミングが良かっただけかもしれません。
忙しくしていても役に立たなかった理由
簡単な説明は、Provaが放置されていたというものでしょう。それにも一理あります。8月から9月にかけて、コミットの間に41日の空白がありました。
しかし、その説明は10月で崩れます。その月、Provaには175件のコミットがありました。iOS、課金、メール、翻訳の修正を出しましたが、Next.jsのバージョンは動きませんでした。10月30日には依存関係の保守計画まで書きました。そのステータス行にはこうあります。「計画済み、実装は未着手。」
つまり、活動は保守ではありません。AIで作るすべての人にとって、ここが一番大事な点だと思います。機能を作れば、すぐにフィードバックが返ってきます。ボタンが見え、クリックして、動くのを確かめられます。5か月前にインストールしたパッケージの脆弱性が発表されても、何も返ってきません。アプリはページを返し続け、テストは緑のままで、ビルドも通ります。何も壊れていない、まさにそのせいで誰も見ないのです。
こちらから聞きに行かないと鳴らない火災報知器のようなものです。AIツールは家を建てるコストを大きく下げてくれます。でも報知器は取り付けてくれません。
これは以前にも起きました。DIALØGUEには2025年10月6日から2026年1月11日までコミットがありませんでした。Next.jsとReactのcriticalな脆弱性であるReact2Shellは、12月初めに公開されました。私のパッチのコミットは1月11日付で、5週間以上後です。今でも見られる最も古い本番デプロイの記録は1月18日のものなので、12月に何が動いていたかを正確には言えません。おそらく一番助けになったのは、私とは何の関係もないことでした。Vercelは全顧客向けにファイアウォールルールを展開したと述べつつ、パッチの代わりにそれに頼らないよう明言しています。
1つはっきりさせておきたいことがあります。「バイブコーダー」という言葉は悪口のように聞こえることがあるからです。私は体系的なコースでコーディングを学び、その中にはサイバーセキュリティの修了証もあります。これらのリポジトリにはテストとレビューのゲートもあります。それでも起きました。ですから、これは初心者の問題だとは思いません。ソフトウェアを作るのが安くなり、それを見張る担当が誰もいないときに、放っておけば起きることだと思います。
報知器は、こんなものです
良い知らせは、ごく小さなチェックでこの状況が大きく変わることです。
10月27日、私はサイトのCIにpnpm audit --prodを追加しました。CIとは、コードをプッシュしたときに自動で走る一連のチェックのことです。10月28日、それが失敗しました。サイトが取り込んでいるだけで一度も使っていないメール描画ライブラリのセキュリティ情報が原因で、10月29日にそのライブラリを削除しました。かかったのは2日で、私は何も覚えておく必要がありませんでした。
賢いことは何もしていません。誰か、あるいは何かに、見るように指示しておく必要があるだけです。
パッケージを更新しても救われなかった
この記事がここで終わるなら、誤った安心感を与えてしまいます。10月に行った修正のうち、より居心地の悪いものの一部は、古いパッケージとは何の関係もなかったからです。自分のプロダクトのミスでした。
たとえばDIALØGUEでは、次のようなものがありました。
- 特権を持つデータベース関数が公開キーだけで呼び出せました。また、ログイン済みのユーザーが、私のバックエンド専用の関数に届いてしまう状態でした。
- 返金済みのポッドキャストが再び動き出し、課金されないまま完成してしまうことがありました。実際に、少数のポッドキャストでそれが起きました。
Provaでは、公開データベースロールが、誰も選んでいないTRUNCATEなどの権限を継承していました。プラットフォームのデフォルトから来たものです。アプリの経路でそれを使うものはありませんでしたが、権限は存在していました。
バージョンアップではどれも直りません。これらのアプリはこうした問題を抱えたまま動いていて、ここでも何も壊れませんでした。問題が表に出たのは、セキュリティレビューが探しに行ったからにすぎません。しかも、それ以前にこれらのプロダクトで行ったセキュリティレビューでは、すべては見つかっていませんでした。
このうちどれかが悪用されたのでしょうか。わかりませんし、完全に突き止めることもできません。データベースのログは約90日分しかさかのぼれず、問題の中にはそれより古いものもあります。確認できる期間では、それらの関数への外部からの呼び出しは見つかりませんでした。ただ、これは限られた期間の話であり、何も起きなかったことを示すものではありません。返金済みのポッドキャストを誰が、あるいは何が再開させたのかもわかりません。自分のコードのリトライが、一番ありそうだとは考えています。
これも完全なリストではありません。他の問題にもまだ取り組んでいて、解決していない間は説明しません。
モデルの問題は思っていたものと違いました
古いAIモデルはお金が余計にかかる、と書くつもりでした。でも、それを裏付けられません。調べましたが、どのリポジトリにも変更前後のコストの記録がないので、ただの推測になってしまいます。
見つかったのは、もっと奇妙なことでした。DIALØGUEの台本ドラフトとレビューのモデルは、10月12日までgemini-3.5-flashに設定されていました。Googleのドキュメントには現在、そのエンドポイントは停止せず、リクエストを自動的にgemini-3.6-flashへ振り向けると書かれています。この振り替えがいつ始まったのかわからないので、どのモデルがどれだけの期間応答していたのかは言えません。設定ファイルにモデル名を書いても、モデルは固定されないのです。
価格も動きます。私が10月にProvaとDIALØGUEを移したGemini 3.8 Flashは、12月31日まで入力100万トークンあたり$0.75、出力100万トークンあたり$3.75と記載されています。2027年1月1日からは2倍の$1.50と$7.50になります。移行したのは先月のことで、これがコストにどう響くかはまだ計算できていません。
アップグレードにもリスクがないわけではありません。ないふりをするつもりもありません。
- Ask Sydney(シドニー)をDeepSeek Flashに切り替えた直後、モデルが私のコードの許可より多くの検索ラウンドを求めたために、本番で少なくとも1件の質問が失敗しました。
- Provaのエンベディングを移行したとき、ナレッジベースのテーブルを空にした後で最初の何回かの実行が失敗し、復旧するまで検索の品質が落ちました。
- 同じ移行で、Claudeのモデル名をGoogleのAPIに送ってしまうはずだった本番設定も見つかりました。
Provaの移行を信頼できるものにしたのは、地味なことでした。変更前に記録した評価があり、スプリントレビューの判断24件中22件が正解で、変更後も同じスコアでした。それがなければ、ただ祈っていただけでしょう。
そもそも保守すべきなのか
一番難しい問いは、もう少しで書かずに済ませるところだったものです。リリースしたアプリはどれも、自分の注意力で払い続ける小さなサブスクリプションです。そしてAIのおかげで、それを増やすのはとても簡単になりました。
STRAŦUM(ストラタム)では、私は別の選択をしました。サービスを終了し、v1をリファレンス実装として公開したのです。リポジトリには本番サービスとしては保守していないと書いてあり、セキュリティのファイルには私が把握しているセキュリティ情報を載せています。その中には修正がまだないビルド時の依存関係の問題もあり、実質的なリスクは低いと評価しています。これは十分に誠実な結末だと思います。不誠実なのは、誰も見ていないのに登録を受け付け続けるアプリです。
月曜日に私がすること
AIで何かをリリースして、使っている人がいるなら、私はこのリストから始めます。どれも完璧である必要はありません。
- 今日、一度だけ監査を実行する。 本番で動いているすべてのリポジトリで
npm audit --omit=devかpnpm audit --prodを実行します。そのうえで、結果のどれがアプリの作りに当てはまるかをAIツールに聞きます。すべてが当てはまるわけではありません。 - そのチェックを自動にする。 CIに追加して、覚えていなくても失敗するようにします。あまり触らないアプリには定期実行も加えます。プッシュのときだけ走るチェックは、静かな月には役に立たないからです。
- パッケージだけでなく権限も読む。 公開キーだけで、アカウントなしで、誰かに何ができるかを問います。そして思い込まず、ローカルのコピーで試します。
- 履歴からシークレットを探す。 本物のキーをコミットしたことがあるなら、失効させます。その行を消すだけでは足りません。
- 必要になる前に、ログがどこまでさかのぼれるかを調べる。 悪用されたかどうかという問いにいつか答えられるかどうかは、それで決まります。
- AI機能ごとに小さな評価を持っておく。 正解がわかっている実例が20件あるだけでも、モデルの変更で悪くなったかどうかがわかります。
- AIプロバイダーの廃止予定と価格のページを定期的に確認する。 カレンダーに入れましょう。Gemini 3.8 Flashを使っているなら、1月1日がまず確認しておくのにちょうどいい日付です。
- アプリごとに、残すか終わらせるかを決める。 見張るつもりがないなら、取り下げるか、そう明言します。
これのどこまでをエージェントに定期実行で任せられるかは、まだ模索しています。ただ、10月30日に計画を書いても何もパッチされなかったことはわかっています。
AIで何かを作っているなら、すでにリリースしたものを最後に確認したのはいつで、何が見つかりましたか?ぜひ知りたいです。私のものより良い報知器を持っているなら、なおさらです。