audit-permissions について
audit-permissions は Power Pages サイトのテーブル権限を、サイトコードや Dataverse メタデータと照合する監査スキルです。固定ルールでスコアを算出し、指摘事項や修正案を含む HTML レポートを作成します。
主な機能
- 01コードと Dataverse メタデータを権限と照合
- 02テーブル単位の A–K 監査チェック
- 03固定ルールによるカテゴリ別スコア算出
- 04根本原因の集約と問題の発生元の分類
- 05HTML レポートの生成と検証
活用シーン
- 01不足または過剰な CRUD 権限の検出
- 02関連、展開、親権限チェーンの確認
- 03権限レビュー向けの再現可能なスコア作成
- 04根拠と修正案の取りまとめ
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ microsoft/ power-platform-skills --skill audit-permissionsインストール後、Claude Code で/audit-permissionsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/audit-report.htmlreferences/non-spa-site-workflow.mdscripts/collect-non-spa-permission-evidence.jsquery-table-lookups.jsquery-table-relationships.jsvalidate-audit.js詳細
翻訳後に原文が更新されています。最新の内容は原文をご確認ください。
プラグインの確認:
node "${PLUGIN_ROOT}/scripts/check-version.js"を実行します。メッセージが出力された場合は、続行する前にユーザーに表示してください。
アクセス許可の監査
Power Pages のコードサイトに既存のテーブルアクセス許可が設定されているか監査します。サイトコードと Dataverse のメタデータに照らしてアクセス許可を分析し、決定論的な 1~5 のカテゴリスコア、助言的な判定、検出事項、根拠、推奨される修正を含むビジュアル HTML レポートを生成します。
このスキルは、次の 2 つの部分で構成されています。
- 監査チェック(根拠と修正) — 各テーブルに対する A~K のチェックリストで、すべてのアクセス許可を実際のコード使用状況と Dataverse のメタデータに照らして比較します。これは主要な問題検出機能です。コードに基づいた具体的な問題(CRUD の不足、append/appendto の不足、
$expandの不足、壊れた親チェーン、過剰なアクセス許可)を検出し、それぞれを具体的な根拠と修正方法を示して重大または軽微な問題として出力します。 - スコアリング(再現可能な結果) — 固定された検出条件と重大度からなる繰り返し発生する問題のカタログを基準とした3 カテゴリの評価基準(9 つの評価項目)を使用し、その後、1 回の統合処理で根本原因をまとめ、重大な問題に対して1 回の一括根本原因帰属を行います。検出事項を純粋な数式による 1~5 のカテゴリスコアに変換するため、同じ構成であれば 2 回実行しても同じ数値になります。
スコアリングの基本ルール: 行うのは、問題を検出して分類すること(
major/minor)だけです。数値スコアを独自に決めてはいけません。1~5 の数値は、問題数に固定の数式を適用して算出します(付録 A を参照)。同じ問題の集合 ⇒ 同じスコアです。
ワークフロー
- サイトのデプロイを確認 —
.powerpages-siteフォルダーとテーブルアクセス許可が存在することを確認します - 構成を収集 — Web ロール、テーブルアクセス許可、サイトコードを読み込み、共有スキーマバリデーターを実行し、決定論的な事前照合を構築します
- 分析して調査 — 決定論的なスクリプトを使用して、Dataverse にリレーションシップとルックアップ列を問い合わせます
- 監査チェックを実行 — アクセス許可をコードの使用状況とベストプラクティスに照らして比較します(A~K のチェックリスト)
- 構成をスコアリング — 繰り返し発生する問題のカタログを使用して 3 つのカテゴリ別に問題を検出・分類し、異なる根本原因(重大/軽微)にまとめ、1~5 のカテゴリスコアと判定を算出します
- 根本原因の帰属 — 1 回の一括処理で、すべての重大な問題をアクセス許可のみが原因か、上流の成果物が原因かに分類し、レポートに表示する 1 文の根拠を添えます
- レポートを生成 — HTML レポート(スコアの概要、検出事項、インベントリ)を作成して検証します
- 検出事項を提示して記録 — 概要を提示し、スキルの使用状況を記録して、問題の修正を希望するかユーザーに確認します
重要: 分析中にユーザーへ質問してはいけません。すべてのデータを自律的に収集してから、検出事項を提示します。
監査完了テレメトリ
ステップ 1 の開始時、サイトを検証する前に、ローカルの実行状態を初期化します。
node "${PLUGIN_ROOT}/scripts/emit-audit-permissions-telemetry.js" --action start
JSON の標準出力を解析し、この実行で使用する <AUDIT_RUN_ID> として auditRunId を保持します。このコマンドはテレメトリイベントを出力しません。既存の汎用スキル呼び出しイベントが開始シグナルとなります。ローカルの実行状態には、完了テレメトリに必要な不透明なランダム ID と開始時刻が含まれます。サイト、環境、テナント、ユーザー、パス、レポートの値から ID を導出したり、それらの値で ID を置き換えたりしてはいけません。
テレメトリはベストエフォートであり、監査を妨げてはいけません。初期化コマンドが失敗した場合、または auditRunId が返されない場合は、完了テレメトリを使わずに監査を続行します。ID を捏造したり、ローカル状態から最新の実行を検索したりしてはいけません。
実行 ID が返された後に、制御可能な終端エラーが発生した場合は、処理を停止する前に実行を終了します。
node "${PLUGIN_ROOT}/scripts/emit-audit-permissions-telemetry.js" \
--action complete \
--runId "<AUDIT_RUN_ID>" \
--outcome failure \
--failureStage "<FAILURE_STAGE>"
次の固定された失敗ステージから、必ず 1 つだけを使用します。
site_verificationconfiguration_gatheringschema_validationrelationship_discoveryaudit_checksscoringroot_cause_attributionreport_renderingreport_validationmetrics_validationunknown_controlled_failure
エラーメッセージ、パス、テーブル名、検出内容、その他のユーザーまたはサイトのデータをテレメトリスクリプトに渡してはいけません。テレメトリ完了処理が失敗しても監査結果は変わりません。また、完了処理を再試行してはいけません。
タスク管理
ステップ 1 の開始時に、TaskCreate を使用してすべてのタスクを作成します。各タスクの開始時に in_progress、完了時に completed に設定します。
| タスクの件名 | activeForm | 説明 |
|---|---|---|
| サイトのデプロイを確認 | サイトのデプロイを確認中 | .powerpages-site フォルダーとテーブル権限が存在することを確認 |
| 構成を収集 | 構成を収集中 | Web ロール、テーブル権限、サイトコードを読み込み、バリデーターを実行して、事前確認を作成 |
| リレーションシップを検出 | リレーションシップを検出中 | Dataverse にクエリを実行して、ルックアップ列とリレーションシップを検出 |
| 監査チェックを実行 | 監査チェックを実行中 | テーブルごとのタスクを作成し、各テーブルにチェックリスト(A–K)を適用してから相互検証 |
| 構成をスコアリング | 構成をスコアリング中 | 定期的な問題カタログを使って 3 つのカテゴリで検出・ラベル付けし、根本原因に集約して 1~5 のスコアを算出 |
| 根本原因を特定 | 根本原因を特定中 | すべての主要な問題を、権限のみ、または上流原因として 1 回の処理でラベル付け |
| レポートを生成 | レポートを生成中 | HTML レポートを作成して検証し、ブラウザーに表示 |
| 検出結果を提示 | 検出結果を提示中 | 結果を要約し、使用状況を記録して、問題の修正を提案 |
注:「監査チェックを実行」フェーズでは、ステップ 4.2 でテーブルごとの追加タスクが動的に作成されます。これらのテーブルごとのタスクでは、各テーブルに対する体系的な A–K チェックリストを個別に追跡します。
ステップ 1:サイトのデプロイを確認
Glob を使って次の項目を検索します。
**/powerpages.config.json— プロジェクトルートを特定します**/.powerpages-site/table-permissions/*.tablepermission.yml— 既存の権限
.powerpages-site フォルダーが存在しない場合は、停止して、まず /deploy-site を使ってデプロイするようユーザーに伝えます。
テーブル権限が存在しない場合は、重大な検出結果として記録します(サイトにデータアクセスが構成されていない可能性があります)。権限が必要なコード参照が存在する可能性があるため、監査は続行します。
.powerpages-site がないために停止する前に、上記の説明に従って failureStage: site_verification で完了テレメトリを終了します。
ステップ 2:構成を収集
単一パスのパフォーマンス契約
判断を下す前に、ローカルの証拠をすべて1回の制限付きパスで収集します。
- Web ロール、テーブル権限、サイト設定、
.datamodel-manifest.jsonをまとめて読み込みます。 - ソースファイルを1回スキャンし、API 操作、エンティティセット、select、filter、lookup、expand、upload、ルート、import、機能/認証ゲートを確認します。グループ化した検索を1回行い、一致した箇所だけを対象に読み込む方法を優先してください。ソースツリー全体をファイルごとに読み込んではいけません。
- 権限プランを1回だけ特定します。認可に関係するすべての記述を1回の操作で抽出して
planClaimsに格納し、後からプランを再度開いたり再抽出したりしないでください。 - これらの事実を
audit-evidence.jsonに固定します。手順4~6では、この台帳と、ゲート条件を満たした Dataverse の結果だけを使用します。
確信を得るためにソースコードやプランを再スキャンしてはいけません。特定の台帳フィールドが欠けており、必要なファイル/領域が明確な場合に限り、2回目の読み込みを許可します。追加した証拠を1回だけ記録してから続行してください。同じプランの主張について、表現を変えて複数回検索してはいけません。
2.1 Web ロールの読み込み
**/.powerpages-site/web-roles/*.yml に一致するすべてのファイルを読み込みます。キーには発行元のプレフィックスが付いている場合があります。mspp_<key>、次に adx_<key>、最後にプレフィックスなしの <key> の順で試してください(最初に見つかったものを採用します)。各ロールから id、name、anonymoususersrole、authenticatedusersrole を抽出します。YAML の解析に失敗した場合、またはルートがオブジェクトでない場合は、解析エラーとして記録します(事前チェック IS に使用)。
2.2 テーブル権限の読み込み
**/.powerpages-site/table-permissions/*.tablepermission.yml に一致するすべてのファイルを読み込みます。同じプレフィックス規則を使用し、各権限から次の項目を抽出します。
entityname(権限名)entitylogicalname(テーブル)scope(数値コード)→ ラベルに変換します:756150000 = Global、756150001 = Contact、756150002 = Account、756150003 = Parent、756150004 = Self。それ以外はUnknown(<code>)、値がない場合は scope-1、ラベルUnknownread、create、write、delete、append、appendto(ブール値フラグ)adx_entitypermission_webrole(Web ロールの UUID の配列)→ §2.1 に従って各 GUID をロール名に解決します。解決できない場合は<unresolved:…>として記録します。contactrelationship、accountrelationship(Contact/Account スコープの場合)parententitypermission、parentrelationship(Parent スコープの場合)
失敗した場合は解析エラーとして記録します(事前チェック IS に使用)。
2.3 サイトコードの分析
単一のソースパスで、サイトのソースコードを検索し、次を確認します。
- Web API 呼び出し(
/_api/) - Lookup バインド(
@odata.bind) - ファイルアップロード(
uploadFileColumn、uploadFile、upload*Photo、upload*Image) $expandの使用($expand、buildExpandClause、ExpandOption)
また、権威あるテーブル一覧を確認するため、プロジェクトルートに .datamodel-manifest.json があるか調べます。このファイルの形式は { environmentUrl?, tables: [{ logicalName, displayName, status?, columns: [{ logicalName, type }] }] } です。ファイルがない場合、または tables が配列でない場合は、データモデルが存在しないものとして扱います。
さらに、Web API アーティファクトもスキャンします(手順6の根本原因の特定に使用。ベストエフォート):
- 中央クライアント:
src/shared/powerPagesApi.ts(x)またはsrc/lib/powerPagesApi.tsが存在する場合に該当します。 - サービスファイル:
src/shared/services/**(.ts/.tsx/.js/.jsx)にあるファイルごとに、データモデルのテーブル論理名と、使用されている HTTP メソッド(GET/POST/PATCH/PUT/DELETE)を記録します。 - ワイルドカード列の選択:スキャン対象のソースファイルで
$select=*(または値が*の$select)が出現する箇所をすべて、ファイルパスとクエリ対象のテーブルとともに記録します。 - 型ファイル:
src/types/**にあるファイルから、最初のinterface/typeリテラルのプロパティ名を抽出します。可能な場合は、ファイル名や内容からテーブルを推定します。 - Web API のサイト設定:
.powerpages-site/site-settings/内のWebapi-*/Webapi/*という名前のファイルから、nameとvalueを解析します。nameを/で分割します。Webapi/<entitySet>/enabledは種類enabled、.../fieldsは種類fields、Webapi/errorはerror、それ以外はotherとします。webapiEnabledEntitySetsは、enabledの値が正確に"true"であるエンティティセットです。すべてのfields設定について、isWildcardも記録します。値の前後の空白を除いた結果が*である場合、またはカンマ区切りの項目のいずれかが*である場合はtrueです。列の論理名を列挙したリストは、どれだけ長くてもワイルドカードではありません。
ファイル名にかかわらず、.powerpages-site/site-settings/ 内の認証/登録サイト設定もスキャンします。Authentication/Registration/ 配下の設定(Enabled、OpenRegistrationEnabled、ExternalLoginEnabled、InvitationEnabled を含む)について、name と value を解析します。また、外部プロバイダーごとに Authentication/<Type>/<Provider>/RegistrationEnabled も解析します。設定済みプロバイダー(Authentication/<Type>/<Provider>/ 配下に設定があるもの)にこの設定がない場合は、サーバーのデフォルトである true として扱います。名前の比較では大文字と小文字を区別せず、値は前後の空白を除いたうえで、大文字と小文字を区別せず "true" と一致する場合にのみ有効とみなします。設定がない場合は false ではなく unknown として記録します。
権限を評価する前に、オーディエンスの到達可能性プロファイルを作成します。
authenticatedusersrole: trueのロールは、サインインしたすべてのサイトユーザーに自動的に割り当てられます。これは幅広いベースラインロールであり、スタッフ、従業員、教師、保護者、その他の信頼されたペルソナに属することの証明にはなりません。- オープン登録が有効と判断できるのは、
OpenRegistrationEnabledがtrueであり、かつローカル登録が有効(Authentication/Registration/Enabledがtrue)であるか、ExternalLoginEnabledがfalseではなく(未設定の場合はサーバーのデフォルトであるtrueとして扱います)、設定済みの外部プロバイダーのうち少なくとも1つでRegistrationEnabledがtrueである場合です。外部認証のみのサイトでは、Authentication/Registration/Enabledが設定されていないことがよくあります。オープン登録が有効な場合、訪問者は誰でもアカウントを作成し、幅広い「Authenticated Users」オーディエンスに加わることができます。 - 外部ログインや招待の有効化によって追加のアクセス経路が提供される場合もありますが、設定による根拠なしに一般公開されていると判断してはいけません。
- このプロファイルを、テーブルごとのすべてのスコープおよびオーディエンスのチェックに引き継ぎます。ユーザーがそのロールをどのように取得するかを考慮せず、ロール名や権限だけを切り離して評価してはいけません。
次の対応関係をまとめます。コードで参照されているテーブル、各テーブルで実行される CRUD 操作、使用されるルックアップ関係、$expand で取得される関連テーブル(これらにも読み取り権限が必要です)。
また、不足しているアクセス権を評価する前に、機能マトリクスも作成します。認可に関係する機能ごとに1行を設けます。
persona | route/component | reachability | table | operation | current grant | plan claim | evidence
reachability は次の基準で機械的に分類します。
reachable— ページ/コンポーネントがデプロイ済みルーターに登録されているか、到達可能な別のコンポーネントから呼び出されており、明示的な機能フラグや認可ゲートによってそのペルソナで無効化されていません。disabled— 明示的なfalseの機能フラグ、サーバー側の認可ゲート、ビルドからの除外、または未到達/未登録のコンポーネントによって、その機能を実行できません。ambiguous— コードは存在しますが、到達可能かどうか、または除外されているかどうかを判断できません。 サインイン中のユーザー自身のデータ(たとえば、isAccountAdminフィールドやプロフィールレコードのロールフラグ)に基づく UI 条件は、機能を「無効」にするものではありません。その機能を使用するペルソナを識別するものです。「延期」「将来のフェーズ」「未対応」といった計画書やコメントの表現も、ルーティングされている機能を「無効」または「曖昧」にするものではありません。
監査では、常に次の証拠の優先順位を使用してください。到達可能な実装 > 明示的な実行時ゲート/ビルド除外 > 実装計画 > コメント/名称。計画書でペルソナを「バックオフィス」と呼んでいるだけで、ルーティングされ呼び出し可能な機能を仮のものと見なすことはできません。逆に、デッドコード、未登録コード、明示的に無効化されたコード、またはサーバー側でゲートされたコードは、必須のポータル権限の根拠にはなりません。到達可能性が曖昧で、計画書と矛盾する場合は、その矛盾について internal-consistency の minor イシューを最大 1 件出力してください。フローが壊れていると推定してはいけません。
ソースの確認後は、スコアリング中にルート、ページ、サービス、権限計画を再確認しないでください。到達可能性と計画に関する主張は、再検討する問いではなく、確定済みの台帳上の事実です。Step 5 ではこれらを再考しないでください。
2.4 共有スキーマバリデーターを実行する
既存のサイトに対して共有バリデーターを実行します。
node "${PLUGIN_ROOT}/scripts/validate-permissions-schema.js" --projectRoot "<PROJECT_ROOT>"
JSON 出力を解析し、検出結果を監査に major/minor イシューとして引き継ぎます。次のように対応付けます。
errorの検出結果 → major イシューwarningの検出結果 → major イシュー(純粋に将来的な内容/見た目上の内容の場合は minor)infoの検出結果 → minor イシュー
後続のコード/Dataverse 分析で追加のイシューが見つかった場合も、これらの検出結果を最終レポートに含めてください。例外:親チェーンが有効な Parent スコープの子権限については、バリデーターの「関連付けられた Web ロールがない」という警告を除外してください(チェック B を参照)。
バリデーターが Webapi/<table>/fields 設定でワイルドカードによるフィールドアクセスを報告した場合は、その検出結果を major の security-posture イシューとして追加します(該当する設定ごとに 1 件)。
- タイトル:
Unsupported wildcard Web API fields for <table> - 理由: このフィールド設定ではワイルドカードアクセスが使用されています。これは 2026 年 9 月 14 日以降、サポートされません。
- 修正方法:
${PLUGIN_ROOT}/references/webapi-field-allowlist.mdを使用してワイルドカードを置き換えます。通常の列には LogicalNames、ルックアップの読み取りには_<LogicalName>_value、@odata.bindで使用するナビゲーションプロパティには正確な Navigation Properties を使用してください。 - 詳細: バリデーターの検出結果に記載されているサイト設定ファイルのパスと設定名を含めてください。
Step 3.1 で環境 URL を特定した後、Step 3 の検出ゲートが開いている場合に限り、ライブのリレーションシップ検証を有効にして共有バリデーターを再実行します。追加の検出結果を統合してください。
node "${PLUGIN_ROOT}/scripts/validate-permissions-schema.js" --projectRoot "<PROJECT_ROOT>" --validate-dataverse-relationships --envUrl "<envUrl>"
リレーションシップの証拠が必要なローカル実行に限り、この Dataverse に基づくリレーションシップ検証を使用してください。CI やその他のオフライン環境では必須ではありません。また、リレーションシップに依存する到達可能なフローがない Global のみ/読み取り専用設計では実行しないでください。
2.5 決定論的な事前クロスチェック(採点者の正解データ)
解析済みの YAML、データモデル、Web API のアーティファクトを機械的に照合します。該当する項目をすべて出力してください。ID はプレフィックスごとに、出現順で番号を付けます(IS1、IS2、DM1、…)。これらは正解データです。Step 5 では、適切なディメンションの issue リストにそのまま引き継ぎ、削除したり重要度を下げたりしないでください。
| 条件 | ID プレフィックス | ディメンション | 重要度 |
|---|---|---|---|
| Web ロール YAML の解析エラー | IS | internal-consistency | major |
| テーブル権限 YAML の解析エラー | IS | internal-consistency | major |
| どのロールにも定義されていない Web ロール GUID を権限が参照している | IS | internal-consistency | major |
| Web ロールに空の idがある | IS | internal-consistency | major |
権限の entitylogicalname が空である | DM | data-model-alignment | major |
| 権限が権限を一切付与していない(CRUD の 6 ビットすべてが false) | TC | table-coverage | major |
権限にスコープがない(scope < 0) | SC | scope-correctness | major |
権限のスコープコードが認識できない(Unknown(code)) | SC | scope-correctness | minor |
| 権限が Anonymous ロールに対して書き込み/作成/削除を許可している | AH | anonymous-access-hygiene | major |
| 権限がデータモデルに存在しないテーブルを対象としている(有効なデータモデルマニフェストがある場合のみ) | DM | data-model-alignment | major |
| Web API サイト設定 YAML の解析エラー | IS | internal-consistency | major |
対応する -fields ホワイトリストがない -enabled エンティティセット | IS | internal-consistency | major |
対応する -enabled 設定がない -fields ホワイトリスト | IS | internal-consistency | major |
| データモデルに存在しないプロパティを 1~6 個宣言している型ファイル(有効なデータモデルマニフェストがある場合のみ) | DM | data-model-alignment | minor |
2.6 エビデンス台帳を確定する
<OUTPUT_DIR> は、Web サイト(powerpages.config.json があるプロジェクトルート)で作業する場合は <PROJECT_ROOT>/docs/、それ以外の場合はシステムの一時ディレクトリです。一時ファイルと最終レポートはすべてここに保存します。
判断に基づくチェックを実行する前に、これまでに収集した正規化済みの事実(ロール、権限、データモデルのテーブル/列、サービス操作、ルート/コンポーネントの到達可能性、機能マトリックス、ルックアップ/展開の使用状況、クライアント側の行フィルター、実装計画の主張、認証の到達可能性、事前チェックの issue)を含む一時ファイル <OUTPUT_DIR>/audit-evidence.json を書き出します。テーブル、権限、ルート、機能は、安定した識別子でソートしてください。
Step 4~6 では、証拠を再スキャンして選択的に再発見するのではなく、この台帳を根拠として推論しなければなりません。Step 3 でライブの Dataverse 検出により事実が追加された場合は、その事実を追加し、Step 4 の前に台帳を一度書き直してください。台帳に新たな証拠が追加されない限り、採点中に reachability の分類を変更しないでください。最終 HTML レポートが検証に合格したら、audit-evidence.json を削除してください。
Step 3:分析と検出(Dataverse API)
すべての Dataverse API 呼び出しには、決定論的な Node.js スクリプトを使用してください。これらのスクリプトは、認証トークンの取得、HTTP リクエスト、JSON 解析を一貫して処理します。
Dataverse の検出ゲート
pac env who または Dataverse クエリスクリプトを呼び出す前に、needsLiveDiscovery を計算します。到達可能な機能のうち、次の未解決の依存関係が 1 つ以上ある場合に限り、true になります。
- 関係名の検証が必要な Contact、Account、または Parent スコープ
- ルックアップをバインドまたは変更する作成/書き込みコード
- ローカルメタデータから対象を解決できない
$expandまたは関連テーブルへのアクセス - 関係または対象メタデータが未解決のファイル/画像のアップロード
該当する依存関係がない場合は false です。たとえば、Global のみを対象とする読み取り専用のフロー、到達可能なサイトコードで使用されていない明示的な Administrator 専用 CRUD、マニフェストとローカルソースですべて解決できるケースなどです。false の場合は Step 3 をすべてスキップし、証拠台帳に liveDiscovery: "not-needed" を記録して Step 4 に進みます。true の場合は、まずアクティブな Dataverse 環境の URL がマニフェストの環境 URL と一致することを確認します。一致しない場合は、ライブクエリをスキップし、誤った環境に対してクエリを実行するのではなく、不一致を記録します。
3.1 環境 URL の取得
pac env who
Environment URL(例:https://org12345.crm.dynamics.com)を抽出し、後続のスクリプト呼び出しで <envUrl> として使用します。
3.2 ルックアップ列のクエリ
create または write が有効なアクセス許可を持つ各テーブルについて、ルックアップクエリスクリプトを使用します。
node "${PLUGIN_ROOT}/skills/audit-permissions/scripts/query-table-lookups.js" --envUrl "<envUrl>" --table "<table_logical_name>"
このスクリプトは、各ルックアップ列について { logicalName, targets } の JSON 配列を返します。以下で説明するマップの作成に、この出力を記録してください。
create または write のアクセス許可を持つすべてのテーブルへのクエリが完了したら、結果をまとめて次の 2 つのマップを作成します。
- ソースマップ(テーブル → ルックアップ列):クエリ対象の各テーブルについて、ルックアップ列とその対象を記録します。セクション H2 でソーステーブルの
appendtoを確認するために使用します。 - 逆引きターゲットマップ(対象テーブル → ソーステーブルのリスト):いずれかのルックアップの
targets配列に含まれる各対象テーブルについて、それを参照するソーステーブルを記録します。セクション H で対象テーブルのappendを確認するために使用します。
例:order_item をクエリすると、[{ logicalName: "cr4fc_orderid", targets: ["cr4fc_order"] }] が返されます。
- ソースマップ:
order_item → [{ column: "cr4fc_orderid", targets: ["cr4fc_order"] }] - 逆引きターゲットマップ:
cr4fc_order → [{ sourceTable: "order_item", column: "cr4fc_orderid" }]
両方のマップをセクション H と H2 で使用します。
- ソーステーブル(ルックアップを持つテーブル)には
appendto: trueが必要です。これは他のレコードへのリンクを作成するためです(ソースマップで確認します)。 targetsに含まれる各対象テーブルにはappend: trueが必要です。他のレコードからリンクされるためです(逆引きターゲットマップで確認します)。
3.3 リレーションシップのクエリ
親スコープのアクセス許可を持つテーブルについて、リレーションシップクエリスクリプトを使用してリレーションシップ名を検証します。
node "${PLUGIN_ROOT}/skills/audit-permissions/scripts/query-table-relationships.js" --envUrl "<envUrl>" --table "<parent_table>"
このスクリプトは、{ schemaName, referencedEntity, referencingEntity, referencingAttribute } の JSON 配列を返します。schemaName を使用して、親スコープのアクセス許可にある parentrelationship の値を検証します。
エラー処理
いずれかのスクリプトがコード 1 で終了した場合は、API に依存するチェックをスキップし、レポートにスキップしたチェックを記載します。認証エラーが発生しても、監査全体を中止してはいけません。フォールバックとして、データモデルマニフェストとコード分析を使用します。
Step 4:監査チェックの実行
テーブルごとのタスク追跡を使用して、すべての監査チェックを体系的に実行します。コードと Dataverse に基づくこれらのチェックが、主要な問題検出手段です。ルーブリックによる判断だけでは見落とされる具体的な問題(CRUD の不足、append/appendto、$expand の不足、壊れた親チェーン、過剰な権限付与など)を検出します。これにより、問題の範囲が広がり、スコアの忠実度も高まります。各チェックに失敗すると、暫定候補(タイトル、担当テーブル、証拠、推奨される修正)を 1 件出力します。Step 5 で最終的な重大度(major/minor)とディメンションを割り当てます。チェックに合格した場合、候補は出力されません。サービスコードを検索するよう指示されたチェックでは、証拠台帳(Step 2.6)で該当する事実を確認します。台帳にない場合に限り、ソースを検索してください。
4.1 監査対象テーブルの一覧作成
まず、次の2つのソースから監査対象となるすべてのテーブルを統合した一覧を作成します。
- コード内で参照されているテーブル(ステップ2.3より)— 権限が設定されている場合と、されていない場合があります
- 既存の権限が設定されているテーブル(ステップ2.2より)— コード内で参照されている場合と、されていない場合があります
この2つの集合の和集合が、監査対象の全範囲です。各テーブルについて、両方の観点から監査します。「コードで必要な権限が存在しないか」と「権限がコードの実際の動作と一致しているか」を確認します。
4.2 テーブルごとの監査タスクの作成
監査対象一覧の各テーブルについて、タスクを作成します。
TaskCreate:
subject: "Audit <table_logical_name>"
activeForm: "Auditing <table_display_name> permissions"
description: "Run all audit checks for <table_logical_name>"
また、サマリータスクも作成します。
TaskCreate:
subject: "Compile audit findings"
activeForm: "Compiling audit findings"
description: "Combine all per-table findings into the final report"
TaskList はいつでも使用して進捗を確認し、監査が必要なテーブルを把握できます。
4.3 テーブルごとの監査チェックリスト
各テーブルについて、そのタスクを in_progress にして、以下のチェックを順番に実行します。問題があれば、その根拠となる具体的な証拠(ファイルパス、権限名、コードパターン)を記録します。このテーブルに該当しないチェックはスキップします。
ステップ4では、重大度やディメンションを確定しないでください。 以下の各チェックには暫定監査レベル(critical、warning、info)が設定されており、事実に基づく候補を { sourceId, auditTier, title, table, reasoning, fix, evidence, reachability? } として出力します。チェックに合格した場合、候補は出力されません。最終的な major|minor の重大度と標準ディメンションを割り当てるのはステップ5のみです。固定カタログのルールを一般ポリシーより先に適用します。これにより、先に実行された監査パスによって異なるスコアが確定するのを防ぎます。
A. 権限の有無
このテーブルにはテーブル権限が設定されていますか?
- テーブルがコード内で参照されているものの、権限がない場合 → 指摘事項:
- 重大度:
critical - タイトル:
<table>の権限がありません - 理由: このテーブルを参照するコードファイルと、そのファイルが実行する操作
- 修正方法: 適切なスコープとCRUDフラグを指定して権限を作成します
- 重大度:
- 権限が存在するものの、テーブルがコード内で参照されていない場合 → 指摘事項:
- 重大度:
info - タイトル:
<table>の未使用の権限 - 理由: このテーブルはソースコード内で参照されていないため、この権限は不要な可能性があります
- 修正方法: この権限が引き続き必要かどうかを確認します
- 重大度:
- 両方が存在する場合 →
passとして、残りのチェックに進みます
B. Web ロールとの関連付け
この権限には Web ロールが割り当てられていますか?
adx_entitypermission_webroleを確認します。空または存在しない場合 → 指摘事項:- 重大度:
warning - タイトル: 権限
<name>に Web ロールが関連付けられていません - 理由: Web ロールが関連付けられていない権限は効果がなく、アクセス権を得るユーザーはいません
- 修正方法: 適切な Web ロールに関連付けます
- 重大度:
- 例外:Parent スコープ(
756150003)の子権限では、ロールの関連付けを空にしてもかまいません。親チェーンを通じてロールの関連付けを継承するためです。parententitypermissionが既存の権限を参照していれば、指摘事項を出しません。チェーンが壊れている場合は、チェックIで報告します。 - ロールが割り当てられている場合 →
pass
C. スコープの適切性
要件を満たす、最小権限のスコープになっていますか?
- サービスコード内でスコープに関連するパターンを検索します: 連絡先スコープのフィルター(
getCurrentContactId、_contactid_value、contactid)とアカウントスコープのフィルター(_accountid_value、parentcustomerid) - 各グローバル読み取り権限について、割り当てられたすべてのロールを解決し、ステップ 2.3 の対象者到達可能性プロファイルを適用します。次に、テーブルの選択済み/許可リスト済みの列、データモデルの列、UI の目的、実装計画を調査して、そのデータが明示的に公開されているのか、より限定されたペルソナ向けなのかを判断します。
- 成果物横断の必須トリガー。影響を受けるテーブルごとに個別に評価します: 次の 4 つの条件がすべて成立する場合に所見を出力します: (1) オープン登録が証明されている(ステップ 2.3 の対象者到達可能性プロファイル)、(2) 割り当てられた Web ロールに
authenticatedusersrole: trueが設定されており、登録されたすべてのアカウントがそれを受け取る、(3) そのロールがこのテーブルの権限にread: trueおよびグローバルスコープで関連付けられている、(4) テーブルに個人単位、名簿、成績、連絡先、または内部運用データが含まれている、もしくはサイトの意図がスタッフ/教師/別の目的特化型ペルソナに限定している → 所見:- 重大度:
critical - タイトル:
自己登録ユーザーがすべての <table> レコードを読み取ることができます - 根拠: 完全な証拠チェーンを記載します: 登録設定 → 自動的な Authenticated Users メンバーシップ → 権限ロールのバインド → グローバルスコープ → 公開される列/想定対象者。認証だけでは信頼されたペルソナのメンバーシップは確立されません。
- 修正: この権限からベースラインの Authenticated Users ロールを削除し、検証済みユーザーのみにプロビジョニングされる目的特化型ロールを割り当てます。アクセスがレコード相対である場合は、グローバルではなく Contact、Account、Parent、または Self スコープを使用します。
- 重大度:
- グローバルスコープ(
756150000)でwriteまたはdeleteが有効な場合 → 所見:- 重大度:
warning - タイトル:
<table> に対する write/delete を伴うグローバルスコープ - 根拠: このロールを持つ任意のユーザーが、このテーブル内の任意のレコードを変更/削除できます
- 修正: Contact または Account スコープに絞り込むか、不要であれば write/delete を削除します
- 重大度:
- グローバルスコープに
readのみがある場合は、テーブルと公開される列が明示的に公開/非機密であり、割り当てられたすべてのロールが想定対象者である場合にのみ合格とします。それ以外の場合は、上記の必須トリガーおよび再発する問題 C3/C4 の下で評価します。読み取り専用アクセスでも重大な機密性の露出となり得ます。 - コードが連絡先スコープのフィルターを使用しているのに権限がグローバルを使用している場合 → 所見:
- 重大度:
warning - タイトル:
<table> のスコープをより狭くできます - 根拠: コードは現在の連絡先でフィルターしていますが、権限はグローバルアクセスを付与しています
- 修正: Contact スコープに絞り込みます
- 重大度:
- それ以外 →
pass
D. 読み取り権限
read は正しく設定されていますか?
- サービスコード内でこのテーブルの GET/list/get パターンを検索します:
/_api/<entity_set>への API 呼び出し、list/get 関数(list<TableName>、get<TableName>) - コードがこのテーブルを読み取っているのに
read: falseの場合 → 所見:- 重大度:
critical - タイトル:
<table> の読み取り権限がありません - 根拠: コードはこのテーブルから読み取っていますが、権限が読み取りアクセスを付与していません
- 修正:
read: trueを有効にします
- 重大度:
read: trueでコードが読み取っている場合 →pass
E. 作成権限
create は正しく設定されていますか?
- サービスコード内で POST/create パターンを検索します: POST メソッドの使用(
method: 'POST')、create 関数(create<TableName>) - コードがレコードを作成しているのに
create: falseの場合 → 所見:- 重大度:
critical - タイトル:
<table> の作成権限がありません - 根拠: コードはこのテーブルにレコードを作成していますが、権限が作成アクセスを付与していません
- 修正:
create: trueを有効にします
- 重大度:
create: trueなのにコードに create パターンがない場合 → 所見:- 重大度:
info - タイトル:
<table> で作成が有効ですが使用されていません - 根拠: コード内に作成操作が見つかりません — 権限が過度に許可的である可能性があります
- 修正: 不要であれば
createを無効化することを検討します
- 重大度:
- 一致した場合 →
pass
F. 書き込み権限
write は正しく設定されていますか?
- サービスコード内で PATCH/update/upload のパターンを検索します: PATCH メソッドの使用 (
method: 'PATCH')、更新関数 (update<TableName>)、ファイルアップロードのパターン (uploadFileColumn、uploadFile、upload*Photo、upload*Image、upload*File) - コードがレコードを更新しているのに
write: falseの場合 → 検出結果:- Severity:
critical - Title:
Missing write permission for <table> - Reasoning: コードはこのテーブルのレコードを更新 (またはファイルをアップロード) していますが、権限に書き込みアクセスが付与されていません
- Fix:
write: trueを有効にしてください
- Severity:
- ファイルアップロードのパターンが見つかったのに
write: falseの場合 → 検出結果:- Severity:
warning - Title:
File upload detected but write is disabled on <table> - Reasoning: ファイルアップロードは書き込み権限を必要とする PATCH を使用します
- Fix:
write: trueを有効にしてください
- Severity:
write: trueなのにread: falseの場合 → 検出結果:- Severity:
warning - Title:
Write enabled without read on <table> - Reasoning: ユーザーは閲覧できないレコードを変更できる状態であり、これは通常とは異なり、意図しない設定である可能性が高いです
- Fix:
read: trueを有効にしてください
- Severity:
write: trueなのにコード内に書き込みパターンがない場合 → 検出結果:- Severity:
info - Title:
Write enabled but not used for <table> - Reasoning: コード内に更新操作が見つかりません — 権限が過剰に許可されている可能性があります
- Fix: 不要であれば
writeを無効にすることを検討してください
- Severity:
- 一致した場合 →
pass
G. 削除権限
delete は正しく設定されていますか?
- サービスコード内で DELETE のパターンを検索します: DELETE メソッドの使用 (
method: 'DELETE')、削除関数 (delete<TableName>) - コードがレコードを削除しているのに
delete: falseの場合 → 検出結果:- Severity:
critical - Title:
Missing delete permission for <table> - Reasoning: コードはこのテーブルのレコードを削除していますが、権限に削除アクセスが付与されていません
- Fix:
delete: trueを有効にしてください
- Severity:
delete: trueなのにコード内に削除パターンがない場合 → 検出結果:- Severity:
info - Title:
Delete enabled but not used for <table> - Reasoning: コード内に削除操作が見つかりません — 権限が過剰に許可されている可能性があります
- Fix: 不要であれば
deleteを無効にすることを検討してください
- Severity:
- 一致した場合 →
pass
H. Append (対象テーブルの確認)
このテーブルに append: true は必要ですか? Append は 対象 テーブル — 他のレコードがルックアップ列を介してリンク 先 とするテーブル — に必要です。
- 手順 3.2 の 逆対象マップ を確認します: このテーブルは、
createまたはwrite権限を持つ他のテーブルからルックアップ対象として参照されていますか? - また、サービスコード内でこのテーブルのエンティティセットへの
@odata.bind参照 (例:/<entity_set>() を検索します - このテーブルが逆対象マップに含まれている (つまり、create/write を持つ別のテーブルがこのテーブルへのルックアップを持っている) のに
append: falseの場合 → 検出結果:- Severity:
critical - Title:
Missing append on <table> - Reasoning: テーブル
<source_table>はこのテーブルを対象とするルックアップ列<column>を持ち、create/write 時にそれを設定しています。対象テーブルには append 権限が必要で、これによりレコードをリンクできます。ユーザーには「関連付けまたは関連付け解除を行う権限がありません」と表示されます - Fix:
append: trueを有効にしてください
- Severity:
append: trueで妥当な場合 →passappend: trueなのにこのテーブルが逆対象マップに含まれておらず、コード内でもルックアップ対象として参照されていない場合 → 検出結果:- Severity:
info - Title:
Append enabled but not needed on <table> - Reasoning: create/write を持つ他のテーブルがこのテーブルへのルックアップを持っていません — append は不要である可能性があります
- Fix: 不要であれば
appendを無効にすることを検討してください
- Severity:
H2. AppendTo (ソーステーブルの確認)
このテーブルに appendto: true は必要ですか? AppendTo は ソース テーブル — 他のレコードへリンクするルックアップ列を持つテーブル — に必要です。
- ステップ3.2のソースマップを確認してください。このテーブルにルックアップ列はありますか?
- また、このテーブルの create/update 呼び出しで
@odata.bindパターンがサービスコードにないか検索してください - このテーブルにルックアップ列があり(ソースマップまたはコード内)、かつ
createまたはwriteが有効になっているのにappendto: falseの場合 → 指摘:- 重大度:
critical - タイトル:
<table> に appendto がありません - 理由: このテーブルは create/write 時に
<target_table>を対象とするルックアップ列<column>を設定しており、これには appendto 権限が必要です。ユーザーには「関連付けまたは関連付け解除の権限がありません」と表示されます - 修正:
appendto: trueを有効にしてください
- 重大度:
appendto: trueで正当化される場合 →pass
I. 親チェーンの整合性
権限に Parent スコープ(756150003)がある場合:
parententitypermissionが存在する有効な権限 ID を参照していることを確認してくださいparentrelationshipが有効な Dataverse リレーションシップであることを確認してください(API が利用可能な場合は、ステップ3.3の結果を使用)- 壊れている場合 → 指摘:
- 重大度:
critical - タイトル:
<permission> の親チェーンが壊れています - 理由: 親権限の参照が無効です — この権限はアクセスを一切許可しません
- 修正: 親権限 ID またはリレーションシップ名(あるいはその両方)を修正してください
- 重大度:
- 有効な場合 →
pass
J. $expand 関連テーブルのカバレッジ
このテーブルは別のテーブルのクエリで $expand を介して取得されていますか?
- ステップ2.3の
$expand分析を確認してください(サイトのソースコードで$expand、buildExpandClause、ExpandOptionを検索) - このテーブルが別のテーブルから展開されているのに、同じ Web ロールに対して
read: trueのテーブル権限がない場合 → 指摘:- 重大度:
critical - タイトル: 展開されたテーブル
<table> の read 権限がありません - 理由: このテーブルは
<service_file>内の<parent_table>で$expandを介して取得されていますが、read 権限がありません。Power Pages はクエリ内のすべてのエンティティにテーブル権限を適用します。 - 修正: 同じ Web ロールに対して
read: trueのテーブル権限を作成してください。コレクション値の展開(1対多)では、リレーションシップ名を指定した Parent スコープを使用します。単一値の展開(参照データへのルックアップ)では、読み取り専用アクセスを指定した Global スコープを使用します。
- 重大度:
- 適切にカバーされている場合 →
pass
K. 指摘の記録と完了
すべてのチェック後、TaskUpdate を介してテーブルのタスクを completed としてマークしてください。
4.4 テーブル間の検証
テーブルごとのすべての監査が完了したら、次のテーブル間チェックを実行します:
- Append/AppendTo の一貫性: ステップ3.2のソースマップと逆ターゲットマップを使用して、次を確認します: (a) すべてのソーステーブル(ルックアップと create/write を持つ)が
appendto: trueを持っていること、(b) 逆マップ内のすべてのターゲットテーブルがappend: trueを持っていること、(c) ソースマップにルックアップ列がないのにappendto: trueを持つテーブルがないこと、(d) 逆ターゲットマップに含まれていないのにappend: trueを持つテーブルがないこと - $expand カバレッジ: すべての
$expandの使用について、展開されるテーブルがread: trueを持っていることを確認してください - 親チェーンの完全性: すべての Parent スコープ権限について、親権限が存在し有効であることを確認してください
- Web ロールの一貫性: 2つの関連テーブル(例: 親と子)が同じ機能からアクセスされる場合、それらが同じ Web ロールの割り当てを共有していることを確認してください
TaskList を使用してすべての完了した監査を確認し、次に「Compile audit findings」タスクを in_progress としてマークし、ステップ5に進みます。
Step 5: 設定を採点する
これは採点オーバーレイです。根拠となる情報はすでに揃っています。監査の問題(Step 4)と、決定論的な事前チェックの問題(Step 2.5)です。これらをカテゴリ別に整理された問題セットと、再現可能な1~5のスコアにまとめます。検出された問題の大半は監査チェックで洗い出されます。以下の頻出問題カタログと採点者の重大度ポリシー(Appendix C)で major/minor を確定し、計算式(Appendix A)で数値を決定します。検出と採点はCATEGORY(各カテゴリは3つのディメンションを持ちます。Appendix A/Appendix B)ごとに整理されます。
5.1 カテゴリ別に問題を検出してラベル付けする(カタログに基づく)
3つのカテゴリを評価し、それぞれのカテゴリ内では3つのディメンションを総合的に検討します。各問題は、次の3つの情報源から抽出します。
- 事前チェックの問題(Step 2.5) — 固定の ID、ディメンション、重大度をそのまま保持します。
- 監査候補(Step 4、および権限がないという Step 1 の観察結果) — これらは事実の観察結果であり、最終的なラベルではありません。まず各候補を頻出問題カタログと照合します。一致するものがない候補に限り、一般的な Appendix C のポリシーを適用します。Step 5 では、すべての最終的な重大度とディメンションをそれぞれ一度だけ割り当てます。
- 頻出問題カタログと評価基準に基づく判断 — 以下のカタログをチェックリストとして適用し、A~Kのチェックでまだカバーされていない項目(例:読み取り専用の管理サイト全体 → 意図の網羅性の失敗)を追加します。
頻出問題カタログ — 検出+重大度(実行ごとの一貫性を高める主要な仕組み)。 各パターンを成果物に照らして確認します。パターンのトリガー条件を満たす場合は、記載されたディメンションに、固定の重大度で、そのパターンにつき問題を1つだけ必ず追加します。どれほど明白、または軽微に見えても、実行のたびに同じように扱います。特定のテーブル名やロール名ではなく、説明されている形状で照合します(括弧内の名前は例示です)。パターンは影響を受けるテーブルグループごとに評価し、相互排他的ではありません。あるテーブルが重大なパターンに該当し、別のテーブルが類似した軽微なパターンに該当する場合(例:PIIテーブルに対するC2と、下書きコンテンツテーブルに対するC14 minor)は、別々の問題として両方を追加します。順序によって重大度を解決するのは同じテーブルグループ内のみであり、別のテーブルに関する指摘を抑制することはありません。
- [C1] 匿名での書き込み、作成、または削除 — ステップ 2.5 の事前チェックの
AH行で報告します。ここでは再度報告しないでください。 - [C2] 匿名ロールが読み取り可能な個人情報/連絡先の列(メールアドレス、電話番号、保護者または親の連絡先、住所) → ディメンション
anonymous-access-hygiene· major。 - [C3] ロールが、個人単位/所有者単位の個人情報または機密性の高い運用データ(個人記録、連絡先の個人情報、成績、名簿)を含むテーブルに GLOBAL スコープでアクセスし、すべてのレコードに到達できる — 変更可能な許可 →
privilege-calibration、読み取り専用 →security-posture· major。これには、ベースラインの Authenticated Users ロールも明示的に含まれます。オープン登録によって訪問者が誰でもこのロールを取得できる場合、信頼されたスタッフではなく、広範な外部オーディエンスとして扱ってください。影響を受けるテーブルごとにこのパターンを個別に評価して報告してください。Student と Teacher(またはその他の互いに重複しないテーブルの露出)を1つの問題にまとめないでください。 - [C4] ロールに、本来必要な知識/操作の範囲を超えるアクセス(CRUD のいずれか)が付与されている → ディメンション
privilege-calibration· 追加アクセスが別のオーディエンスの機密レコード(個人情報、機密情報、財務情報)に及ぶ場合に限り major、それ以外は minor。C3、C9、C16、C17、C18 のいずれかに該当するケースには、C4 は適用されません。 - [C5] サイトで作成/管理が必要なのに、どのロールにもいずれのテーブルに対する Create/Write/Delete 権限もない(完全に読み取り専用)— サイト全体で1回だけ報告します → ディメンション
intent-coverage· major (意図的に除外されたAdministratorsロールが作成を行う想定の場合は報告しないでください — 下記の ADMINISTRATORS EXCEPTION を参照)。 - [C6] サイトで明らかに公開が必要なテーブル(サービス/UI コードで参照されている、または明らかなエンドユーザーエンティティ)に、必要なアクセスを許可する権限がない → ディメンション
table-coverage· major (不足している許可がAdministratorsロールに対するものだけの場合は報告しないでください — ADMINISTRATORS EXCEPTION を参照)。 - [C7] サイトで目的別のペルソナ(例:学生/教師/スタッフ/管理者の個別オーディエンス)が必要なのに、汎用の Anonymous/Authenticated ロールしかない → ディメンション
role-completeness· major (Administratorsロール/ペルソナの不足は不備ではありません — ADMINISTRATORS EXCEPTION を参照)。 - [C8] Web ロールが定義されているものの、どのテーブル権限からも参照されておらず(孤立している)、サイトで必要なすべてのペルソナは別のロールでカバーされている → ディメンション
role-completeness· minor。 - [C9] 本質的に公開されている/機密性のない共有参照データ(お知らせ、施設、公開されている教職員/幹部の一覧)の GLOBAL スコープでの読み取りは、適切なスコープです → 問題なし。公開オーディエンスをインベントリ/コンテキストに記録しますが、セキュリティスコアを下げたり、修正を求めたりしないでください。
- [C10] 仮定上の将来の変更があった場合にのみ意味を持つスコープ/許可(「保護者が後から認証する場合は、Contact または Parent スコープのほうが適切」など) → ディメンション
scope-correctness· minor。 - [C11]
permissions-plan.htmlが存在しない、または「Global は最も広いスコープです」のような汎用的/定型的な計画しかない → ディメンションinternal-consistency· minor。 - [C12] データモデルのテーブルに権限がなく、ユーザー向けかどうかが本当に不明確 — 実装計画またはデータモデルではユーザー向けと説明されているものの、コードでは使われていない(おそらく計画されたものの実装されなかった機能) → ディメンション
table-coverage· minor。明らかにバックエンド/システム用のテーブルに権限がない場合は適切なため、報告しないでください。 - [C13] ワイルドカード
*による列の露出 —$select=*を指定する Web API クエリ、または列リストが*に設定されているケース → ディメンションsecurity-posture· major。影響を受けるテーブルごとに1件報告します。Webapi/<table>/fieldsのワイルドカードは、代わりにステップ 2.4 のバリデーターの指摘として報告します。列を明示的に列挙したリストは、どれほど長くてもこの問題には該当しません。 - [C14] ポータルから読み取り可能なテーブルに、許可された行と制限対象の行が混在しており、分離手段がクライアント制御の
$filter/クエリ述語のみである。クライアント側のフィルタリングは認可境界ではありません。回避によって 機密情報または内密のコンテンツ(個人情報、認証情報/シークレット、財務/健康情報、内部スタッフまたはエージェントのメモ、非公開のケース詳細、セキュリティ情報)が露出する場合 → ディメンションsecurity-posture· major。回避によって、未公開、下書き、またはワークフロー未完了にすぎない機密性のないコンテンツのみが露出する場合 → ディメンションsecurity-posture· minor。Dataverse の権限、列セキュリティ、サーバー側エンドポイント、または別の保護されたテーブルによって分離が強制されている場合、C14 は該当しません。 - [C15] 目的別のペルソナに
reachableなポータル機能が少なくとも1つあるものの、そのペルソナの Web ロールがなく、その機能のテーブル/操作に必要な権限もない → ディメンションrole-completeness· major の問題を、必ず1件にまとめて報告します。その結果として生じるテーブル不足や操作不足の症状は、この問題に含めてください。ペルソナのロールが存在し、カバーされていないテーブルが1つだけの場合は、C6table-coverageを使用します。テーブル権限は存在し、操作ビットだけが不足している場合はintent-coverageを使用します。disabledコードは C15 の対象になりません。計画と矛盾するambiguousコードについては、ステップ 2.3 で説明されているinternal-consistencyの minor 問題のみを報告します。 - [C16] スタッフの共有作業キュー — スタッフ/バックオフィスのペルソナロール(Anonymous でも、ベースラインの Authenticated Users ロールでもない)が、共有作業キューテーブル(ケース、チケット、リクエスト、タスク)またはその子テーブルで Global スコープを持つ。そのロールで到達可能なページが、所有者をまたいでそれらのレコードを読み取りまたは更新する場合 → 問題なし(C3 または C4 を報告しないでください)。到達可能なスタッフページで使われていない Global の書き込み/作成権限 → そのロールの C18 の問題に含めます(minor)。到達可能なスタッフページで実行されない Global の削除権限 → ディメンション
privilege-calibration· major。Anonymous または顧客/ベースラインロールによる変更は、引き続き C1/C3 の major に該当します。 - [C17] 子テーブルの同等性 — 子テーブルの権限(コメント、添付ファイル、メモ、エスカレーション)の到達範囲が、同じロールの親テーブルへの許可と一致する(たとえば、Global の親の下にある Global の子、またはその下にある Parent スコープ) → 問題なし。子テーブルへの許可が、そのロールの親テーブルへの許可より広い場合 → C3 を適用します(子テーブルに機密性の高いコンテンツがある場合は major)。
- [C18] 未使用の許可 — ロールの到達可能なコードが使用しない権限(未使用のテーブル、未使用の作成/書き込み/削除ビット、不要な append/appendto の権限)。明示的な Administrators の権限も含みます → ロールごとに1件だけ、ディメンション
privilege-calibration· minor の問題を報告し、未使用の許可をすべて列挙します。テーブルごとに分割しないでください。C16 の未使用の削除権限は、別個の major の問題として扱います。 Administrators の例外(厳守してください)。 設計上、サイトでAdministratorsWeb ロールにテーブル権限を設定してはなりません。通常、管理者による作成・編集はポータルの外(モデル駆動型アプリまたは Dataverse の直接操作)で行われるため、Administrators に明示的な CRUD 権限を付与する必要はなく、セキュリティ上のノイズが増えるだけです。AdministratorsWeb ロールは、ポータルのデータアクセスにおいてテーブル権限を迂回しません。したがって、次のとおりです。 - 管理作業を行う
reachableなサイトコードが存在しない場合、Administrators にテーブル権限がないことは正しい状態です。Administratorsロールにテーブル権限がないことだけを理由に、不足している権限、テーブルカバレッジ、ロールの完全性、または完全な読み取り専用に関する問題を、決して指摘してはなりません。これは、意図的に除外されたAdministratorsロールが作成・編集を担うためにのみ生じる、権限不足のギャップ(C5 の読み取り専用サイト、C6 の権限付与漏れ、C7 のペルソナ欠落)にも当てはまります。 reachableなサイトコードがポータルを通じて管理者専用の CRUD(たとえば、ルーティングされた管理者ページから Web API を呼び出す処理)を、権限付与なしで実行する場合、この例外は適用されません。他のロールと同様に、C6 または C15 を指摘してください。- この例外が適用されるのは、
Administratorsロールに対する権限不足やユーザビリティのギャップに限られます。権限過剰(セキュリティ)に関する検出結果の重大度を下げることは決してありません。Administrators(または他のロール)に実際に過剰な権限付与がある場合は、通常どおりセキュリティルールに基づいて評価してください。
新規の問題(C1~C18 でカバーされていないもの): 引き続き検出してください。カタログの項目だけに限定してはなりません。重大度は、付録 C のセキュリティ優先ポリシーに基づいて割り当ててください。新規の問題が自動的に軽微になるわけではありません。誤った対象者にデータを公開する、アクセス範囲を過剰に広げる、または必須のフローを妨げる場合は、重大です。
LLM によって導出された問題 ID は、正規のディメンション接頭辞(IC、PC、SC、RC、TC、AH、DM、IS、SP)ごとに番号を付けてください。各ディメンションでは、その接頭辞ですでに使用された最も大きい事前チェック ID の次から続けてください(存在しない場合は 1 から開始してください)。これにより、生成される ID が固定の事前チェック ID と重複しなくなります。問題 ID の接頭辞は、統合後の最終的なディメンションと一致させる必要があります。各問題は、報告する検出結果(ステップ 7.2)の形式に従い、裏付けとなる証拠とともに {id, dimension, severity, title, reasoning, fix} として記録してください。ディメンションごとの 1~5 のスコアは割り当てないでください。 スコアは統合後にカテゴリレベルで算出します。
5.2 明確な根本原因に統合する(重大/軽微)
統合前の課題リスト全体を明確な根本原因にまとめ、それぞれを主な影響に基づいて、必ずいずれか1つのカテゴリに分類します。このステップ後の課題数は、根本原因の数と同じになります。
- 統合: 同じテーブルについて、同じ対象者に影響する同一の根本的な設定ミスを説明している課題は、ディメンションやカテゴリが異なっていても、1つにまとめます。広範な説明(「すべてのテーブルが Global」など)で、テーブルごとの個別の露出をまとめてはいけません。C3の課題は、影響を受けるテーブルごとに1つのままにします。説明には影響をすべて列挙し、統合後の重大度は、統合対象の中で最も高いものにします。
- 根本原因ごとに1つのカテゴリを選択 ― 主な影響に基づく: 過剰な露出 = アクセスが広すぎる(誤った対象者へのデータ露出、匿名での変更、広すぎる範囲で閲覧可能なPII、所有者ごとのデータに Global が設定されている)。露出不足 = アクセスが狭すぎる(必要なCRUDフローがない、ユーザー向けテーブルに権限がない、ペルソナ/ロールが不足している)。正確性 = 構造上の問題(モデルに存在しないテーブルへの参照、切れたロールリンク、誤った/無効なスコープ、設計ドキュメントの欠落)。根本原因が2つのカテゴリにまたがる可能性がある場合は、現実世界での最悪の結果を選びます。統合した課題の
dimensionは、選択したカテゴリ内のものに再割り当てします。 - 露出不足のディメンションを選ぶ際のタイブレーク(最初に一致したものを優先): 1つ以上の到達可能な機能にまたがるペルソナロールの欠落 →
role-completeness。ペルソナロールは存在するが、テーブル権限がない →table-coverage。ロールとテーブル権限はあるが、CRUD/追加のビットがない →intent-coverage。説明文で強調されている内容を基準に、これらのディメンションを選んではいけません。 - 計画とコードが競合する場合のタイブレーク:
reachableの機能では実装を優先し、該当するC6/C7/C15の課題を重大のまま残します。disabledの機能では計画を優先し、露出不足の課題は挙げません。ambiguousの機能では、軽微なinternal-consistencyの競合のみを挙げます。 - 異なるテーブルに対する別々の露出を統合しないでください。 テーブルの集合が重ならない、または対象者/重大度が異なる2つの露出/PIIの課題は、別々の根本原因です。両方を残します(例:学生/教師テーブルの匿名PII(重大)と、公開のお知らせ/施設に対する過度に広範な Global の読み取り(軽微))。同じテーブルの場合にのみ統合します。判断に迷ったら、分けたままにします。C16とC18は、定義上、ロールごとに1つの課題です。
- すべての事前検査の課題(Step 2.5)を、それぞれ固定のIDと重大度を持つ独立した課題として保持します。 監査/ルーブリックの課題が事前検査の課題と重複する場合は、事前検査の課題に統合します(事前検査のIDを保持し、重大度は2つのうち高い方にします)。
- 新しい問題を作り出してはいけません。統合後の各課題は、少なくとも1つの元の課題にたどれる必要があります。件数を減らすために、重大な課題を軽微に変更してはいけません。
統合した課題が、スコアリングおよび後続のすべてのフェーズに使われます。各課題を、再割り当てされた可能性のあるディメンションの下に再分類してください。統合後のこの major/minor リストが、後続のすべての処理で使用する唯一の課題セットです。 レポートの FINDINGS_DATA(Step 7.2)、OverviewのMajor/Minor件数、スコアの入力値になります。課題セットも集計も1つだけです。課題が多いほどスコアは低くなります。
5.3 スコアを計算する(純粋な数式)
Appendix A を正確に適用します。統合前の課題をまとめた結果に基づき、カテゴリごとに1~5のスコアを1つ算出します(総合スコアはありません)。判定は重大な課題の件数に基づき、課題数 { major, minor, total } は統合後の全課題を対象にします。
5.4 判定(文章)
2~3 文で評価者の判定を書きます。設定で最も適切な点、最も問題のある点、そして判定を述べます。判定は、重大な問題がゼロの場合は "Safe to go"、重大な問題が1 件以上ある場合は "Needs revision" と、一字一句そのまま記載します。軽微な問題は判定に影響しません。デプロイ、リリース、出荷の可否をユーザーに伝えてはいけません。判定は調査結果を要約するものであり、決定するのはユーザーです。文章のみを使用してください。レポートの SUMMARY として使用します(ステップ 7.2)。
データモデルマニフェストがない(SCORECARD_DATA が null)場合は、判定を省略します。これは部分的な監査であることを述べる 2~3 文の要約を記載し、どちらの判定フレーズも使用しないでください。
ステップ 6:根本原因の特定(一括)
(統合後、軽微な問題を除き)すべての重大な問題を 1 回の一括処理で分析します。サイトの証拠は共通しているため、すべての重大な問題をまとめて、1 つの推論ステップで分類します。それぞれについて、純粋に権限設計上の問題なのか、それとも上流の成果物が全体または一部の原因なのかを判断します。慎重に判断してください。上流が原因である明確な証拠がない限り、permissions を既定値にします。根拠のない上流原因の特定は、原因を見落とすよりも問題です。
根本原因の値:
permissions— 権限設計に実際の問題があります。上流の要因による強制はありません。権限または Web ロールの YAML で修正します。data-model— マニフェストにテーブルまたは列がないか、発行元プレフィックスが誤っています。webapi-code— サイトコード(src/shared/services/*、src/types/*、ページ)がテーブルまたは列を参照する方法によって権限の形が強制されているか、ブラウザー側のルールに依存しています。webapi-settings—Webapi-*-enabled/-fieldsYAML によって許可(または不足)が強制されています。mixed— 上流の原因と権限側の誤りの両方が影響しています。
重大な問題ごとに rootCause を決定し、explanation を記述します。これらはステップ 7.2 で、該当する指摘の rootCause と rootCauseReason になります。
explanation は重大な問題とともにユーザーに表示されるため、ラベルの根拠を示す平易な 1 文を記述します。permissions の場合は、権限設定のみが原因である理由を述べます。それ以外の場合は、具体的な上流ファイルまたは設定名と、それが強制している内容を示します。
ステップ 7:レポートの生成
7.1 出力先の決定
- Web サイトのコンテキストで作業している場合(プロジェクトルートに
powerpages.config.jsonが存在する場合):<PROJECT_ROOT>/docs/permissions-audit.htmlに書き込みます - それ以外の場合:システムの一時ディレクトリに書き込みます
このフォルダーが、ステップ 2.6 で定義した <OUTPUT_DIR> です。
7.2 データを準備する
HTML を手動で生成したり、テンプレートを自分で読み込んだり変更したりしないでください。 render-audit-report.js スクリプトを使用してください。このスクリプトはテンプレートを機械的に読み込み、プレースホルダートークンをデータに置き換えます。
次のキーを含む一時的な JSON データファイル(例:<OUTPUT_DIR>/audit-data.json)を作成します。
{
"SITE_NAME": "The site name (from powerpages.config.json or folder name)",
"AUDIT_DESC": "Security audit of table permissions for Contoso Portal",
"SUMMARY": "2-3 sentence summary of the audit results",
"FINDINGS_DATA": [/* array of finding objects */],
"INVENTORY_DATA": [/* array of current permission objects */],
"SCORECARD_DATA": {/* grader overlay — see below; use null if scoring was skipped */}
}
SITE_NAME、AUDIT_DESC、SUMMARY、FINDINGS_DATA、INVENTORY_DATA は、レンダリングスクリプトが検証する必須キーです。SCORECARD_DATA は拡張テンプレートが使用する追加キーです。プレースホルダーが置き換えられないまま残らないよう、必ず含めてください(オブジェクトまたは null)。SCORECARD_DATA.reportPaths には、HTML レポートの絶対パスを指定してください。ユーザーがレポートを開けるよう、Overview タブに表示されます。
FINDINGS_DATA の形式 — これはステップ 5.2 の統合された重大/軽微な問題リストです(問題ごとに 1 件。スコアの計算に使用されるデータそのものです)。ステップ 5 の正規の問題 ID(例:AH1)を使用してください。重大な問題については、その問題のステップ 6 の項目から rootCause と explanation をコピーします。軽微な問題では、両フィールドを省略します。
{
"id": "AH1",
"severity": "major",
"title": "Staff email/phone readable by Anonymous",
"table": "contoso_staff",
"dimension": "anonymous-access-hygiene",
"scope": "Global",
"permission": "Staff - Public Read",
"reasoning": "Staff email/phone are in the $select and the Webapi field whitelist, and Staff-Public-Read grants the Anonymous role Global read — public internet can read staff PII.",
"fix": "Restrict staff read to a purpose-specific role assigned only to verified users, or drop contoso_email/contoso_phone from the whitelist and $select.",
"details": "Staff-Public-Read.tablepermission.yml; Webapi-contoso_staff-fields.sitesetting.yml; src/shared/services/staffService.ts:85",
"rootCause": "mixed",
"rootCauseReason": "The anonymous Global read grant is a permission mistake, and the Web API fields setting also whitelists contoso_email and contoso_phone."
}
id:ステップ 5 の正規の問題 ID。ステップ 6 でも同じ ID を使用します。severity:majorまたはminorのいずれかです。この 2 段階のみを使用します(critical/warning/info/pass は使用しません)。table:この問題に関連する論理テーブル名(全般的な問題の場合はnull)dimension:問題が集約される評価者の評価軸(Appendix C の対応付けによる)scope:該当する場合は現在のスコープ(数値コードまたはわかりやすい名称)、該当しない場合はnullpermission:既存の権限に関する問題の場合は権限名、該当しない場合はnullreasoning:これが問題である理由の詳細な説明。具体的なコードファイル、行のパターン、または Dataverse のメタデータを参照してください。fix:問題を解決するための、実行可能な提案details:ファイル参照、列名、リレーションシップの詳細など、追加のコンテキストrootCause:重大な問題のみ。ステップ 6 の根本原因(permissions、mixed、data-model、webapi-code、webapi-settings)rootCauseReason:重大な問題のみ。根本原因の理由となるステップ 6 のexplanation(根本原因を正当化する 1 文)
INVENTORY_DATA の形式:
{
"name": "Product - Anonymous Read",
"table": "cra5b_product",
"scope": "Global",
"roles": ["Anonymous Users"],
"read": true,
"create": false,
"write": false,
"delete": false,
"append": true,
"appendto": false
}
SCORECARD_DATA の形式(ステップ 5 から取得。レポートではこれらのフィールドのみを読み込みます):
{
"categories": [
{ "name": "Over-Exposure (Security)", "score": 3.10 },
{ "name": "Under-Exposure (Usability & Coverage)", "score": 4.21 },
{ "name": "Correctness (Validity & Alignment)", "score": 3.48 }
],
"reportPaths": [
{ "label": "HTML report", "path": "<PROJECT_ROOT>/docs/permissions-audit.html" }
]
}
レポートには、このデータが自動的に表示されます。Overview タブには、最初に監査の概要が表示され、続いて問題の行(重大と軽微の件数。問題の合計数であり、スコアの計算に使用される数そのものです)、判定セクション(FINDINGS_DATA の重大な問題数から算出される Safe to go/Needs revision と、カテゴリごとの 1~5 のスコアバー)、さらにレポートのパスが表示されます。レポート内の問題はすべて重大または軽微で、critical/warning/info/pass の段階はありません。Issues タブには、各重大/軽微な問題の根拠と修正方法が表示されます。また、各重大な問題には、根本原因を示すバッジ(Permissions only、Permissions + upstream、Upstream: data model / site code / Web API settings)と、1 文の根拠も表示されます。
7.3 HTML ファイルをレンダリングする
レンダリングスクリプトを実行します(必要に応じて出力ディレクトリが作成されます)。
node "${PLUGIN_ROOT}/scripts/render-audit-report.js" --output "<OUTPUT_PATH>" --data "<DATA_JSON_PATH>"
レンダリングスクリプトは既存のファイルを上書きしません。実行する前に、既定の出力パス(<PROJECT_ROOT>/docs/permissions-audit.html)がすでに存在するか確認してください。存在する場合は、permissions-audit-apr-2026.html や permissions-audit-post-migration.html など、コンテキストに応じたわかりやすい新しいファイル名を選んでください。選んだ名前を --output で指定し、reportPaths にも同じパスを使用します。
7.4 レポートを検証する
レンダリングしたレポートに対して、セマンティックバリデーターを実行します。
node "${PLUGIN_ROOT}/skills/audit-permissions/scripts/validate-audit.js" \
--report "<OUTPUT_PATH>" \
--data "<DATA_JSON_PATH>" \
--auditRunId "<AUDIT_RUN_ID>"
ローカル実行の初期化で実行 ID が返されなかった場合は、--data と --auditRunId を省略します。レポートの検証は引き続き実行する必要があります。
検証に成功するまで、レポートを提示したりコピーしたりしないでください。バリデーターはレポートから FINDINGS_DATA、INVENTORY_DATA、SCORECARD_DATA を読み取り、次の項目を確認します。重複のない、ディメンションに一致するプレフィックスを持つイシュー ID、major/minor の重大度、空ではない title/reasoning/fix、すべての major 所見に有効な rootCause と rootCauseReason があること(minor 所見にはいずれもないこと)、インベントリーが配列であること、所見から再計算した 3 つのカテゴリー別スコア、SUMMARY に major の件数と一致する判定が記載されていること(部分監査では、どちらの判定フレーズも含まれていないこと)、および未置換のプレースホルダーです。実行 ID とデータパスを指定すると、同じ決定的なコマンドでソースデータを再検証し、確定した集計メトリクスを算出したうえで、正常完了と経過時間を記録します。
検証に失敗した場合は、直前にレンダリングしたレポートを削除し、データファイルで指摘された問題を修正して、同じパスに再度レンダリングし、バリデーターを再実行します。検証エラーを修正するために、証拠の収集を繰り返さないでください。
バリデーターは telemetryStatus として、recorded_success、recorded_failure(レポートは有効ですが、そのソースデータを照合できず、failureStage: metrics_validation として実行が記録された状態)、disabled(このプラグインではテレメトリが完全に無効化されているため、何も記録されなかった状態)、failed、または not_requested を報告します。テレメトリはベストエフォートです。recorded_failure、disabled、failed のいずれも、レポートを無効にするものではありません。ペイロードを縮小して再試行したり、診断出力にレポートデータを含めたりしないでください。
一時ファイル audit-data.json と audit-evidence.json は、レポートの検証に成功した後にのみ削除してください。テレメトリのステータスはクリーンアップに影響しません。
7.5 ブラウザーで開く
実際の HTML 出力パスを、ユーザーのデフォルトブラウザーで開きます。
ステップ 8:所見を提示して記録する
8.1 スキルの使用を記録する
参照:
${PLUGIN_ROOT}/references/skill-tracking-reference.md
参照先のスキル追跡手順に従って、このスキルの使用を記録します。--skillName "AuditPermissions" を使用してください。
8.2 概要の提示
🚦 ゲート(plan · audit-permissions:6.fix-offer): 重大または軽微な問題に対する自動修正を適用するか、ユーザーに確認します。レポートはすでに作成されているため、ここで断っても HTML レポートがそのまま残るだけです。Dataverse やファイルシステムに変更は加えられません。
トリガー: フェーズ 8 で検出事項を集計し、レポートを保存した時点です。 確認する理由: ツールが table-permissions-architect エージェントをひそかに起動する可能性があります。デフォルトで承諾すると、ユーザーの意図に反して権限 YAML が書き込まれたり変更されたりするためです。 キャンセル時に残るもの: ありません。レポートは保存先に残ります。web-role / table-permission ファイルは書き込まれません。
ユーザーに次の概要を提示します。
- 判定 —
SCORECARD_DATAが null でない場合は、安全に進められます または 修正が必要です と、3 つのカテゴリスコアを表示します。それ以外の場合は、監査が部分的なものであることを伝え、判定の文言とスコアはどちらも省略します。 - 重大な問題の件数 — 早急な対応が必要な問題
- 軽微な問題の件数 — あれば望ましい改善や、今後に向けた提案
- レポートの場所 — HTML レポートの保存先
AskUserQuestionを使ってユーザーに確認します:「これらの問題を修正しますか?重大な問題を解決するために、テーブル権限を作成または更新できます。」
ユーザーが修正を希望する場合:
- 403 / Web API アクセスに関する問題(テーブル権限の不足、CRUD フラグの不足、スコープの誤り、append / appendto の不足、
$expandの読み取り対象範囲の不足など、Power Pages Web API から 403 Forbidden 応答が返る原因となる検出事項):Agentツールを使って table-permissions-architect エージェントを起動します。検出事項に含まれる具体的なテーブル、必要な CRUD フラグ、推奨スコープ、リレーションシップの詳細をプロンプトに含めてください。エージェントはサイトを分析し、権限計画を提案して、ユーザーの承認後に適切なテーブル権限 YAML ファイルを作成します。プロンプトの例:"Create table permissions for the following tables based on audit findings: <table1> needs Global scope with read:true; <table2> needs Parent scope under <parent_table> with read:true, create:true, append:true; <table3> needs appendto:true for lookups from <source_table>. The site project root is <PROJECT_ROOT>." - 権限以外の問題(未使用の権限、スコープを狭める提案、その他の軽微な問題など):必要な手動変更を説明するか、Web API 設定アーキテクトがサイト設定レベルの問題に対処できるよう
/integrate-webapiの実行を提案します。
付録 A — スコアリングモデル
スコアリングは統合された検出事項のみを入力とする純粋関数です。正確に再現してください。
カテゴリ
| カテゴリ | 評価軸 |
|---|---|
| 過剰な公開(セキュリティ) | 権限の適正化、匿名アクセスの衛生管理、セキュリティ態勢 |
| 公開不足(ユーザビリティとカバレッジ) | 意図のカバレッジ、テーブルのカバレッジ、ロールの完全性 |
| 正確性(有効性と整合性) | スコープの正確性、データモデルとの整合性、内部整合性 |
検出事項に基づくスコアの計算式
検出事項の集合について、nMajor と nMinor をそれぞれ重大な問題と軽微な問題の件数とします。その場合:
load = 1.0 * nMajor + 0.25 * nMinor
penalty = tanh(0.2 * load) # tanh maps [0,∞) → [0,1); penalty(0)=0
score = 5 - 4 * penalty # 0 issues → 5.00 ; grows → 1.00
score = round(score * 100) / 100 # 2 decimals
定数:MAJOR_LOAD = 1.0、MINOR_LOAD = 0.25、PENALTY_STEEPNESS = 0.2。
- カテゴリスコア =
scoreFromIssues(そのカテゴリの評価軸に属する、重複排除済みの検出事項をプールした集合)。スコアリングの単位はカテゴリです。評価軸ごとのスコアを平均してはいけません。 - 総合スコアはありません。 3 つのカテゴリスコアは横並びで報告し、単一の数値にまとめることはありません。
参考値(急峻度 0.2):検出事項 0 件 → 5.00、重大な問題 1 件 → 4.21、重大な問題 2 件 → 3.48、重大な問題 3 件 → 2.85、軽微な問題 1 件 → 4.80。最初の重大な問題による減点が常に最大であり、以降は減少します。
最終判定
判定はスコアではなく重大度によって決まります。
| 条件 | 判定 | 意味 |
|---|---|---|
| 重大な問題が 0 件 | そのまま進めて安全 | 重大な問題は特定されませんでした。 |
| 重大な問題が 1 件以上 | 修正が必要 | 1 件以上の重大な問題が特定されました。確認して対応してください。 |
軽微な問題が判定を変えることはありません — 引き続き記録され、カテゴリスコアも下げますが、ユーザーの判断に委ねられます。判定の文言は、ステップ 5.4 およびレポートにそのまま記載してください。
判定は参考情報です。監査で見つかった内容を要約したものであり、承認やリリースのゲートではありません。それに基づいて行動するかどうかの判断は人間のレビュアーに委ねられます。ユーザーにデプロイするよう、あるいはしないように指示しないでください。
付録 B — 9 つのディメンション
各ディメンションの定義を使用して、問題がどこに属するかを判断してください。順序は出力における正規の順序です。
1. 意図のカバレッジ — IC · 過少露出 — リクエスト内の認可に関連するすべての機能は、少なくとも 1 つの (ロール × テーブル × 操作) 許可に対応していますか? ユーザビリティルール: リクエストがレコードの管理・作成を意味しているのに、関連するテーブルに対する Create/Write/Delete をどのロールも一切持っていない (どこも読み取り専用) 場合、それはカバレッジの失敗 → 重大な問題です。
2. 権限のキャリブレーション — PC · 過剰露出 — (ロール、テーブル、操作) ごとに: 各 CRUD ビットは適切なサイズになっていますか — 過剰付与でも過少付与でもないですか?
3. スコープの正確性 — SC · 正確性 — スコープ (Global/Contact/Account/Parent/Self) はデータ所有モデルと一致していますか? ユーザーごと → Contact、公開掲示板 → Global、親レコードを通じて到達する子レコード → Parent、ユーザー自身の連絡先レコード → Self。
4. ロールの完全性 — RC · 過少露出 — リクエストによって示唆されるすべてのペルソナが Web ロールとして存在していますか? 不要なロールはありませんか? Anonymous + Authenticated は正しいですか?
5. テーブルのカバレッジ (権限) — TC · 過少露出 — ユーザー向けのすべてのテーブルに 1 つ以上の権限があり、システム用・非ユーザー向けのテーブルは正しく除外されていますか?
6. 匿名アクセスの衛生 — AH · 過剰露出 — リクエストが公開コンテンツを示唆する場合に限り、匿名には最小限の読み取りのみを許可します。正当化されない限り、匿名の書き込み・作成・削除は許可しません。
7. データモデルの整合性 — DM · 正確性 — 権限で参照されるすべてのテーブルが実在するデータモデルのテーブルに解決されますか? 存在しない・名前が誤ったテーブルはありませんか?
8. 内部整合性 — IS · 正確性 — YAML が有効ですか? 権限内のすべての Web ロール GUID が解決されますか? permissions-plan.html は YAML と一致していますか?
9. セキュリティ態勢 — SP · 過剰露出 — 全体的な最小権限。機密テーブル (Contact、システムテーブル) に対する予期しない広範な許可はありませんか? 共有レコードに対する回避可能な削除・書き込みはありませんか? Web API のフィールド許可リストが存在する場合、機密性の高い PII 列を広く公開していないか確認してください。
付録 C — 重大度ポリシーと監査↔グレーダーのブリッジ
重大度ポリシー(ルールを順番に適用し、最初に該当したものを優先)
重大度は major(実際の問題。サイトが動作しなくなる、データが漏えいする、または明らかに最適でない設定で修正すべきもの)と minor(見た目の問題、将来を見越したもの、あると望ましいもの)の2種類のみです。採点ツールに「info」区分はありません(監査における info の指摘は minor に分類します)。
基本方針 — セキュリティ最優先/最小権限。 アクセスを許可するのは、そのロールの実際の用途で必要な場合に限り、可能な限り範囲を絞ります。その必要性を超えてレコードにアクセスしたり変更したりできる場合は、他の点では「承認済み」のロールであっても、過剰な権限付与 → major です。
- 現実に起きている問題か、仮定上の問題か。 major は、問題が現時点で実際に存在する場合です(フローが動作しない、または見るべきではない対象にデータが公開されている)。minor は、将来の変更があった場合にのみ問題となるもの、または見た目の問題です。minor を示す表現の例:「後で…する場合」「可能性がある」「見直すべき」。
- 知る必要性/範囲の過剰さ。 major は、ロールが必要性を超えてレコードを読み取り/変更できる場合です。対象には、権限のない対象者(匿名ユーザー、またはデータの想定対象者以外のロール)がアクセスできる場合、または正当な権限を持つロールが、個人ごと/関係ごとのデータ(個人記録、PII、成績、名簿、保護者の連絡先)に対して Global 範囲の権限を持ち、自分の対象範囲だけでなくすべてのレコードにアクセスできる場合が含まれます。PII(メールアドレス、電話番号、保護者の連絡先、住所)は特に重大です。過剰に広い範囲が、真に公開/共有される参照用データ(お知らせ、施設、公開ディレクトリ)のみを対象とする場合は minor です。スタッフのロールが共有の作業キューを扱う場合は C16、子テーブルが親テーブルと同じ範囲に一致する場合は C17 に従います。
- 変更権限と読み取り権限の違い。 major は、望ましくない変更可能な権限付与(権限を付与すべきでないロールへの書き込み/作成/削除権限。匿名ユーザーへの変更権限は常に major)です。過剰に広い読み取り権限は、ルール (2) に該当しない限り minor です。必要な変更権限がないこと(管理/コンテンツ作成サイトに Create/Write/Delete 権限が一切ないこと)はフローの不具合であり、ルール (1) に基づき major です。
判断の基準となる例。 繰り返し発生する問題のカタログ(C1–C18、Step 5.1)が主な判断基準です。カタログが対象とするケースは、カタログの記載どおりに分類してください。さらに、繰り返し発生するケースが2つあります。
- スタッフ/教師/管理者が、PII を含む内部テーブルを Global 範囲で読み取れる → major(r2)。
- ロールが、必要性を超えてユーザーごとの運用データに Global 範囲の権限を持つ(教師が自分のクラスだけでなく、すべての生徒の成績を読み取り/編集できる)→ major(r2)。
監査結果 → 評価者の評価軸への対応付け
Step-4 の各指摘を評価軸に対応付けてから、上記の重大度ポリシーを適用し、major/minor を設定します。(監査の critical はほぼ常に → major、監査の info は → minor、監査の warning はポリシーを適用します。)
| 監査チェック/指摘 | 評価者の評価軸 | 評価者の一般的な重大度 |
|---|---|---|
| A. 参照されているテーブルへの権限がない | intent-coverage(+ table-coverage) | major |
| A. 未使用の権限(コード内で参照されていない) | privilege-calibration(C18、ロールごとに1件) | minor |
| B. 権限に Web ロールがない | role-completeness | major。有効な親チェーンを持つ Parent スコープの子には該当なし |
| C. 書き込み/削除を伴う Global スコープ | privilege-calibration / security-posture | major。ただし、スタッフ用の共有作業キュー(C16)と子テーブルとの同等性(C17)は除く |
| C. スコープをさらに限定できる | scope-correctness | 所有者ごと/PII の場合は major、それ以外は minor |
| D. 読み取りがない | intent-coverage | major |
| E. 作成がない · F. 書き込みがない · G. 削除がない | intent-coverage | major |
| E/F/G. 有効だが未使用 | privilege-calibration(C18、ロールごとに1件) | minor |
| F. ファイルアップロードがあるが、書き込みが無効 | intent-coverage | major |
| F. 読み取りなしの書き込み | privilege-calibration | major |
| H. append がない · H2. appendto がない | privilege-calibration / intent-coverage | major |
| H. append が有効だが不要 | privilege-calibration(C18、ロールごとに1件) | minor |
| I. 親チェーンが壊れている | internal-consistency | major |
J. $expand テーブルへの読み取りがない | intent-coverage | major |
| 匿名ユーザーが PII を読み取り可能 | anonymous-access-hygiene / security-posture | major |
スキーマバリデーターの error / warning / info | internal-consistency(または該当する評価軸) | error→major、warning→ポリシーを適用、info→minor |
重要な制約
- 読み取り専用の分析:このスキルは既存の構成とコードを読み取るだけです。ユーザーが問題の修正を明示的に依頼した場合(ステップ 8)を除き、ファイルを変更しません。スコアリングとレポート生成はすべて非変更操作です。
- スコアを捏造しない:必ず付録 Aに従って、統合した問題件数からスコアを算出してください。同じ問題なら、スコアも同じになります。
- 事前チェックの問題を正とする:ステップ 2.5 の問題(id、dimension)を統合とスコアリングに引き継ぎ、省略や重要度の引き下げを決して行わないでください。重複を統合した場合、重要度を上げることのみ認められます。
- 根本原因ごとに問題を 1 件とする:単一の根本原因を複数の問題に分割せず、独立した問題を過剰に統合しないでください。
- 最終ラベルの割り当ては 1 回だけ:ステップ 4 では事実に基づく候補を出力し、ステップ 5 だけで最終的な重要度、dimension、issue id を割り当てます。
- 説明文より到達可能性を優先する:ルーティングされた、または呼び出し可能なポータルの動作は、矛盾する計画文書より優先されます。明示的なランタイム/ビルドでの除外は、休眠中のソースコードより優先されます。曖昧さは軽微な正確性の問題であり、公開範囲の不足と決めつけないでください。
- クライアント側のフィルターは認可ではない:回避可能な制限付き行は、コンテンツの機密性に基づいて分類します。機密/センシティブな情報の露出は重大、センシティブでない下書き/ワークフローの露出は軽微です。
- 最小権限/セキュリティ優先を、dimension の重要度(付録 C)と再発する問題のカタログ(ステップ 5.1)に適用します。スタッフロールにすべてのレコードへのアクセス権があるわけではありません。関連するレコードのみが対象です。
- 管理者にテーブル権限は不要です。到達可能なサイトコードが管理者専用の CRUD を実行する場合を除き、権限がないことを問題として指摘しないでください。ステップ 5.1 の「管理者の例外」を参照してください。
- 決定論的な API 呼び出し:Dataverse API のクエリには、必ず Node.js スクリプト(
query-table-lookups.js、query-table-relationships.js)を使用してください。インラインの PowerShellInvoke-RestMethod呼び出しは使用しないでください。 - 分析中に質問しない:すべてのデータを自律的に収集し、チェックを実行してスコアを算出し、調査結果を提示してください。問題の修正についてユーザーに質問するのは最後だけです。
- セキュリティ:認証トークンを記録または表示しないでください。スクリプトは
getAuthToken()を介して内部的にトークンを取得します。 - 段階的な縮退:Dataverse API スクリプトが失敗した場合(終了コード 1)、API に依存するチェック(H/H2 の append/appendto 検証、I の親チェーン整合性)をスキップし、レポートにスキップしたチェックを記載してください。データモデルのマニフェストがない場合も監査結果は出力し、スコアを捏造する代わりに
SCORECARD_DATAをnullに設定してください。その場合、レポートには判定やスコアではなく部分監査の通知が表示され、SUMMARYに判定なしと記載されます(ステップ 5.4)。 - 入力に存在しないファイルやテーブルを捏造しないでください。