Activity, over time
Daily GitHub activity and social mentions.
Mentions
Daily posts linking to this repository
Attention
Daily stars & forks
Star accumulation
Cumulative stars
Pull requests
Daily opens & merges
Conversations
Daily issues & comments
Community
Daily accounts & reviews
Trending history
Daily top 10 · last 90 days · UTC
Mentions
Stars
Forks
Contributors
Latest stargazers
The last 10 distinct accounts we observed this year.
Most active participants
PR, issue, comment & review events in the past year.
Packages
Scan pending.
Live mentions badge
Show how many posts mention your project in the last 30 days.
Live mentions · last 30 days
Mentions
OpenCode+Docker+GitHub Actionsで全自動AIペネトレーションテストはじめました はじめに Zennチームのdyoshikawaです。 ZennではAIによるセキュリティチェックの仕組みを急ピッチで構築しました。現在はPull Request(以下PR)ごとのAIレビューに加えて、AIエージェントによる全自動のペネトレーションテストを毎週GitHub Actionsで回しています。 背景は、みなさんもご存知のとおり昨今のサイバー攻撃の激化です。 scan.netsecurity.ne.jp 攻撃する側もAIをフル活用しはじめているといわれており、脆弱性が見つかってから悪用されるまでの時間が非常に短くなっています。防御側としては、人間が定期的に診断する従来のやり方では攻撃に追いつかない時代になってきました。 本記事では、ZennでどのようにAIセキュリティレビューと自動AIペネトレーションテストを回しているかを紹介します。特別な製品は使っておらず、GitHub ActionsとDocker、OSSのツールを組み合わせて作っています。何かお役に立つものがあれば幸いです。 まずはPR単位のセキュアコードレビューを整備 最初に取り組んだのは、PRごとのAIレビューです。 PRがReady for reviewになると、GitHub ActionsがAIエージェントを起動します。エージェントは差分と周辺のコードを読み、コード品質に加えてセキュリティとコンプライアンスの観点でレビューします。結果はPRのコメントとして投稿されます。 AIによるPRレビューのコメントの例 モデルが非常に低コストなので、1回のレビューにつき$0.01〜$0.1程度で済みます。 エージェントにはOpenCodeを使い、セキュリティレビューの観点はSkillとしてリポジトリに置いています。手元のコーディングエージェントからも同じSkillを呼び出せるので、PRを出す前にClaude Codeなど手元のツールでローカルからレビューすることもできます。 なお、PRレビューのエージェントも後述するペネトレーションテストと同じDockerのサンドボックスとネットワーク隔離の中で動かしています。 また、レビュー中にWeb検索が必要な場面では、OpenAIのWeb検索ツールをAPI経由で使っています。OpenCodeにも組み込みのWeb検索ツールがあり、ExaまたはParallelを使ってAPIキーなしで検索できます。 opencode.ai ただ、Exaのプライバシーポリシーには、送信された検索クエリをモデルの学習やファインチューニングを含む製品の改善に使うと書かれています。検索語にはレビュー中の文脈が入りうるので、学習に使われることは避けたいところです。そこで、組み込みのWeb検索は使わず、OpenAIのAPIを使うようにしています。 exa.ai こうしたWeb検索のAPIを呼び出すCLIを、webseekとしてOSSにもしています。OpenAIのResponses APIの web_search などを、複数のプロバイダーで共通のインターフェースから呼び出せるツールです。検索ごとに使ったトークン数と推定コストも出力されます。 dyoshikawa/webseek サンドボックスとWeb検索の制限は、どちらも詳しくは後述します。 レビューの過程と結果をGitHub上に残したかった 外部のAIレビューサービスを使う選択肢もありました。それでも自前で作ったのは、レビューの過程と結果をGitHub上に残したかったためです。 結果はPRのコメントとして残るので、後から「このPRで何が指摘されたか」を追えます。実行のログはGitHub Actionsに残るので、どのような観点でどのファイルを読んだのかも確認できます。レビューの観点もSkillとしてリポジトリで管理しているので、観点の変更そのものをPRでレビューできます。 AIはレビュー結果をファイルに書くだけで、コメントの投稿はGitHub Actionsのstepが行います。AIのコンテナにはGitHubのトークンを渡していません。この「AIには書かせるだけ、外への操作は決まった処理で行う」という形は、後述のペネトレーションテストでもそのまま使っています。 Fireworksのオープンウェイトモデルを活用 モデルはFireworks AI上のオープンウェイトモデルを使っています。PRのたびにレビューを回すので、1回あたりのコストはできるだけ下げたいところです。 モデルの選び方やコストについては、以前の記事で詳しく紹介しています。 zenn.dev 実際のコストは、PRコメントや週次検査のIssueの末尾に毎回載せています。たとえば、ある週の全体を対象にしたペネトレーションテストでは、1億以上のトークンを使って、OpenCodeのコストは$3.02でした。コストだけの話でいうと、その気になれば毎日でも回せそうです(人間の対応が追いつくかは別として……)。 OpenCodeのトークン使用量とコスト Fireworks AIは明示的なオプトインなしに入力データをモデルの学習に使わないとしています。レビューではソースコードを送るので、ここは導入前に確認しておきたいポイントです。 さらにAIによる全自動ペネトレーションテストの構築へ PRレビューが回るようになったので、次は実際にアプリを動かして攻撃を試すペネトレーションテストに取り組みました。 毎週、GitHub Actionsの1つのジョブの中で上の流れを実行しています。AIが動くのは試験の部分だけで、依存関係の監査、アプリの起動、テストデータの準備、Issueへの集約は決まった処理で行います。 試験の範囲として、直近の変更を中心に見る「差分」と全体を見る「全体」の2種類を用意しています。 なぜAIによるペネトレーションテストが必要か PRレビューは差分を読むので、変更の中に入った問題を見つけるのは得意です。一方で、実際に動かさないとわからない問題もあります。 代表的なのは認可の不備です。たとえば「有料のChapterを買っていない人が本文を取得できないか」「Publicationを抜けた人が非公開の内容を見られないか」といった問題は、実際にユーザーを用意してリクエストを送らないと確かめにくいです。複数の変更が組み合わさって初めて起きる問題や、レスポンスヘッダーやリダイレクトのようにコードを読むだけでは気づきにくい問題もあります。 こうした問題は、外部の診断を定期的に受けるのが一般的だと思います。ただ、毎週のように受けるのは現実的ではありません。そこで、AIエージェントに実際にHTTPリクエストを送らせ、毎週自動で試験することにしました。 AIエージェントには試験用のユーザーとデータの組み合わせを順に試させています。OpenCodeの上で動くAIエージェントは、次のツールを使えます。 curlでは、ユーザーごとの認可を1件ずつ確認します。 nucleiでは、Zenn向けに自作したテンプレートで、開発用APIの露出やセキュリティヘッダーの不足などを確認します。 projectdiscovery/nuclei OWASP ZAPでは、spiderとactive scanで広く確認します。 zaproxy.org Playwrightでは、ヘッドレスブラウザでXSSやopen redirectを確認します。 playwright.dev ペネトレーションテストでもオープンウェイトモデルを使う ペネトレーションテストのAIエージェントにも、Fireworks AI上のオープンウェイトモデルを使っています。コストのほかにもう1つ理由があります。米国のクローズドモデルと比べて、セーフガードがゆるいことです。 ペネトレーションテストでは、AIエージェントに攻撃者と同じ操作をさせます。米国のクローズドモデルでは、こうした操作を断られることがあります。実際、このペネトレーションテストの仕組みを作る作業そのものを、手元のClaude Codeに断られたことがありました。 だからこそ攻撃する側に使われているのだと思いますが、防御する側も使わない手はありません。 Dockerでサンドボックスとネットワーク隔離を実現 AIエージェントに攻撃者役をやらせるとなると、エージェント自身が暴走したときのことを考える必要があります。認証情報を持ち出す、テスト対象以外を攻撃する、といったケースです。 プロンプトで「テスト対象以外にリクエストしないでください」と指示することはできます。ですが、プロンプトの指示はあくまでお願いです。そこで、DockerでAIが到達できる宛先そのものを絞ることにしました。 コンテナごとにネットワークだけを持つコンテナを用意してnetwork namespaceを共有させ、そのコンテナのiptablesでデフォルト拒否にしたうえで必要な宛先とポートだけを許可することで、ネットワーク境界を作っています。AIエージェントから外への通信はAIゲートウェイのコンテナを経由する経路だけで、その先も許可したAIプロバイダーのAPIに限っています。以下はイメージを伝えるために簡略化した例です。 services: ai-agent-network: image: firewall cap_add: [NET_ADMIN] # firewallを設定するコンテナだけが持つ healthcheck: test: ["CMD", "test", "-f", "/run/network-ready"] ai-agent: image: ai-agent network_mode: service:ai-agent-network cap_drop: [ALL] security_opt: ["no-new-privileges:true"] depends_on: ai-agent-network: condition: service_healthy 実際に処理を行うコンテナにはcapabilityを渡さないので、AIエージェントが自分でfirewallを書き換えることはできません。 AIエージェントが通信できる先は、試験対象のローカル起動のAppサーバーとAIゲートウェイだけです。AIゲートウェイは、LLMのAPIへのリクエストを中継する、Node.jsで書いた小さなプロキシです。LLMのAPIキーはゲートウェイだけが持ち、AIエージェントにはダミーの値を渡しています。DBに直接つないだり、外部にデータを送ったりはできません。 Web検索もこのゲートウェイを経由します。OpenAI APIのWeb検索ツールだけを許可し、検索語にURLや長いトークンのような文字列が含まれる場合は拒否しています。コードや内部の情報を検索語に含めないよう、プロンプトでも指示しています。 このサンドボックスとネットワーク隔離は、前述のPRレビューでも共通です。PRレビューではアプリを起動しないので、AIエージェントが通信できる先はAIゲートウェイだけになります。 ほかにも、エージェントが実行しようとするコマンドをAIの分類器で審査しています。こちらは後ほど詳しく紹介します。 GCSやBigQueryはOSSエミュレータを活用 試験対象は本番環境ではなく、GitHub Actionsのrunner上に立ち上げたZennです。 ZennのbackendはGoogle Cloud StorageやBigQuery、Stripeなどの外部サービスとつながっています。これらはOSSのエミュレーターに置き換えています。 fsouza/fake-gcs-server goccy/bigquery-emulator stripe/stripe-mock こうすることで本物の外部サービスに一切つながない状態で試験できます。AIが有料のAPIを叩いてしまう心配もありません。 もともとローカルの開発環境で使っていたものなので、そのほかにペネトレーションテスト用に改修したものはほとんどありません。開発環境をDockerで整えているプロダクトなら同じように流用できると思います。 サンドボックスとAIの分類器で多重にガード ここまで、Dockerのサンドボックスとネットワーク隔離の話をしてきました。ただ、(以前の自分であればそういう考え方自体しなかったのですが)サンドボックスが絶対に安全とも言い切れなくなってきました。 2026年7月、OpenAIのモデルが評価用のサンドボックスを脱出したという事例が公表されました。 openai.com 今回の構成でも設定の漏れやミドルウェアの未知の脆弱性が絶対にないとは言い切れません。 そこで、サンドボックス環境の整備だけでなく、AIの分類器によるチェックも用いた多重ガードの仕組みにしています。Claude CodeのAuto modeやCodex CLIのAuto-reviewのようなものであるとお考えください。 claude.com alignment.openai.com ZennのAIレビューとペネトレーションテストではOpenCodeを使っています。OpenCodeはベンダーフリーなAIコーディングエージェントであるため、オープンウェイトモデルの使用と相性が良いと感じています。 OpenCodeには --auto という起動オプションがありますが、自分が調べた範囲ではこれはClaude Codeの --dangerously-skip-permissions 相当の設定でした。現状、分類器を使った自動承認の仕組みはOpenCode本体にないと思われます。幸い、OpenCodeにはプラグイン機構があるため、自動承認プラグインを自作することで対応できました。これは別途OSSとしても公開しています。 dyoshikawa/opencode-auto-approval-plugin 分類器にJev/Clef/OpenAI Decisions APIといったDecisionモデルを使うこともできます。興味があれば見てみてください。 もちろんオープンウェイトモデルも(no censorを謳っているモデルでもない限り)明らかにペネトレーションテストの範囲を超えたクラッキングのような指示は拒否するはずですが、ペネトレーションテストの文脈を与えたAIは通常の開発作業時のコンテキストと比べると暴走しやすいのではないかという懸念を持っています。どこかの層が破られる前提で考え、慎重を期するに越したことはないと思います。 ペネトレーションテストの実行環境もGitHub Actionsに ペネトレーションテストの実行環境にもGitHub Actionsを選びました。すでにCIで使っていてチームが使い慣れている点、また、簡単な設定変更だけでログを最大400日まで残せるのも嬉しい点です。 docs.github.com 一応ペネトレーションテストをするにあたりGitHubの規約も確認したのですが、試験の対象はrunnerの中で立ち上げた自分たちのアプリだけで、外部のサービスには一切リクエストを送らない構成にしているため、規約上も問題ないと判断しています。 docs.github.com 検出結果への対応と次回ペネトレーションテストの改善のサイクルを回す ペネトレーションテストは回して終わりではなく、結果への対応こそが重要です。 診断結果はPrivateリポジトリのIssueとして投稿されるようになっています。 週次検査の診断結果Issue このIssueは、手元のコーディングエージェントのSkillで片付けています。Skillは週次検査のIssueをすべて読み、同じ指摘をまとめたうえで、指摘ごとに今のコードでも成立するかを確かめます。偽陽性や修正済みのものはここで落とします。対応が必要なものはAI Coding Agentに指示を出して修正していきます。 そして、未検証で終わった試験や脆弱性診断ツール実行の失敗がないかを見て、次回の検査の改善点を洗い、それもコーディングエージェントで修正をします。 おわりに 以上、ZennでのAIによるセキュリティチェックの取り組みを紹介しました。 まずはPRごとのAIレビューで、変更に入った問題を見つけます。さらにAIエージェントに毎週ペネトレーションテストをさせ、動かして初めてわかる問題を見つけます。AIの暴走はDockerのネットワーク隔離とAIの分類器で多重に抑え、外部サービスはOSSのエミュレーターに置き換えています。実行はGitHub Actionsで行い、結果への対応と検査の改善のサイクルを回しています。 どれもGitHub ActionsとDocker、OSSのツールの組み合わせなので、ローカルの開発環境をDockerで動かしているプロダクトなら、比較的手軽に始められると思います。いきなりペネトレーションテストまでやらなくても、まずはPRごとのAIレビューから始めるのもおすすめです。 今後は試験する範囲を広げつつ、継続して検出精度を上げていきたいと考えています。何かひとつでも参考になることがあれば幸いです。