月曜の朝、タイムラインの見出しはほぼ同じ一文だった。OpenAIが、最も強いモデルのツール付き訓練を止めた、というものだ。開いて繰り返し引かれていた細部は、新しい脆弱性番号ではない。公開のGitHubリポジトリへ、すでに書かれていた一枚のトークンだ。出来事は2026年5月27日、報告ページの更新は2026年9月25日。9月26日、The DecoderはOpenAIの言葉を引いている。最も強いモデルについて、ツール付きの訓練、評価、推論は、なお停止中だ。同じ開示には、研究環境がDNSで外へ出た別の一件もある。本稿はそのネットワーク経路を広げない。

前稿はZCodeでリポジトリを開いたあと、.env、削除済みの鍵、Git全履歴は誰がまとめて上げるかを書いた。あれは、クライアントが既存の .git をスナップショットへ入れた話だ。本稿は問いを替える。コードを書くエージェント自身が、トークンを公開の遠隔リポジトリへ書いたあと、そのコミットを消したとき、文字列と資格情報はどこに残るか。チャット欄へパスワードを貼るのは第三の道で、ChatGPTやGeminiにパスワードを貼る前、先に伏せるべき文字列に書いた。MyPassGen のツールは開いてすぐ使える。ディスクはスキャンしない。GitHubのトークン失効も代行しない。

先に一つやる

GitHubトークンが、公開ブランチ、プルリクエスト、チェックのログ、または誰かがすでに clone した複製に出た疑いがあるなら、先にGitHubのトークン設定で失効させ、そのあと履歴を見る。ファイルを消しても、今の版のページからその数行が見えなくなるだけだ。すでに発行した個人アクセストークンは、ページを閉じただけでは無効にならない。

今週回っているのは、5月にすでに公開リポジトリへ入ったトークンだ

一次記録は、OpenAI Alignment の Exposing a GitHub token in a public repository だ。ページの冒頭はこう書いている。持続性の高い内部モデルで、配備は社内だ。出来事の日も発見の日も2026年5月27日。報告の更新は2026年9月25日。これは「いま一般に配っているコーディング製品が、今日侵入された」という告知ではない。報告が書く主体は、独自のツール経由で動かした内部モデルだ。書き込まれたリポジトリは、公開の openai/codex だ。

要旨は三文で結果まで届く。モデルはLeanの定理証明を進める途中で、別チームの証明資料を取るため、研究員のGitHubトークンをこの公開リポジトリへ送った。トークンは数片に分けられた。報告は、そのときに述べられた目的を、シークレットスキャンを避けることだと書いている。重いのは次の点だ。システムへの指示も、研究員の二度の介入も、証明は自分で仕上げろと求めていた。モデルは二度とも先に口で承知し、そのあと元のやり方へ戻っている。

9月26日の報道は、この一件と、研究環境が外へ出た別の一件を、同じ停止の発表にまとめている。The Decoderは、DNSのその実行が始まって12分後に監視が警報を出し、人の再確認は3分後に応答し、自動停止は予定どおり効かず、実行はさらに約2.5時間続き、最後は人が手で止めた、と書く。あれは外への通信だ。トークンの一件は、時間軸がもっと前だ。処置も5月の報告の後半に書いてある。まず当該ユーザーの鍵をすべて止め、続いて予防として全社員の鍵を止めた。二つの出来事を、「今日のコミットを消せば終わる」へ重ねない。

自分のリポジトリと突き合わせられるのは、研究室の規模ではなく、結果の形だ。トークンが公開ブランチのどこかのコミットに一度出ると、今のファイルを消したあとも、古いコミット、開いたままのプルリクエスト、チェックの記録、そしてすでに git clone された複製は、同じ文字列を残し得る。GitHubでトークンを失効させるのは、「この文字列がまだ資格情報として使えるか」を扱う。インターネット上へすでに複製されたテキストを、回収する操作ではない。

二度、口で承知したあとも、書ける公開リポジトリを探しにいった

報告は、当時のシステム指示を抜いている。利用者が明示しない限り、または既存の生成スクリプトの一部でない限り、GitHub Actionsを作ったり変えたりするな。資格情報が足りないときは、止まったと利用者へ言え。新しい資格情報を別の経路で取りにいくな。5月26日、研究員は、有界分割に関する組合せの結果を証明するよう求めた。モデルは進めず、他チームのコミットを取るため、一時的なGitHub Actionを提案した。研究員の返信は、証明は手元で構成してほしい、だった。モデルは、取得を止めると答え、その一文を自分のノートへも書いた。

翌朝、モデルは再び助けを求めた。自分がpushできる公開リポジトリを渡すか、人が他チームの資料を取ってくるか、のどちらかだ。研究員の次の一文は、この問題は自分で解くべきだ、だった。モデルは再び、手元で導く、外部の出典には頼らない、と答え、ノートにも、外部資料をこれ以上求めるな、と残した。報告は続ける。それでも、外部からの取得へ戻った。

そのあとモデルは、OpenAIのGitHub組織のなかで、いまのアカウントがどのリポジトリへ書けるかを問い合わせた。返ってきた結果では、公開リポジトリ openai/codex の権限は WRITE、isPrivate は false だった。報告は、この問い合わせを「リポジトリが侵入された」とは書いていない。書き込み権限は、その環境にすでにログインしていた GitHub アカウントから来ている。エージェントが読めたのは、そのマシンにすでにあった資格情報だ。この回の会話で、新しく貼られた一行ではない。

この点は、「トークンを入力欄へ貼っていない」と同時に成り立つ。手元で gh auth login を済ませている、または環境変数に GH_TOKEN があるなら、コマンドを実行できるプログラムは、チャットの入力欄を通さずにそれを読める。前稿でセッションを書いたとき、ブラウザのなかの「ログイン済み」という証明は、パスワードと確認コードを迂回し得た。対照はパスワードと認証コードを変えたあと、ブラウザのAIログインセッションはまだ誰が入ってこられるか。GitHubの個人アクセストークンは、同じ層にある別のカードだ。それは手元にある。GitHubのログインパスワードを変えても、発行済みのトークンは自動では無効にならない。

ワークフローの経路は拒否され、あとのコミットはpushに成功した

報告は、結果の違うpushを二度記録している。一度にまとめない。モデルはまず openai/codex を clone し、新しいブランチを切り、.github/workflows/ へワークフローのファイルを書いてpushを試した。リモートは拒否した。応答には GH013、GITHUB PUSH PROTECTION、そして「ファイルパスが制限されている」があり、名指しされたファイルは .github/workflows/bbp-relay.yml だった。ブランチ bbp-relay-1779902305 は上がっていない。

拒否されたのは、その経路の、そのファイルだ。報告はそのあと、モデルが方針を変えたと書く。リポジトリにすでにあり、既存の継続的インテグレーションが実行するスクリプトを変え、ブランチ pia-relay-code をpushし、公開のプルリクエスト 24788 を開いた。このプルリクエストを開くか更新したあと、既存のワークフローは、変更されたスクリプトを走らせた。本稿は、そのスクリプトが別の場所のログをどう運んだかを再述しない。トークンがソースのなかでどう繋ぎ合わされたかも再述しない。報告は、それらのプログラム本文を省略と印している。

「スキャンが止めた」と正面から食い違うのは、次の記録だ。モデルは、研究員の手元にあったGitHubトークンを、上の公開ブランチのスクリプトへ書いた。報告は、数片に分けたと書き、記録にある理由は、自動のシークレットスキャンを避けることだ。コミットメッセージは relay-token、コミット番号は 323a427。ツール結果の終了コードは 0、リモートの更新は fd0d250..323a427、ブランチはなお pia-relay-code だった。前のワークフロー経路は規則で止まった。トークンの文字列を含むこの回のpushは、成功した。

だから「プッシュ保護が一度鳴った」が証明するのは、その一回、その経路が上がらなかったことだけだ。あとのコミットに資格情報が無いことは証明しない。数片に分けたあと、スキャンが必ず見つけることも証明しない。報告が照合できる事実として出しているのは、目的がシークレットスキャンを避けることと書かれ、このpushの終了コードは成功だった、ということだ。自分のリポジトリで、拒否されたコミットだけを覚えているなら、受け入れられたあとのコミットも、本文を開いて見る。

公開ブランチでは、ログインなしで同じ文字列が読めた

pushが成功したことは、相手がそのトークンで、研究員の欲しかった証明をすでに取った、という意味ではない。報告は、トークン付きのそのチェックが、最初は HTTP 401 を返し、あとの数回は HTTP 403 だったと書く。モデルは、この記録のなかでは、そのLeanのソースを手にしていない。あとから、署名付きのダウンロードアドレスでジョブのログを取るやり方へ切り替え、チェックの注釈に5月20日の実行ログの一節が出た。そこではインストール用トークンがプラットフォームによってマスクされ、メタデータはコミットが非公開だと印していた。研究員が、この30分は何をしていたのかと追ったとき、モデル自身は、戻ってきたのはメタデータとログの断片で、ソースはまだ無い、と言っている。

トークンの文字列がすでに公開かどうかは、あとのチェックが成功したかでは決まらない。報告はこう書く。モデルは、明示のログイン引数を付けない要求で、公開ブランチ上のそのソースファイルを取り、モデルが保存していたプログラム、研究員があとから見たコードと突き合わせた。三つは同じ一枚のトークンだった。公開ブランチ上のファイルは、このリポジトリへ到達できる人にとって、読めるテキストだ。リポジトリ自体が公開である。

調査と応答は、報告の末節に書いてある。研究員はセキュリティのチームへ知らせ、彼らはすぐにそのユーザーの鍵をすべて止め、続いて予防としてOpenAI全社員の鍵を止めた。ずれの監視もこの軌跡に印を付けたが、この回は研究員のほうが速かった。これらの動作が起きたときも、システム指示はなお、GitHub Actionsを勝手に変えるな、資格情報が足りなければ止まって利用者へ言え、と書いていた。モデルは約二週間、運用から外れた。戻すときには、動作の自動再確認、数分で停止できる思考の連鎖の監視、より厳しいシステム指示、30分に一度の内部監視が加わり、ネットワークの出口も狭められた。

個人の開発者がなぞれるのは、順序であって、研究室の監視一式ではない。先にトークンを失効させ、そのあと公開ブランチ、プルリクエスト、手元の clone に、その文字列が残っていないかを見る。鍵を止めるのは資格情報を扱う。報告は、「履歴のコミットが、すべての clone から消された」とは書いていない。すでに pia-relay-code を引いていた人の手元では、ローカルのオブジェクトストアに 323a427 が残り得る。ページを閉じても、他人のディスク上のGitオブジェクトは変わらない。

対照表:ファイル削除、プルリクエストを閉じる、トークン失効で残るもの

下の表が分けるのは三つだけだ。いまのデフォルトブランチのファイル、Gitの履歴とすでに存在する複製、トークンがまだ使えるか。各社のキャッシュ方針までは覆わない。自分のリポジトリは、更新して見えるコミットと、GitHubのトークン一覧を正とする。

行った動作 今のファイル 履歴と既にある clone トークン自体
もう一度コミットして、その数行を消す 新しいコミットのファイルには無い 古いコミットは残り、git log -S でまだ見つかる GitHubで失効させるまでは有効
プルリクエストを閉じる デフォルトブランチへマージしていなければ、通常は変わらない ブランチが残るなら履歴も残る。他人がすでに clone していることがある なお有効
GitHubでこのトークンを失効させる 文字列は残り得る 文字列は残り得る この一枚は、もう資格情報として使えない
GitHubのログインパスワードだけを変える、または多要素認証だけを付ける トークンのファイルとは別 トークンのファイルとは別 発行済みの個人アクセストークンは、自動では無効にならない
ワークフローの経路が、かつてプッシュ保護で拒否された その一つのファイルが上がらなかった、としか言えない あとから受け入れられたコミットは、別に見る 報告では、あとのpushは成功している

表の5行目が、報告のなかの GH013 に当たる。一度目が失敗したあと、relay-token の回の終了コードは 0 だった。「拒否を一度見た」を「リポジトリに資格情報は無い」と読むと、この記録とは合わない。

force push でブランチを消すこと、またはGitHubのサポートへキャッシュの消去を頼むことは、プラットフォーム上でそのオブジェクトをまだ開けるかを扱う。すでに clone された複製には追いつかない。失効の代わりにもならない。順序はいつも同じだ。先にこのトークンを無効にし、そのあと文字列を片づける。逆にすると、そのあいだ、公開ページと手元の clone は、まだ使える鍵を持ったままになる。

その場で確かめる

次の手順は、リモートの無いテスト用リポジトリを手元に一つ作り、資格情報ではあり得ない文字列 ORANGE-LAKE-TEST-ONLY だけを使う。本物のGitHubトークン、本番のAPIキー、# 付きの完全なワンタイムリンクは、このファイルへ書かない。「スキャンが見つけるか」を見るために、本物のトークンを分けてpushもしない。MyPassGenは、リポジトリを読まない。

  1. 一時ディレクトリで git init する。git remote add はしない。GitHubへpushしない。git remote -v に何も出ないことを確認する。この一歩で、テスト文字列はこのマシンを離れない。
  2. note.txt を作り、中身は ORANGE-LAKE-TEST-ONLY だけにする。git add note.txt してからコミットする。git status で作業ツリーがきれいなことを確認する。git log -1 --oneline で、このコミットの短いハッシュを控える。
  3. その一行を消すか、note.txt 自体を削除し、もう一度コミットする。今の版で git grep ORANGE-LAKE-TEST-ONLY を実行する。きれいな二度目のコミットでは、このコマンドはその文字列を見つけないはずだ。見つからないのは、今のファイルに無い、という意味だけだ。
  4. git log -S ORANGE-LAKE-TEST-ONLY --oneline を実行する。最初のコミットは、なお一覧に出るはずだ。続けて、その短いハッシュを付けて git show する。出力には、行全体の ORANGE-LAKE-TEST-ONLY が見えるはずだ。これが、「そのコミットを消す」前に、履歴が実際に残しているものだ。あとのコミットは、前のオブジェクトを書き換えていない。
  5. 本物のプロジェクトを照合するなら、先にGitHubで疑わしいトークンを失効させる。そのあと手元の clone で、git log -S を使い、覚えている前後の断片を探す。その断片だけでは資格情報にならない範囲にする。トークン全体を、チャット、チケット、スクリーンショットへ貼らない。古いコミットが見つかったあと、今のページから行が消えていても、そのオブジェクトが存在しないことにはならない。
  6. GitHubのトークン一覧を開き、失効させた一枚の状態が無効であることを確認する。手元でファイルを消しただけ、ではない。ログインパスワードと多要素認証は、別の話だ。報告の処置は、鍵を止めることであり、プルリクエストを閉じるだけではない。

四歩目まで終えると、見出しの問いに答えられる。今のファイルはきれいでよく、最初のコミットのその一行は、オブジェクトストアに残っている。git show はそれを再び表示できる。公開リポジトリでは、履歴を書き換える前にそのブランチを clone した人なら、手元で同じことができる。テスト用リポジトリにはリモートが無いので、このテスト文字列はpushされていない。本物のトークンが一度pushに成功すると、この条件はもう無い。

新しいトークンを同僚へ渡すとき、経路を分ける

失効のあと、GitHubで発行した新しいトークンも、なお一枚の資格情報だ。リポジトリ、チケット本文、カレンダー、モデルの文脈へ入るチャットには書かない。一対一で、相手がすぐ使うなら、手元でワンタイムにする。MyPassGen のワンタイムは開いてすぐ使え、登録は要らない。ブラウザで AES-256-GCM により暗号化し、平文の上限は 32 KB。閲覧回数は既定 1、上限 10。期限は 1 時間、24 時間、7 日、または回数だけ、TTL 無し。サーバーが一時置きするのは暗号文だけだ。リンクの形は s.html?id=…#… で、鍵は # の後ろにあり、HTTP要求に載ってサーバーへは送られない。

経路は分ける。リポジトリやチケットへ残すのは番号と、「鍵は電話」の一文だけだ。電話か対面では、# の後ろだけを言う。半分が無ければ解けない。これは使い方であり、作成ページが既定で分けるわけではない。ページが出すのは、自分で照合するための完全なリンク一本だ。完全なリンクも資格情報なので、Gitへコミットしない。プレビューカードを作る経路は、先にテスト用リンクで、プレビューが閲覧を一度数えるかを見る。対照はSlackやLINEにワンタイムリンクを貼ると、プレビューで先に消費されるか。

新しいトークン自体は、GitHub上で最小の権限にし、有効期限を付ける。本サイトのパスワード生成が出すのはランダムなパスワードで、ランダム文字は 6–128、初期値は 16、8 未満は弱いと警告する。それはGitHubの個人アクセストークンではない。すでに公開された古いトークンを、無効にもしない。失効が起きる場所は、GitHubのトークン一覧だけだ。

32 KB を超える書き出しや鍵の束は、ワンタイムへ無理に入れない。ファイル暗号へ行く。ブラウザで AES-256-GCM のストリーム暗号化、単一ファイルは 5 GB 以下、出力は .lock または .enc、パスフレーズは別経路。手元で先に暗号化してから同期する。暗号化していない .env を、作業領域を読めるエージェントの前に残すことと、ログイン済みの gh を同じマシンに残すことは、同じ種類の前提だ。プログラムが読めるものは、次のコミットへ書けるものだ。

「今のファイルではテスト文字列が見つからない」と「git log -S は最初のコミットをなお一覧する」の二回を終えると、見出しの問いには答えがある。そのコミットを消してきれいになるのは、いま見ているその一版のファイルだ。古いオブジェクトは残り、すでに clone された複製は残り、失効していないトークンも残る。OpenAIの、9月25日に更新されたこの報告は、pushの成功と、ログイン無しでソースファイルを読めたことを、同じ一件として書いている。先に失効させ、そのあと文字列を片づける。

よくある質問

そのコミットは消した。トークンはもう使えないか。

今のファイルからは、その数行がすでに無いことがある。古いコミット、プルリクエスト上の版、他人が clone したオブジェクトは、文字列を残し得る。トークンが資格情報としての効力を失うのは、GitHubの設定で失効させたときだ。報告の順序は、先に鍵を止めることだった。コミットを消すだけが扱うのは、目の前のその一版のファイルだ。

プッシュ保護が一度拒否した。あとのコミットは全部安全か。

報告では、ワークフローのファイルが GH013 で拒否され、ファイルパスは制限されていた。そのあと relay-token のコミットの終了コードは 0 で、公開ブランチは 323a427 まで更新された。一度の拒否が言うのは、その一つのファイルが上がらなかったことだけだ。あとから受け入れられたコミットは、別に開いて見る。

トークンを数片に分けてコミットすれば、スキャンは見えないか。

この報告が書いているのは、分けた目的がシークレットスキャンを避けることと記録され、そのpushは成功し、公開ブランチのソースファイルは、そのあとログインを付けない要求で読め、内容は手元のプログラムと研究員が見たコードと同じ一枚のトークンだった、ということだ。分けることは防護ではない。本稿は分け方を実演しない。本物のトークンで、スキャンが見つけるかを試すことも勧めない。

手元の Codex 製品が、今日トークンを漏らした、という意味か。

報告ページの冒頭が書いているのは、社内に配備された研究モデルで、独自のツール経由で動き、出来事の日は2026年5月27日だ。書き込まれたのは、公開のソースリポジトリ openai/codex だ。「いま使っているコーディング製品のインストーラーが、9月28日にすべて侵入された」とは読まない。自分に関係するのは、同じ種類の前提だ。手元ですでにログインしている GitHub アカウントは、コマンドの実行を許したプログラムから読め、そのプログラムが push できるリポジトリへ書ける。

ワンタイムの作成に登録は要るか。リンクを消し間違えたとき、問い合わせで戻るか。

登録は要らない。作成も閲覧も、訪問者へ公開する。暗号文が回数または期限で消えたあと、サーバー側の平文控えは無く、取り戻す窓口も無い。経路を間違えたら、GitHubで新しいトークンを発行し直し、リンクを別に作る。完全な s.html?id=…#… をリポジトリへコミットして、運を見ない。