AWS Continuum for penetration testing の検出結果から、プライベートリポジトリにコード修正 PR を作ってみた

AWS Continuum for penetration testing の検出結果から、プライベートリポジトリにコード修正 PR を作ってみた

AWS Continuum for penetration testingの、検出結果からコード修正のPRを作る機能を、プライベートリポジトリで試しました。PRの作成、複数の検出結果を指定したときの挙動、マージ後の再検証からPRコードレビューコメントの挙動まで深ぼって確認しています。
2026.10.22

はじめに

クラウド事業統括本部の浅野です。

AWS Continuum for penetration testing(旧AWS Security Agent)には、ペネトレーションテストで見つかった脆弱性(finding)に対して、エージェントが修正のコードを書いてプルリクエストを作る機能があります。

https://docs.aws.amazon.com/securityagent/latest/userguide/remediate-finding.html

連携したリポジトリがパブリックだと、この修正PRは作られません。今回は、リポジトリをプライベートにして、既存のペネトレーションテストの検出結果から修正PRを作り、そのPRをマージして再検証するところまで試しました。あわせて、同じ連携で使えるPRのコードレビューコメントの挙動についても確認しました。

コード修正とは

公式ドキュメントに書かれている内容をまとめると、次のとおりです。

https://docs.aws.amazon.com/securityagent/latest/userguide/enable-remediate-findings.html

  • 接続済みのGitHubリポジトリに、検出結果を直すためのPRを作る機能
  • 対象は、ペネトレーションテストとコードレビューの検出結果(今回試したのは、ペネトレーションテストの検出結果)
  • 有効にする単位は、Agent Spaceに接続したリポジトリごと
  • 検出結果の画面から手動で実行できる。ペネトレーションテストの設定で自動修正を有効にすると、検出結果が確認された時点で自動でも実行される(今回は手動のみ検証)

GitHub連携には、コード修正のほかにもリポジトリに関係する機能がいくつかあり、ややこしいので、一覧で整理しておきます。

機能 内容
ソースコードの参照 ペネトレーションテストが、連携したリポジトリのコードを読んで検証に使う
コード修正 検出結果を直すPRを作る
コードレビューコメント PRにセキュリティ上の指摘をコメントする

なお、2026年10月時点で、コード修正はプレビューとして無料で使えます。

構成

検証対象はOWASP Juice Shop(v20.3.0)です。ペネトレーションテストの実行環境は、公式のコンテナイメージをALB + ECS Fargateにデプロイし、Route 53のサブドメインを割り当てています。構成図です。

2026-10-22-security-agent-pentest-code-remediation-01

連携するリポジトリは、Juice Shopの公式リポジトリをフォークしたものです。このフォークは、最初はパブリックの状態で連携していたので、プライベートに切り替えます。GitHub Appのインストールと、Agent Spaceへの連携は、事前に済んでいる前提です。

ペネトレーションテストで出ていた検出結果のうち、次の3件を、修正PRの対象に選びました。

検出結果 重大度 内容
/rest/memoriesの情報漏洩 High 未認証で他ユーザーのメールアドレス・パスワードハッシュなどが取れる
商品検索のSQLインジェクション High qパラメータにUNION句を足して、Usersテーブルの内容が取れる
ファイルアップロードのXXE High XMLのアップロードで、サーバー上のファイルが読める

やってみた

リポジトリをプライベートにする

フォークしたリポジトリのSettingsを開き、Danger Zoneを見ると、「Change visibility」のボタンが押せません。「For security reasons, you cannot change the visibility of a fork.」と表示されています。

2026-10-22-security-agent-pentest-code-remediation-02

今回のリポジトリは、OWASP Juice Shopの公式リポジトリをフォークして作ったものなので、フォークのままではプライベートに切り替えられません。フォークの関係を外す「Leave fork network」を先に実行すると、プライベートに切り替えられるようになります。数分かかります。フォーク元がない通常のリポジトリでは、この手順は要りません。

2026-10-22-security-agent-pentest-code-remediation-03

コード修正を有効にする

Agent Spaceのペネトレーションテストの設定を編集すると、「接続されたリポジトリ」の欄に「是正」の列があります。この時点では、リポジトリタイプが「公開」、是正が「無効化済み」のままでした。GitHub側をプライベートにしても、この表示は変わっていません。

2026-10-22-security-agent-pentest-code-remediation-04

ここで是正をオンにして保存しましたが、保存した直後の時点では、リポジトリタイプは「公開」のまま、コード修正も有効になっていませんでした。

一方で、Agent SpaceのGitHubの欄にある「編集」→「機能を管理」の画面でも、同じコード修正の設定を操作できます。こちらでは、リポジトリタイプが変更どおり「プライベート」と表示され、コードレビューコメントとコード修正はどちらも無効になっていました。

2026-10-22-security-agent-pentest-code-remediation-05

コードレビューコメントとコード修正を両方オンにして保存しました。コードレビューコメントをオンにすると、どのPRでレビューを走らせるかを決めるトリガーのフィルターを設定できます。トリガーイベントには、次の3つの選択肢があります。

  • プルリクエストのレビューの準備ができました: レビューの準備が整ったPRが開かれたとき、再オープンされたとき、コードが更新されたとき(ドラフトが終了したときも含む)
  • プルリクエストの下書きが作成されました: ドラフトのPRが開かれたとき、再オープンされたとき、コードが更新されたとき
  • プルリクエストのラベルが追加されました: 一致するラベルが追加されたとき(ラベルの条件が必要)

今回は、PRを開いて、レビューを受けられる状態になったときにレビューが走ってほしいので、「プルリクエストのレビューの準備ができました」を選びました。また、条件としてターゲットブランチも設定できます。今回はmasterにしました。この設定はコードレビューコメントを走らせるPRの条件で、コード修正のPRの作成先を決める設定ではありません。

2026-10-22-security-agent-pentest-code-remediation-06

2026-10-22-security-agent-pentest-code-remediation-07

保存すると、Agent Spaceの画面に「統合リソースが追加されました」と表示され、リポジトリのコード修正が「有効化済み」になりました。

2026-10-22-security-agent-pentest-code-remediation-08

AWS CLIのlist-integrated-resourcesで連携の状態を見ると、「機能を管理」を保存する前後で次のように変わっていました。

$ aws securityagent list-integrated-resources \
    --region ap-northeast-1 \
    --agent-space-id <agent-space-id> \
    --query 'integratedResourceSummaries[].{repo:resource.githubRepository.name,accessType:resource.githubRepository.accessType,capabilities:capabilities.github}'
状態 accessType remediateCode
GitHub側をプライベートにした直後 PUBLIC False
ペネトレーションテストの設定で是正をオンにして保存 PUBLIC False
「機能を管理」でコード修正をオンにして保存 PRIVATE True

今回の結果では、「機能を管理」でコード修正をオンにして保存した場合に、accessTypeがPRIVATE、remediateCodeがTrueになりました。この機能を確実に有効にするには、この「機能を管理」の画面から行ったほうがよさそうです。

ペネトレーションテストの設定の「是正」と、「機能を管理」の「コード修正」は、同じ設定でした。片方を変更すると、もう片方にも反映されました。

2026-10-22-security-agent-pentest-code-remediation-09

検出結果から「コードを修正」を実行する

以前実行したペネトレーションテストの結果の中から、まず/rest/Memoriesの検出結果を対象にしました。

この検出結果は、次のような脆弱性です。Juice Shopには、ユーザーが写真を投稿して一覧で見られる「Photo Wall」という機能があり、その一覧を返すAPIがGET /rest/memoriesです。このAPIは、ログインなしで呼べるうえに、写真と一緒に、投稿したユーザーの情報をまるごと返していました。含まれるのは、メールアドレス、ソルトなしのMD5のパスワードハッシュ、ロール、会員トークンなどです。ログインしていない誰でも、他のユーザーのこれらの情報を取得できます。

検出結果には、推奨される対応も書かれています。

  • このAPIで返すユーザー情報を、IDや表示名など、必要なものだけに限定する
  • ユーザーモデルの側で、パスワードなどの機微な属性が、既定ではレスポンスに入らないようにする
  • このAPIにも認証を求める
  • 漏れた会員トークンをローテーションし、パスワードのリセットを求める。MD5のハッシュは、ソルト付きの方式に移行する

ペネトレーションテストの実行を開き、検出結果の詳細に出ている「コードを修正」を押します。エージェントがどういうPRを作るのかは、このあと確認します。

2026-10-22-security-agent-pentest-code-remediation-10

「コード修正が開始されました」というバナーが出ます。

2026-10-22-security-agent-pentest-code-remediation-11

押した直後の画面には、進捗も完了も表示されませんでした。再読み込みしても変わりません。

2026-10-22-security-agent-pentest-code-remediation-12

進捗は、AWS CLIのbatch-get-findingsが返すcodeRemediationTaskで確認できます。

$ aws securityagent batch-get-findings \
    --region ap-northeast-1 \
    --agent-space-id <agent-space-id> \
    --finding-ids <finding-id> \
    --query 'findings[0].codeRemediationTask'

実行中は次のように返ります。

{"status": "IN_PROGRESS", "statusReason": "Code remediation in progress"}

完了すると、PRのリンクが入ります。

{
  "status": "COMPLETED",
  "statusReason": "1 remediation(s) in COMPLETED status",
  "taskDetails": [
    {
      "repoName": "<owner>/juice-shop",
      "pullRequestLink": "https://github.com/<owner>/juice-shop/pull/1"
    }
  ]
}

ボタンを押してから約2分で、リポジトリにPRが作られました。押した直後の画面は更新されませんでしたが、この検出結果を開き直すと、詳細の下のほうに「コード修正」の欄があり、ステータスとPRのリンクが表示されていました。

2026-10-22-security-agent-pentest-code-remediation-13

作られたPRの中身を見る

作成者はaws-security-agent[bot]で、ブランチ名はaws-continuum-<数字>、マージ先はmaster(このフォークのデフォルトブランチ)です。1コミットで、コミットメッセージの先頭に[skip ci]が付いています。[skip ci]は、コミットメッセージにこの文字列を入れておくと、そのコミットではGitHub Actionsなどのワークフローが起動されなくなる記法です。つまり、このPRのコミットでは、リポジトリのCIが動きません。PRに含まれるテストの書き換えが通るかどうかは、自分で確認する必要があります。

https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/skipping-workflow-runs

2026-10-22-security-agent-pentest-code-remediation-14

PRの本文は、先頭の「Pentest Information」と、そのあとの「Summary / Problem / Fix / Files Changed」(何が問題で、どう直したか)でできています。

「Pentest Information」には、このPRがどの検出結果から作られたかを示す情報が入っていました。

項目 内容
Pentest Title ペネトレーションテストの名前
Finding Title 検出結果のタイトル
Web URL コンソールの、該当するジョブの「検出結果」タブへのリンク
Agent Space ID Agent SpaceのID
Pentest ID ペネトレーションテストのID
Pentest Job ID 検出結果が属する実行(ジョブ)のID
Finding ID 検出結果のID

PRからコンソールへは、Web URLか、これらのIDでたどれました。コンソールからPRへは、検出結果の「コード修正」の欄のリンクと、先ほどのCLIの出力に出ていたtaskDetails[].pullRequestLinkでたどれました。

変更はroutes/memory.tsの1行です。GET /rest/memoriesの処理は、Sequelize(Node.jsのORM)のfindAllで写真の一覧を取り、includeで、投稿したユーザーのモデル(UserModel)を結合して返しています。修正前は、include: [UserModel]とモデルだけを渡していたので、ユーザーの全カラム(パスワードハッシュ、会員トークン、TOTPシークレット、ロール、最終ログインIPなど)が、そのままレスポンスに入っていました。修正後は、includeに{ model: UserModel, attributes: [...] }の形でオプションを渡し、attributesに返すカラムをid・email・usernameの3つだけ指定しています。

2026-10-22-security-agent-pentest-code-remediation-15

-    const memories = await MemoryModel.findAll({ include: [UserModel] })
+    const memories = await MemoryModel.findAll({ include: [{ model: UserModel, attributes: ['id', 'email', 'username'] }] })

PRの本文には、id・email・usernameは「Photo Wallのフロントエンドが必要とする最小限の項目」と書かれていましたし、パスワードなども抜けているので修正対応できていそうです。

複数の検出結果をまとめて指定する

ここまでは、コンソールの「コードを修正」ボタンから、検出結果を1件ずつ実行しました。今度は、StartCodeRemediationのAPIで、2件の検出結果をまとめて指定してみます。このAPIは、検出結果のIDをリストで受け取ります。

指定したのは、ペネトレーションテストで見つかった、次の2件です。どちらも重大度はHighです。

商品検索のSQLインジェクション

GET /rest/products/searchのqパラメータは、検索語をそのままSQLに文字列として連結して、商品テーブルへのクエリを組み立てています。'))でLIKE句を閉じて、あとにUNION SELECT ... FROM Usersを続けると、ログインなしで、全ユーザーのメールアドレスとパスワードハッシュを取得できます。検出結果が推奨している対応は、次の3つです。

  • 検索語をパラメータ化したクエリやORMのバインドで渡して、常にデータとして扱う
  • 生のSQLやスタックトレースをクライアントに返さず、汎用のエラーにする
  • MD5を、ソルト付きの遅いハッシュ(bcryptやargon2)に移行する

ファイルアップロードのXXE

POST /file-uploadは、ログインなしでXMLファイルをアップロードできます。このXMLを読み込むときに、XMLの中に書かれた「外部のファイルを取り込む指定」(外部エンティティ)を、そのまま処理してしまいます。そのため、file:///etc/passwdのようなサーバー上のファイルを指定したXMLをアップロードすると、そのファイルの中身が、エラーの本文に入って返ってきます。データを大量に展開させる攻撃(Billion Laughs)で、サービスを止められる問題もあります。検出結果が推奨している対応は、主に次の3つです。

  • XMLパーサーで、外部エンティティの展開とDTDの読み込みを無効にする(これだけで、ファイルの読み取りもサービス停止も防げる)
  • パースした内容を、エラーメッセージに入れてクライアントに返さない
  • 使われていない(非推奨の)XMLアップロードの経路は、廃止するか、認証の後ろに置く

この2件を、1回の呼び出しで指定しました。AWS CLIでは、次のコマンドになります。

$ aws securityagent start-code-remediation \
    --region ap-northeast-1 \
    --agent-space-id <agent-space-id> \
    --pentest-job-id <pentest-job-id> \
    --finding-ids <sqli-finding-id> <xxe-finding-id>

レスポンスは空で、エラーなく受理されました。結果は、検出結果ごとに別のPRです。2件で2本のPRができました。

作成者はどちらもaws-security-agent[bot]で、本文の構成は先ほどのPRと同じです。

2026-10-22-security-agent-pentest-code-remediation-16

2026-10-22-security-agent-pentest-code-remediation-17

検出結果 PR 作成までの時間 変更
商品検索のSQLインジェクション #3 約3分 3ファイル(+45 / −88)
ファイルアップロードのXXE #4 約4分 4ファイル(+92 / −22)

PRは、検出結果に対して1:1で作られます。PRの「Pentest Information」にも、その1件の検出結果のIDが入っていて、ジョブIDは、--pentest-job-idに指定したものと同じです。

2つのPRの差分を読んでみました。

SQLインジェクションのPR(#3)は、3ファイルを変更しています。実コードはroutes/search.tsの1ファイルで、検索語をSQLの文字列に直接埋め込んでいたところを、値をエスケープして埋め込む形に変えています。残りの2ファイルは、テストコードでした(test/api/search.test.tsとtest/cypress/e2e/search.spec.ts)。

Juice Shopは、脆弱性を学ぶためのアプリなので、既存のテストは「攻撃が成功すること」を確かめる内容でした。たとえば、SQLインジェクションで構文エラーが返ること、UNIONでUsersテーブルのメールアドレスとパスワードハッシュが取れること、といったものです。脆弱性を直すと、これらのテストは失敗します。そこでエージェントは、テストも「攻撃文字列が検索語として扱われて、0件が返ること」を確かめる形に書き換えています。このように修正したコードの背景に合わせてテストコードまで柔軟に変更してくれました。

2026-10-22-security-agent-pentest-code-remediation-18

XXEのPR(#4)は、4ファイルを変更しています。実コードはlib/xml.tsとroutes/fileUpload.ts、テストコードはtest/api/file-upload.test.tsとtest/server/xml.unit.test.tsです。

主にroutes/fileUpload.tsに変更がありました。Juice Shopには、攻撃に成功すると「チャレンジ達成」と判定される仕組みがあります。XXEのチャレンジは、攻撃で読めたファイルの中身を見て、達成を判定していました。修正で攻撃が成功しなくなると、この判定ができなくなります。そこでエージェントは、アップロードされたXMLの文字列を正規表現で調べて、/etc/passwdなどを狙った内容なら達成と判定するコードを足していました。脆弱性を直すだけなら要らないコードですが、PRの本文には、この達成判定を保つためと書かれていました。このように単純に検知した脆弱性を修正するだけじゃなく、アプリの目的・コンテキストも加味した修正を提案してくれていました。

2026-10-22-security-agent-pentest-code-remediation-19

PRをマージして再検証する

#1(/rest/memories)と#3(SQLインジェクション)の2つのPRを、フォークのmasterにマージしました。

マージしたあとのmasterから、リポジトリのDockerfileでイメージ(arm64)をビルドし、ECRにpushして、ECSのサービスを再デプロイしました。

再検証は、検出結果の詳細画面で「この検出結果を選択して再検証する」にチェックを入れ、「1個の検出結果を再検証する」を押します。今回は2件を、1件ずつ別のジョブで実行しました。

2026-10-22-security-agent-pentest-code-remediation-20

2026-10-22-security-agent-pentest-code-remediation-21

2つの再検証ジョブが、同時に進行中になっています。

2026-10-22-security-agent-pentest-code-remediation-22

対象の検出結果 PR 期間(実時間) タスク時間 判定
商品検索のSQLインジェクション #3 37分 0.449h Resolved
/rest/memoriesの情報漏洩 #1 27分 0.321h Resolved

再検証の結果、SQLインジェクションは再現せず、検出結果は解決済み(Resolved)になりました。再検証のジョブには、「NOT reproducible; endpoint is parameterized」(再現しない。エンドポイントはパラメータ化されている)というタイトルの検出結果が記録されていました。

2026-10-22-security-agent-pentest-code-remediation-23

説明には、エージェントが確かめた内容が書かれています。

  • 元の指摘にあった、エラーメッセージからクエリが漏れるプローブ(q='()が、500とクエリの表示ではなく、200と空の配列({"status":"success","data":[]})を返すようになった
  • q='は、名前が先頭にアポストロフィで始まる実在の商品を返す。引用符が、SQLの構文ではなく、検索語の一部として扱われている
  • 約30種類のSQLインジェクションのペイロードを試して、元の攻撃(UsersテーブルのUNION)も含め、データは返らなかった

このように、入力がSQLの構文ではなくデータとして扱われることが確認できて、#3の修正で、脆弱性が解決したと判定されました。

/rest/memoriesのほうも、再検証で解決済みになりました。再検証のジョブには、「Memories endpoint no longer leaks credential material」(認証情報の漏洩はなくなった)というタイトルの検出結果が記録されていました。

2026-10-22-security-agent-pentest-code-remediation-24

こちらの説明には、返されるユーザー情報が{id, email, username}だけになり、パスワードハッシュ・会員トークン・TOTPシークレット・ロールは含まれなくなったことが書かれていました。emailは残っていますが、エージェントは「残っているのはメールアドレスとユーザー名だけで、検出結果が主張していた認証情報の漏洩には当たらない」と判断しています。

PRでemailを残した修正でも、検出結果が根拠にしていたパスワードハッシュと会員トークンが消えたので、Resolvedになりました。

プルリクエストのコードレビューの挙動を確認する

コードレビューコメントの機能そのものは、以前の記事で試しています。今回確認したいのは、コード修正と同じGitHub連携の上で、エージェントが作った修正PRにも、コードレビューのコメントが付くかどうかです。付くなら、エージェントが書いた修正も、人が書いたコードと同じように、マージする前にセキュリティの観点でレビューにかけられます。比較のために、人が作ったPRでの動きも確認しました。

コードレビューコメントは、先ほど「機能を管理」で、コード修正と一緒に有効にしました。コードレビューのタブでも、有効になっていることを確認できます。

2026-10-22-security-agent-pentest-code-remediation-25

人が作ったPR

脆弱性を含むコードを適当に書いたPRを、試しに作成してみました(master宛、1ファイル、ドラフトではない通常のPR)。

PRを開いてから約23秒で、aws-security-agent[bot]が「AWS Security Agent is reviewing your pull request and will post feedback shortly.」とコメントしました。約4分後にレビューが終わり、「2 security findings: 2 critical」というコメントと、該当の行へのインラインコメントが2件付きました。

2026-10-22-security-agent-pentest-code-remediation-26

インラインコメントには、重大度(CRITICAL)、何が問題か、証拠となるペイロード、推奨する修正が書かれています。「このファイルはまだどのルートにも接続されていないので、いまは休眠しているが、接続された時点で悪用できる」という注記まで読み取っていました。また、コメントの末尾には、自動修正を依頼する場合は、PRの会話に@AWS-Security-Agent fix all findingsとコメントするよう書かれています。ドキュメントにも、コード修正をオンにしていれば、PRの作成者がこのコメントで、エージェントにレビューの指摘を直させられる、と書かれています(ドキュメントでは、末尾にピリオドが付いた@AWS-Security-Agent fix all findings.の形で載っています)。

案内どおりに、PRの会話にコメントしてみました。

2026-10-22-security-agent-pentest-code-remediation-27

約15分待っても、エージェントの反応はありませんでした。小文字の書き方、末尾のピリオドを付けた書き方、インラインコメントへの返信でも試しましたが、同じでした。手動でレビューを起動するコメント(@AWS-Continuum-Pentesting review)も、このPRで試しましたが、反応はありませんでした。色々調べましたが理由は特定できませんでした。

Security Agentが作ったPR

一方で、Security Agentが作った修正PR(#1)には、コードレビューのコメントが付きませんでした。手動で起動するコメント(@AWS-Continuum-Pentesting review)をPRの会話に投稿しても、反応はありませんでした。

そこで、XXEの修正PR(#4)を、いったんDraftに戻してから、もう一度「Ready for review」にして開き直してみました。

2026-10-22-security-agent-pentest-code-remediation-28

2026-10-22-security-agent-pentest-code-remediation-29

2026-10-22-security-agent-pentest-code-remediation-30

すると、コメントが付きました。人が作ったPRのときのようなレビューの形式のコメントではなく、「No issues identified.」(指摘なし)という通常のコメントが1件です。

2026-10-22-security-agent-pentest-code-remediation-31

エージェントが自動で作ったPRにはコメントが付かず、開き直したときに付いたのは、トリガーのイベントが「プルリクエストのレビューの準備ができました」なので、PRを開き直す操作で、このイベントが発生したからだと思います。
コードレビューに関しては一部ドキュメントと異なる挙動だったり、ドキュメントだけでは読み取れない挙動をしました。

最後に

今回は、プライベートリポジトリで、既存の検出結果からコード修正のPRを作るところから、マージ、再検証までを試し、さらにAgentが自動で作成した修正PRにコードレビューコメントがつくのかまで確認しました。設定の確認先が2か所にあることと、コード修正PR作成中はコンソール画面に進捗が出ないこと(CLIで確認する)は、事前に知っておくと便利だと思います。

今回のケースで分かったこととして、コード修正のPR精度は高いものであり、単に脆弱性の修正だけでなくアプリの目的やコードの意図を汲み取ってテストコードまで修正してくれるなどの挙動が確認できました。

エージェントが作成してくれたPRの中身をそのまま適用しても脆弱性は修正されましたが、運用では必ず人間の手で差分を読むことをおすすめします。

今回は以上です。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事