scan-site について
scan-site は Power Pages プロジェクト向けのセキュリティスキルです。デプロイ済みサイトをスキャンして最新レポートを取得し、検出内容を要約します。ローカルのソースコードではなく、稼働中のサイトを対象にします。
主な機能
- 01デプロイ済み Power Pages サイトのスキャン
- 02スキャン状態と最新レポートの確認
- 03検出内容の平易な要約
- 04HTML レポートの生成
- 05関連するセキュリティスキルの提案
活用シーン
- 01サイトの現在のセキュリティ上の問題を確認
- 02デプロイ済みサイトを新規スキャン
- 03HTML レポートで結果を共有
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ microsoft/ power-platform-skills --skill scan-siteインストール後、Claude Code で/scan-siteを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/commands.mdscan-reference.mdscripts/get-latest-report.jspoll-deep-scan.jsstart-deep-scan.jstransform-report.js詳細
プラグインの確認:
node "${PLUGIN_ROOT}/scripts/check-version.js"を実行します。メッセージが出力された場合は、先にユーザーに表示してから続行してください。
サイトをスキャンする
デプロイ済みの Power Pages サイトでセキュリティスキャンを実行し、最新のスキャンレポートを取得して、検出結果を平易な言葉で要約します。スキャンはサーバー側で実行されます。所要時間はサイトの規模によって異なり、小規模なサイトは数分、大規模なサイトは数時間かかる場合があります。
このスキルは、ローカルのソースコードではなく、デプロイ済みの実サイトをスキャンします。
初回リクエスト: $ARGUMENTS
注意点
- Web サイトレコード ID とポータル ID の違い。
.powerpages-site/website.ymlに格納されているのは Web サイトレコード ID であり、ポータル ID ではありません。すべてのスクリプトで--portalIdを使用します。前提条件の確認中にwebsite.js --websiteIdを使って一度だけ解決してください。 - 名前で解決しないでください。 環境内でサイト名が重複する場合があるため、Web サイトレコード ID のみが安全です。
- リゾルバーの
nullは、サイトがデプロイされていないか、認証済みプロファイルが別の環境を参照していることを意味します。 - スキャンには時間がかかります。 所要時間はサイトの規模によって異なり、小規模なサイトは数分、大規模なサイトは数時間かかる場合があります。バックグラウンドでポーリングし、大規模なサイトでは
--timeoutMinutesの値を大きくしてください。 - 1 つのサイトで同時に実行できるスキャンは 1 つだけです。 スキャンの実行中に開始すると
Z003が返されます。start-deep-scan.jsはこれを{ "status": "already-running" }として報告します(終了コード 0)。 - レート制限が適用される場合があります。 サービスは、同じサイトに対するスキャンの繰り返しを制限することがあります。制限された場合は、時間をおいてから再試行してください。
- 完了済みのスキャンがありません。 新しいサイト、またはスキャン中のサイトには完了済みのレポートがありません。
get-latest-report.jsは{ "status": "empty" }を返します。
ワークフロー
- 前提条件 — プロジェクトを特定し、サインインを確認してサイトを特定します
- スキャン状態を確認 — 現在スキャンが実行中かどうかを検出します
- アクションを選択 — 状況に応じた推奨を提示します(新しいスキャンを実行/最新の結果を表示)
- スキャンを実行 — 開始して完了までポーリングします
- 取得して要約 — レポートを取得し、検出結果を提示します
- 後続の対応を案内 — 問題がレポートに含まれる場合に限り、適切な後続スキルに問題を引き継ぎます
タスクの追跡
4 つのグループに分けてタスクを作成します。各タスクは開始時に in_progress、完了時に completed に設定してください。
| グループ | 作成するタイミング | タスク |
|---|---|---|
| 1 | 開始時 | 前提条件を確認 |
| 2 | 前提条件の確認後 | スキャン状態を確認 · アクションを選択(レビュー・モードではスキップ) |
| 3 | ユーザーがアクションを確認した後(またはレビュー・モードの場合) | スキャンを実行(対話モードでユーザーが最新の結果の表示を選んだ場合に限りスキップ) · 取得して要約(必ず実行) |
| 4 | 取得して要約した後 | 後続の対応を案内(レポートに問題が含まれ、かつレビュー・モードでない場合に限る) |
1. 前提条件
1.1 プロジェクトを特定し、レビュー・モードを検出する
Glob を使って **/powerpages.config.json を検索します。$ARGUMENTS に --review <out-dir> が含まれている場合は、出力ディレクトリを記録します。この場合、ステップ 3(アクションを選択)はスキップされ、ステップ 4(スキャンを実行)は自動的に実行され(新しいスキャンを開始するか、実行中のスキャンに接続します)、ステップ 5 では JSON のみを書き込み、ステップ 6(後続の対応)はスキップされます。
1.2 サイト識別子を解決する
.powerpages-site/website.yml を読み込み、id フィールドを取り出します。これが <WEBSITE_ID> です。
値がない場合、サイトはデプロイされていません。ユーザーに伝え、/deploy-site を推奨してください。停止します。名前や URL で解決してはいけません。
portalId に解決します。
node "${PLUGIN_ROOT}/scripts/website.js" --websiteId "<WEBSITE_ID>"
Id(portalId)、Type、Name、WebsiteUrl を取得します。終了コードが 2 の場合は、サインインが必要です(pac auth create または az login)。null の場合は、この環境でサイトが見つかりません。どちらの場合も停止します。
2. スキャン状態を確認する
node "${PLUGIN_ROOT}/skills/scan-site/scripts/poll-deep-scan.js" --portalId "<PORTAL_ID>" --once
--once は状態を 1 回だけ確認し、終了コード 0 で終了して、次のいずれかを出力します。
{ "status": "ongoing" }→ スキャンが現在実行中です。{ "status": "idle" }→ 実行中のスキャンはありません。
次に get-latest-report.js を呼び出し、完了済みのレポートがあるかどうかを確認します。
node "${PLUGIN_ROOT}/skills/scan-site/scripts/get-latest-report.js" --portalId "<PORTAL_ID>"
{ "status": "ok" } はレポートを取得できることを意味します。{ "status": "empty" } は完了済みのスキャンがないことを意味します。
3. アクションを選択する
レビュー モードではスキップし、ステップ 4 に進みます(ステップ 4 は常にレビュー モードで実行されます)。
平易な表現のみを使用してください。CSP、CORS、OWASP、hardening、scan profile などの言葉は決して使用しないでください。
デフォルトの進め方
🚦 ゲート(プラン · scan-site:3.action-choice): サイトのスキャン状態(実行中、待機中、レポートあり、レポートなし)に基づいてアクションを提案し、ユーザーにその提案を受け入れるか、別のアクションを選ぶか尋ねます。新しいスキャンを開始すると、バックエンドの処理に数分かかります。既存のレポートを使う場合、費用はかかりません。
実行条件: フェーズ 3 の開始時(対話モードのみ。レビュー モードではステップ 4 に進みます)。 確認する理由: 最近のレポートで質問に答えられる場合に新しいスキャンを自動で開始すると時間を無駄にし、古いレポートを自動で使うと最近の検出結果を見落とす可能性があります。 キャンセル時の状態: 何も変わりません。スキャンは開始されず、レポートも消費されません。
サイトの現在の状態を分析し、AskUserQuestion を使って最も関連性の高いアクションを 1 つだけ提案してください。
- スキャン実行中で、完了済みのレポートがない場合 → 実行中のスキャンが完了するまで待つことを提案します。
- スキャン実行中で、レポートがある場合 → 新しいスキャンの実行中に最新の結果を表示することを提案します。
- 待機中で、完了済みのレポートがない場合 → 新しいスキャンの実行を提案します。
- 待機中で、最近のレポートがある場合 → 既存のレポートを使うか、新しいスキャンを実行するかを尋ねます。
サイトの状態から特定のアクションを提案する必要がない場合は、無理に提案せず、ユーザーが何をしたいか尋ねてください。
オプションのルール
AskUserQuestion でオプションを提示する場合:
labelは 1~5 語にします。すべてのオプションにdescriptionを含めてください。- 新しいスキャンを開始するオプションでは、ユーザーが判断できるよう、関連する注意事項をそのオプションの
descriptionに記載してください。ユーザーがオプションを選んだ後に、別途確認を求めてはいけません。 previewを含めるのは、新しいスキャンの開始など、具体的な変更を伴うオプションの場合に限ります。「最新の結果を表示」や情報提供のみの選択肢にpreviewを追加してはいけません。- 現在の状態で実行可能なオプションのみを表示してください。すでにスキャンが実行中の場合、「新しいスキャンを開始」は提示しないでください。
- サイトの状態が正当化する場合にのみ「(推奨)」を付けてください。
4. スキャンを実行する
レビュー モードでは、このステップを必ず実行してください。すでにスキャンが実行中の場合は接続してポーリングし、それ以外の場合は新しいスキャンを開始してポーリングします。尋ねてはいけません。レビュー モードではユーザーの操作を求めず、最初から最後まで実行します。
対話モードでは、ユーザーが最新の結果を表示することを選んだ場合、このステップをスキップします。
スキャンを開始します:
node "${PLUGIN_ROOT}/skills/scan-site/scripts/start-deep-scan.js" --portalId "<PORTAL_ID>"
標準出力が { "status": "already-running" } の場合は、ポーリングに進みます。すでにスキャンが実行中です。
次に、完了するまでポーリングします:
node "${PLUGIN_ROOT}/skills/scan-site/scripts/poll-deep-scan.js" --portalId "<PORTAL_ID>"
ユーザーが作業を続けられるように、run_in_background: true を指定してポーリングを実行します。スクリプトはスキャンが完了するか、タイムアウトに達すると終了します(デフォルトは 20 分)。タイムアウトした場合は、取得可能なレポートを取得し、要約にタイムアウトしたことを記載してください。
5. 取得して要約する
5.1 レポートを取得して変換する
node "${PLUGIN_ROOT}/skills/scan-site/scripts/transform-report.js" --portalId "<PORTAL_ID>"
標準出力の JSON を解析します。ステータスフィールドには次のいずれかが指定されます:
ok—findingsとdetailsを含む通常のレポートです。empty— このサイトには完了済みのスキャンがありません(例:新しいサイト、またはスキャンがまだ実行中)。このことを説明するinfo検出結果を 1 件記録し、続行します。malformed— API から返されたレスポンスにRules配列がありません。変換処理は、このことを説明するwarning検出結果を 1 件出力します。これをユーザーに伝え、スキャンの再実行を提案してください。
スクリプトが適用する Risk → 重要度のマッピングについては、references/scan-reference.md を参照してください。
5.2 レビュー モード
レビュー モードでは、HTML レポートをスキップし、変換処理の標準出力を <REVIEW_DIR>/scan-site.json に書き込みます。その後、停止します。変換処理は { status, findings, details } を出力し、プレゼンテーションはオーケストレーションを行うスキルが処理します。
5.3 HTML レポートをレンダリングする
レビュー モードではスキップします。
レンダリングには、統合セキュリティレビューと同じ共有テンプレートを使用します。単一セクションのレビュー データ ペイロードを作成してから、レンダリングします。
node "${PLUGIN_ROOT}/scripts/build-review-data.js" \
--reportName "Site Scan" \
--inputDir "<TEMP_DIR>" \
--siteName "<SITE_NAME>" \
--goalLabel "Live Site Scan" \
--scopeLabel "<SCOPE_LABEL>" \
--summary "<SUMMARY_TEXT>" \
--output "<TEMP_DIR>/data.json"
node "${PLUGIN_ROOT}/scripts/render-review.js" \
--data "<TEMP_DIR>/data.json" \
--output "<PROJECT_ROOT>/docs/site-scan-<YYYY-MM-DD-HHMMSS>.html"
<TEMP_DIR>にはscan-site.json(ステップ 5.1 の変換出力)のみを格納してください。build-review-data.jsは中間ファイルを無視します。ファイル名にはローカルのタイムスタンプを必ず含めてください(例:site-scan-2026-05-14-053805.html)。レンダリングに成功したら、<TEMP_DIR>を削除します。レンダリングしたHTMLをブラウザーで開きます。
5.4 概要を提示する
チャットで平易な言葉による概要を伝えます。検出結果の合計数、重大度別の件数、前回のスキャンからの変更点(確認できる場合)を含めます。技術的な名称から説明を始めないでください。
5.5 スキルの使用を記録する
参照:
${PLUGIN_ROOT}/references/skill-tracking-reference.md
--skillName "ScanSite"を使用してください。
6. 追加対応を案内する
レビュー モードではスキップします。レポートに問題がない場合もスキップします。
検出結果を、対応に役立つ下流スキルごとにまとめます。
- ヘッダー/Cookie の問題 →
/manage-headers - WAF/ファイアウォールの問題(ボットのブロック、ページへのアクセス頻度の制限、IP/国の制限)→
/manage-firewall - 権限の問題 → 既存のテーブル権限を確認するには
/audit-permissions、ロールベースのアクセスを設定するには/create-webroles、またはその両方 - ログインまたは外部 ID の問題 →
/setup-auth - コードレベルの問題(デバッグページの露出、情報漏えい、ソースコードの一般公開)→ 手動でのコード修正を提案します。これらの検出結果に対応するスキルはありません
レポートに実際に含まれる検出結果に該当するスキルのみを提案してください。どのスキルにも該当しない検出結果は、ユーザーが対応できる手動の追加対応として伝えます。意味のある追加対応がなければ、そのままスキルを終了します。質問すること自体を目的に質問しないでください。
制約
- 平易な言葉 — ユーザーに技術用語を使ってはいけません。日常的な言葉を使い、求められた場合にのみ技術用語を説明します。
- 読み取り専用 — このスキルはスキャンを実行し、結果を読み取るだけです。WAF の有効化、スキャンの削除、サイト設定の変更は行いません。
- 長時間実行される呼び出しはバックグラウンドで実行 — スキャンを開始してから、
run_in_background: trueでポーリングし、ユーザーが作業を続けられるようにします。 - 状況に応じたやり取り — すべての推奨事項は、サイトの現在の状態を反映する必要があります。
- スキャンの実行中に「新しいスキャンを開始する」と提案しないでください。
- 完了したレポートがない場合、「最新の結果を表示する」と提案しないでください。
- 状況に照らして適切な場合にのみ「(推奨)」を付けてください。
- プレビューは変更内容の確認専用 — 新しいスキャンを開始する選択肢にのみ
previewを含めます。ナビゲーションや情報提供の選択肢には追加しないでください。
参考資料
references/commands.md— スクリプトのフラグ、応答形式、エラーカタログ、運用上の注意事項。スクリプトがゼロ以外の終了コードを返した場合は、「Common error catalogue」の節を読んでください。references/scan-reference.md— ディープスキャンレポートのフィールドごとのスキーマ、アラートのリスク値、ルールのステータス、重大度の対応付け。検出結果を標準化する際に読んでください。