audit-permissions

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Audit Permissions

权限审计

Audit existing table permissions on a Power Pages code site. Analyze permissions against the site code and Dataverse metadata, then generate a visual HTML audit report with findings, reasoning, and suggested fixes.
对Power Pages代码站点上的现有表权限进行审计。对照站点代码和Dataverse元数据分析权限,然后生成包含检查结果、推理过程和建议修复方案的可视化HTML审计报告。

Workflow

工作流程

  1. Verify Site Deployment — Check that
    .powerpages-site
    folder and table permissions exist
  2. Gather Configuration — Read all web roles, table permissions, and site code
  3. Run Local Schema Validation — Use the shared validator to detect invalid permission/site-setting YAML before deeper analysis
  4. Analyze & Discover — Query Dataverse for relationships and lookup columns using deterministic scripts
  5. Run Audit Checks — Compare permissions against code usage and best practices
  6. Generate Report — Create the HTML audit report and display in browser
  7. Present Findings & Track — Summarize findings, record skill usage, and ask user if they want to fix issues
Important: Do NOT ask the user questions during analysis. Autonomously gather all data, then present findings.
  1. 验证站点部署 — 检查是否存在
    .powerpages-site
    文件夹和表权限
  2. 收集配置信息 — 读取所有Web角色、表权限和站点代码
  3. 运行本地架构验证 — 使用共享验证工具在深度分析前检测无效的权限/站点设置YAML
  4. 分析与发现 — 使用确定性脚本查询Dataverse中的关系和查找列
  5. 运行审计检查 — 对照代码使用情况和最佳实践验证权限
  6. 生成报告 — 创建HTML审计报告并在浏览器中显示
  7. 呈现结果与跟踪 — 总结检查结果,记录Skill使用情况,并询问用户是否需要修复问题
重要提示: 分析过程中请勿向用户提问。自主收集所有数据,然后呈现检查结果。

Task Tracking

任务跟踪

At the start of Step 1, create all tasks upfront using
TaskCreate
. Mark each task
in_progress
when starting and
completed
when done.
Task subjectactiveFormDescription
Verify site deploymentVerifying site deploymentCheck .powerpages-site folder and table permissions exist
Gather configurationGathering configurationRead web roles, table permissions, and site code
Run local schema validationValidating local permissions schemaRun shared validator against existing table permission and site setting YAML
Discover relationshipsDiscovering relationshipsQuery Dataverse for lookup columns and relationships
Run audit checksRunning audit checksCreate per-table tasks and run checklist (A–K) for each table, then cross-validate
Generate audit reportGenerating audit reportCreate HTML report and display in browser
Present findingsPresenting findingsSummarize results, record usage, and offer to fix issues
Note: The "Run audit checks" phase creates additional per-table tasks dynamically in Step 4.2. These per-table tasks track the systematic A–K checklist for each table independently.

在步骤1开始时,使用
TaskCreate
预先创建所有任务。开始任务时标记为
in_progress
,完成后标记为
completed
任务主题活动表单描述
验证站点部署正在验证站点部署检查是否存在.powerpages-site文件夹和表权限
收集配置信息正在收集配置信息读取Web角色、表权限和站点代码
运行本地架构验证正在验证本地权限架构对现有表权限和站点设置YAML运行共享验证工具
发现关系正在发现关系查询Dataverse中的查找列和关系
运行审计检查正在运行审计检查为每个表创建任务并运行检查表(A–K),然后进行交叉验证
生成审计报告正在生成审计报告创建HTML报告并在浏览器中显示
呈现结果正在呈现结果总结结果,记录使用情况,并提供修复问题的选项
注意: "运行审计检查"阶段会在步骤4.2中动态创建额外的单表任务。这些单表任务独立跟踪每个表的系统化A–K检查表执行情况。

Step 1: Verify Site Deployment

步骤1:验证站点部署

Use
Glob
to find:
  • **/powerpages.config.json
    — identifies the project root
  • **/.powerpages-site/table-permissions/*.tablepermission.yml
    — existing permissions
If no
.powerpages-site
folder exists, stop and tell the user to deploy first using
/deploy-site
. If no table permissions exist, note this as a critical finding (the site may have no data access configured) and continue the audit — there may still be code references that need permissions.

使用
Glob
查找:
  • **/powerpages.config.json
    — 识别项目根目录
  • **/.powerpages-site/table-permissions/*.tablepermission.yml
    — 现有权限
如果不存在
.powerpages-site
文件夹,请停止操作并告知用户先使用
/deploy-site
命令部署站点。 如果不存在表权限,请将此标记为关键检查结果(站点可能未配置任何数据访问权限)并继续审计 — 代码中仍可能存在需要权限的引用。

Step 2: Gather Configuration

步骤2:收集配置信息

2.1 Read Web Roles

2.1 读取Web角色

Read all files matching
**/.powerpages-site/web-roles/*.yml
. Extract
id
,
name
,
anonymoususersrole
,
authenticatedusersrole
from each.
读取所有匹配
**/.powerpages-site/web-roles/*.yml
的文件。从每个文件中提取
id
name
anonymoususersrole
authenticatedusersrole

2.2 Read Table Permissions

2.2 读取表权限

Read all files matching
**/.powerpages-site/table-permissions/*.tablepermission.yml
. For each permission, extract:
  • entityname
    (permission name)
  • entitylogicalname
    (table)
  • scope
    (numeric code)
  • read
    ,
    create
    ,
    write
    ,
    delete
    ,
    append
    ,
    appendto
    (boolean flags)
  • adx_entitypermission_webrole
    (array of web role UUIDs)
  • contactrelationship
    ,
    accountrelationship
    (if Contact/Account scope)
  • parententitypermission
    ,
    parentrelationship
    (if parent scope)
读取所有匹配
**/.powerpages-site/table-permissions/*.tablepermission.yml
的文件。对于每个权限,提取:
  • entityname
    (权限名称)
  • entitylogicalname
    (表)
  • scope
    (数字代码)
  • read
    create
    write
    delete
    append
    appendto
    (布尔标志)
  • adx_entitypermission_webrole
    (Web角色UUID数组)
  • contactrelationship
    accountrelationship
    (如果是联系人/账户范围)
  • parententitypermission
    parentrelationship
    (如果是父范围)

2.3 Analyze Site Code

2.3 分析站点代码

Search the site source code for:
  • Web API calls (
    /_api/
    )
  • Lookup bindings (
    @odata.bind
    )
  • File uploads (
    uploadFileColumn
    ,
    uploadFile
    ,
    upload*Photo
    ,
    upload*Image
    )
  • $expand
    usage (
    $expand
    ,
    buildExpandClause
    ,
    ExpandOption
    )
Also check for
.datamodel-manifest.json
in the project root for the authoritative table list.
Build a map of: which tables are referenced in code, which CRUD operations are performed on each, which lookup relationships are used, and which related tables are fetched via
$expand
(these need read permissions too).
在站点源代码中搜索:
  • Web API调用(
    /_api/
  • 查找绑定(
    @odata.bind
  • 文件上传(
    uploadFileColumn
    uploadFile
    upload*Photo
    upload*Image
  • $expand
    用法(
    $expand
    buildExpandClause
    ExpandOption
同时检查项目根目录中的
.datamodel-manifest.json
以获取权威表列表。
构建映射:代码中引用了哪些表、对每个表执行了哪些CRUD操作、使用了哪些查找关系、通过
$expand
获取了哪些相关表(这些表也需要读取权限)。

2.4 Run Shared Schema Validator

2.4 运行共享架构验证工具

Run the shared validator against the existing site:
powershell
$schemaValidation = node "${CLAUDE_PLUGIN_ROOT}/scripts/validate-permissions-schema.js" --projectRoot "<PROJECT_ROOT>"
Parse the JSON output and carry the findings into the audit. Treat:
  • error
    findings as critical
  • warning
    findings as warning
  • info
    findings as info
These findings should be included in the final audit report even if the later code/Dataverse analysis also finds additional issues.
After Step 3.1 determines
$envUrl
, if this audit is running locally with Dataverse access available, rerun the shared validator with live relationship verification enabled and merge any additional findings:
powershell
$schemaValidation = node "${CLAUDE_PLUGIN_ROOT}/scripts/validate-permissions-schema.js" --projectRoot "<PROJECT_ROOT>" --validate-dataverse-relationships --envUrl "$envUrl"
Use this Dataverse-backed relationship validation only for local runs. Do not require it in CI or other offline contexts.

对现有站点运行共享验证工具:
powershell
$schemaValidation = node "${CLAUDE_PLUGIN_ROOT}/scripts/validate-permissions-schema.js" --projectRoot "<PROJECT_ROOT>"
解析JSON输出并将检查结果带入审计。将:
  • error
    检查结果标记为关键
  • warning
    检查结果标记为警告
  • info
    检查结果标记为信息
即使后续的代码/Dataverse分析发现了其他问题,这些检查结果也应包含在最终审计报告中。
在步骤3.1确定
$envUrl
后,如果此审计在本地运行且可访问Dataverse,请重新运行共享验证工具并启用实时关系验证,合并额外的检查结果:
powershell
$schemaValidation = node "${CLAUDE_PLUGIN_ROOT}/scripts/validate-permissions-schema.js" --projectRoot "<PROJECT_ROOT>" --validate-dataverse-relationships --envUrl "$envUrl"
仅在本地运行时使用此基于Dataverse的关系验证。在CI或其他离线环境中请勿要求使用此功能。

Step 3: Analyze & Discover (Dataverse API)

步骤3:分析与发现(Dataverse API)

Use deterministic Node.js scripts for all Dataverse API calls. These scripts handle auth token acquisition, HTTP requests, and JSON parsing consistently.
对所有Dataverse API调用使用确定性Node.js脚本。这些脚本一致地处理身份验证令牌获取、HTTP请求和JSON解析。

3.1 Get Environment URL

3.1 获取环境URL

powershell
pac env who
Extract the
Environment URL
(e.g.,
https://org12345.crm.dynamics.com
). Store as
$envUrl
.
powershell
pac env who
提取
Environment URL
(例如:
https://org12345.crm.dynamics.com
)。存储为
$envUrl

3.2 Query Lookup Columns

3.2 查询查找列

For each table that has permissions with
create
or
write
enabled, use the lookup query script:
powershell
$lookups = node "${CLAUDE_PLUGIN_ROOT}/skills/audit-permissions/scripts/query-table-lookups.js" --envUrl "$envUrl" --table "<table_logical_name>"
The script returns a JSON array of
{ logicalName, targets }
for each lookup column.
After querying all tables with create or write permissions, build two maps from the combined results:
  1. Source map (table → lookup columns): For each queried table, record which lookup columns it has and their targets. Used in Section H2 to check
    appendto
    on the source table.
  2. Reverse target map (target table → list of source tables): For each target table found in any lookup's
    targets
    array, record which source table(s) reference it. Used in Section H to check
    append
    on the target table.
Example: querying
order_item
returns
[{ logicalName: "cr4fc_orderid", targets: ["cr4fc_order"] }]
  • Source map:
    order_item → [{ column: "cr4fc_orderid", targets: ["cr4fc_order"] }]
  • Reverse target map:
    cr4fc_order → [{ sourceTable: "order_item", column: "cr4fc_orderid" }]
Both maps are used in Sections H and H2:
  • The source table (with the lookup) needs
    appendto: true
    — it links TO other records (checked via the source map)
  • Each target table in
    targets
    needs
    append: true
    — other records link TO it (checked via the reverse target map)
对于每个启用了
create
write
权限的表,使用查找查询脚本:
powershell
$lookups = node "${CLAUDE_PLUGIN_ROOT}/skills/audit-permissions/scripts/query-table-lookups.js" --envUrl "$envUrl" --table "<table_logical_name>"
脚本返回每个查找列的
{ logicalName, targets }
JSON数组。
查询所有启用了创建或写入权限的表后,从合并结果中构建两个映射:
  1. 源映射(表 → 查找列):对于每个查询的表,记录它有哪些查找列及其目标。用于H2部分检查源表的
    appendto
    权限。
  2. 反向目标映射(目标表 → 源表列表):对于在任何查找的
    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 Query Relationships

3.3 查询关系

For tables with parent-scope permissions, verify the relationship names using the relationship query script:
powershell
$rels = node "${CLAUDE_PLUGIN_ROOT}/skills/audit-permissions/scripts/query-table-relationships.js" --envUrl "$envUrl" --table "<parent_table>"
The script returns a JSON array of
{ schemaName, referencedEntity, referencingEntity, referencingAttribute }
. Use
schemaName
to validate the
parentrelationship
value in parent-scope permissions.
对于具有父范围权限的表,使用关系查询脚本验证关系名称:
powershell
$rels = node "${CLAUDE_PLUGIN_ROOT}/skills/audit-permissions/scripts/query-table-relationships.js" --envUrl "$envUrl" --table "<parent_table>"
脚本返回
{ schemaName, referencedEntity, referencingEntity, referencingAttribute }
的JSON数组。使用
schemaName
验证父范围权限中的
parentrelationship
值。

Error Handling

错误处理

If any script exits with code 1, skip the API-dependent checks and note which checks were skipped in the report. Do NOT stop the entire audit for auth errors. Use the data model manifest and code analysis as fallback.

如果任何脚本以代码1退出,请跳过依赖API的检查,并在报告中注明跳过了哪些检查。请勿因身份验证错误而停止整个审计。使用数据模型清单和代码分析作为备选方案。

Step 4: Run Audit Checks

步骤4:运行审计检查

Use per-table task tracking to systematically run every audit check. Each check produces a finding with severity, title, reasoning, and a suggested fix. Findings can be
critical
,
warning
,
info
, or
pass
.
使用单表任务跟踪系统地运行每个审计检查。每个检查都会生成包含严重程度、标题、推理和建议修复方案的检查结果。检查结果可以是
critical
warning
info
pass

4.1 Build Audit Inventory

4.1 构建审计清单

First, build a combined list of all tables to audit from two sources:
  1. Tables referenced in code (from Step 2.3) — these may or may not have permissions
  2. Tables with existing permissions (from Step 2.2) — these may or may not be referenced in code
The union of these two sets is the complete audit scope. Each table will be audited from both directions: "does the code need a permission that doesn't exist?" and "does the permission match what the code actually does?"
首先,从两个来源构建所有要审计的表的组合列表:
  1. 代码中引用的表(来自步骤2.3) — 这些表可能有也可能没有权限
  2. 具有现有权限的表(来自步骤2.2) — 这些表可能被代码引用也可能没有
这两个集合的并集是完整的审计范围。每个表将从两个方向进行审计:“代码是否需要不存在的权限?”和“权限是否与代码实际执行的操作匹配?”

4.2 Create Per-Table Audit Tasks

4.2 创建单表审计任务

For each table in the audit inventory, create a task:
TaskCreate:
  subject: "Audit <table_logical_name>"
  activeForm: "Auditing <table_display_name> permissions"
  description: "Run all audit checks for <table_logical_name>"
Also create a summary task:
TaskCreate:
  subject: "Compile audit findings"
  activeForm: "Compiling audit findings"
  description: "Combine all per-table findings into the final report"
Use
TaskList
at any point to review progress and see which tables still need auditing.
对于审计清单中的每个表,创建一个任务:
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 Per-Table Audit Checklist

4.3 单表审计检查表

For each table, mark its task
in_progress
and run through the following checks in order. For every finding, note the specific evidence (file path, permission name, code pattern) that supports it. Skip checks that don't apply to this table.
A. Permission Existence
Does this table have a table permission?
  • If the table is referenced in code but has no permission → finding:
    • Severity:
      critical
    • Title:
      Missing permission for <table>
    • Reasoning: Which code files reference this table and what operations they perform
    • Fix: Create a permission with the appropriate scope and CRUD flags
  • If a permission exists but the table is not referenced in code → finding:
    • Severity:
      info
    • Title:
      Unused permission for <table>
    • Reasoning: The table is not referenced in any source code — the permission may be unnecessary
    • Fix: Review whether this permission is still needed
  • If both exist →
    pass
    , proceed to remaining checks
B. Web Role Association
Does the permission have web role(s) assigned?
  • Check
    adx_entitypermission_webrole
    — if empty or missing → finding:
    • Severity:
      warning
    • Title:
      Permission <name> has no web role association
    • Reasoning: A permission without a web role has no effect — no users will receive this access
    • Fix: Associate with the appropriate web role
  • If roles are assigned →
    pass
C. Scope Appropriateness
Is the scope the least-privileged option that fits?
  • Search the service code for scope-relevant patterns: contact-scoped filters (
    getCurrentContactId
    ,
    _contactid_value
    ,
    contactid
    ) and account-scoped filters (
    _accountid_value
    ,
    parentcustomerid
    )
  • If Global scope (
    756150000
    ) with
    write
    or
    delete
    enabled → finding:
    • Severity:
      warning
    • Title:
      Global scope with write/delete on <table>
    • Reasoning: Any user with this role can modify/delete any record in this table
    • Fix: Narrow to Contact or Account scope, or remove write/delete if not needed
  • If Global scope with only
    read
    pass
    (acceptable for public reference data)
  • If code uses contact-scoped filters but permission uses Global → finding:
    • Severity:
      warning
    • Title:
      Scope could be narrower for <table>
    • Reasoning: Code filters by current contact but permission grants Global access
    • Fix: Narrow to Contact scope
  • Otherwise →
    pass
D. Read Permission
Is
read
correctly set?
  • Search the service code for GET/list/get patterns for this table: API calls to
    /_api/<entity_set>
    , list/get functions (
    list<TableName>
    ,
    get<TableName>
    )
  • If code reads this table but
    read: false
    → finding:
    • Severity:
      critical
    • Title:
      Missing read permission for <table>
    • Reasoning: Code reads from this table but permission does not grant read access
    • Fix: Enable
      read: true
  • If
    read: true
    and code reads →
    pass
E. Create Permission
Is
create
correctly set?
  • Search the service code for POST/create patterns: POST method usage (
    method: 'POST'
    ), create functions (
    create<TableName>
    )
  • If code creates records but
    create: false
    → finding:
    • Severity:
      critical
    • Title:
      Missing create permission for <table>
    • Reasoning: Code creates records in this table but permission does not grant create access
    • Fix: Enable
      create: true
  • If
    create: true
    but no create patterns in code → finding:
    • Severity:
      info
    • Title:
      Create enabled but not used for <table>
    • Reasoning: No create operations found in code — permission may be overly permissive
    • Fix: Consider disabling
      create
      if not needed
  • If matched →
    pass
F. Write Permission
Is
write
correctly set?
  • Search the service code for PATCH/update/upload patterns: PATCH method usage (
    method: 'PATCH'
    ), update functions (
    update<TableName>
    ), file upload patterns (
    uploadFileColumn
    ,
    uploadFile
    ,
    upload*Photo
    ,
    upload*Image
    ,
    upload*File
    )
  • If code updates records but
    write: false
    → finding:
    • Severity:
      critical
    • Title:
      Missing write permission for <table>
    • Reasoning: Code updates records (or uploads files) in this table but permission does not grant write access
    • Fix: Enable
      write: true
  • If file upload patterns found but
    write: false
    → finding:
    • Severity:
      warning
    • Title:
      File upload detected but write is disabled on <table>
    • Reasoning: File uploads use PATCH which requires write permission
    • Fix: Enable
      write: true
  • If
    write: true
    but
    read: false
    → finding:
    • Severity:
      warning
    • Title:
      Write enabled without read on <table>
    • Reasoning: Users can modify records they cannot see, which is unusual and likely unintended
    • Fix: Enable
      read: true
  • If
    write: true
    but no write patterns in code → finding:
    • Severity:
      info
    • Title:
      Write enabled but not used for <table>
    • Reasoning: No update operations found in code — permission may be overly permissive
    • Fix: Consider disabling
      write
      if not needed
  • If matched →
    pass
G. Delete Permission
Is
delete
correctly set?
  • Search the service code for DELETE patterns: DELETE method usage (
    method: 'DELETE'
    ), delete functions (
    delete<TableName>
    )
  • If code deletes records but
    delete: false
    → finding:
    • Severity:
      critical
    • Title:
      Missing delete permission for <table>
    • Reasoning: Code deletes records in this table but permission does not grant delete access
    • Fix: Enable
      delete: true
  • If
    delete: true
    but no delete patterns in code → finding:
    • Severity:
      info
    • Title:
      Delete enabled but not used for <table>
    • Reasoning: No delete operations found in code — permission may be overly permissive
    • Fix: Consider disabling
      delete
      if not needed
  • If matched →
    pass
H. Append (target table check)
Does this table need
append: true
? Append is required on the target table — the table that other records link TO via lookup columns.
  • Check the reverse target map from Step 3.2: is this table referenced as a lookup target by any other table that has
    create
    or
    write
    permissions?
  • Also search the service code for
    @odata.bind
    references to this table's entity set (e.g.,
    /<entity_set>(
    )
  • If this table appears in the reverse target map (i.e., another table with create/write has a lookup to this table), but
    append: false
    → finding:
    • Severity:
      critical
    • Title:
      Missing append on <table>
    • Reasoning: Table
      <source_table>
      has lookup column
      <column>
      targeting this table and sets it during create/write. The target table needs append permission so records can be linked to it. Users will see "You don't have permission to associate or disassociate"
    • Fix: Enable
      append: true
  • If
    append: true
    and justified →
    pass
  • If
    append: true
    but this table does NOT appear in the reverse target map and no code references it as a lookup target → finding:
    • Severity:
      info
    • Title:
      Append enabled but not needed on <table>
    • Reasoning: No other table with create/write has a lookup to this table — append may be unnecessary
    • Fix: Consider disabling
      append
      if not needed
H2. AppendTo (source table check)
Does this table need
appendto: true
? AppendTo is required on the source table — the table that has lookup columns linking TO other records.
  • Check the source map from Step 3.2: does this table have lookup columns?
  • Also search the service code for
    @odata.bind
    patterns in create/update calls for this table
  • If this table has lookup columns (in source map or code) AND has
    create
    or
    write
    enabled, but
    appendto: false
    → finding:
    • Severity:
      critical
    • Title:
      Missing appendto on <table>
    • Reasoning: This table sets lookup column
      <column>
      targeting
      <target_table>
      during create/write, which requires appendto permission. Users will see "You don't have permission to associate or disassociate"
    • Fix: Enable
      appendto: true
  • If
    appendto: true
    and justified →
    pass
I. Parent Chain Integrity
If the permission has Parent scope (
756150003
):
  • Verify
    parententitypermission
    references a valid permission ID that exists
  • Verify
    parentrelationship
    is a valid Dataverse relationship (if API available, using Step 3.3 results)
  • If broken → finding:
    • Severity:
      critical
    • Title:
      Broken parent chain for <permission>
    • Reasoning: The parent permission reference is invalid — this permission will not grant any access
    • Fix: Correct the parent permission ID and/or relationship name
  • If valid →
    pass
J. $expand Related Table Coverage
Is this table fetched via
$expand
on another table's query?
  • Check the
    $expand
    analysis from Step 2.3 (search site source code for
    $expand
    ,
    buildExpandClause
    ,
    ExpandOption
    )
  • If this table is expanded from another table but has no table permission with
    read: true
    for the same web role → finding:
    • Severity:
      critical
    • Title:
      Missing read permission for expanded table <table>
    • Reasoning: This table is fetched via
      $expand
      on
      <parent_table>
      in
      <service_file>
      , but has no read permission. Power Pages enforces table permissions on every entity in the query.
    • Fix: Create a table permission with
      read: true
      for the same web role. For collection-valued expansions (one-to-many), use Parent scope with the relationship name. For single-valued expansions (lookups to reference data), use Global scope with read-only access.
  • If properly covered →
    pass
K. Record Findings & Complete
After all checks, mark the table's task as
completed
via
TaskUpdate
.
对于每个表,将其任务标记为
in_progress
,并按顺序运行以下检查。对于每个检查结果,记录支持它的具体证据(文件路径、权限名称、代码模式)。跳过不适用于此表的检查。
A. 权限存在性
此表是否有表权限?
  • 如果代码中引用了该表但没有权限 → 检查结果:
    • 严重程度:
      critical
    • 标题:
      Missing permission for <table>
      (<表>缺少权限)
    • 推理: 哪些代码文件引用了此表以及执行了哪些操作
    • 修复方案: 创建具有适当范围和CRUD标志的权限
  • 如果存在权限但代码中未引用该表 → 检查结果:
    • 严重程度:
      info
    • 标题:
      Unused permission for <table>
      (<表>的权限未被使用)
    • 推理: 源代码中未引用该表 — 该权限可能不必要
    • 修复方案: 审查此权限是否仍需保留
  • 如果两者都存在 →
    pass
    ,继续剩余检查
B. Web角色关联
权限是否关联了Web角色?
  • 检查
    adx_entitypermission_webrole
    — 如果为空或缺失 → 检查结果:
    • 严重程度:
      warning
    • 标题:
      Permission <name> has no web role association
      (权限<名称>未关联Web角色)
    • 推理: 未关联Web角色的权限无效 — 没有用户会获得此访问权限
    • 修复方案: 关联适当的Web角色
  • 如果已关联角色 →
    pass
C. 范围适当性
范围是否是符合需求的最小权限选项?
  • 在服务代码中搜索与范围相关的模式:联系人范围过滤器(
    getCurrentContactId
    _contactid_value
    contactid
    )和账户范围过滤器(
    _accountid_value
    parentcustomerid
  • 如果是全局范围(
    756150000
    )且启用了
    write
    delete
    → 检查结果:
    • 严重程度:
      warning
    • 标题:
      Global scope with write/delete on <table>
      (<表>的全局范围启用了写入/删除权限)
    • 推理: 拥有此角色的任何用户都可以修改/删除此表中的任何记录
    • 修复方案: 缩小到联系人或账户范围,或在不需要时移除写入/删除权限
  • 如果全局范围仅启用了
    read
    pass
    (对于公共参考数据是可接受的)
  • 如果代码使用联系人范围过滤器但权限使用全局范围 → 检查结果:
    • 严重程度:
      warning
    • 标题:
      Scope could be narrower for <table>
      (<表>的范围可以更窄)
    • 推理: 代码按当前联系人过滤,但权限授予全局访问权限
    • 修复方案: 缩小到联系人范围
  • 其他情况 →
    pass
D. 读取权限
read
是否设置正确?
  • 在服务代码中搜索此表的GET/列表/获取模式:对
    /_api/<entity_set>
    的API调用、列表/获取函数(
    list<TableName>
    get<TableName>
  • 如果代码读取此表但
    read: false
    → 检查结果:
    • 严重程度:
      critical
    • 标题:
      Missing read permission for <table>
      (<表>缺少读取权限)
    • 推理: 代码从此表读取数据,但权限未授予读取访问权限
    • 修复方案: 启用
      read: true
  • 如果
    read: true
    且代码读取 →
    pass
E. 创建权限
create
是否设置正确?
  • 在服务代码中搜索POST/创建模式:POST方法使用(
    method: 'POST'
    )、创建函数(
    create<TableName>
  • 如果代码创建记录但
    create: false
    → 检查结果:
    • 严重程度:
      critical
    • 标题:
      Missing create permission for <table>
      (<表>缺少创建权限)
    • 推理: 代码在此表中创建记录,但权限未授予创建访问权限
    • 修复方案: 启用
      create: true
  • 如果
    create: true
    但代码中没有创建模式 → 检查结果:
    • 严重程度:
      info
    • 标题:
      Create enabled but not used for <table>
      (<表>的创建权限已启用但未被使用)
    • 推理: 代码中未发现创建操作 — 权限可能过于宽松
    • 修复方案: 如果不需要,考虑禁用
      create
  • 如果匹配 →
    pass
F. 写入权限
write
是否设置正确?
  • 在服务代码中搜索PATCH/更新/上传模式:PATCH方法使用(
    method: 'PATCH'
    )、更新函数(
    update<TableName>
    )、文件上传模式(
    uploadFileColumn
    uploadFile
    upload*Photo
    upload*Image
    upload*File
  • 如果代码更新记录但
    write: false
    → 检查结果:
    • 严重程度:
      critical
    • 标题:
      Missing write permission for <table>
      (<表>缺少写入权限)
    • 推理: 代码更新此表中的记录(或上传文件),但权限未授予写入访问权限
    • 修复方案: 启用
      write: true
  • 如果发现文件上传模式但
    write: false
    → 检查结果:
    • 严重程度:
      warning
    • 标题:
      File upload detected but write is disabled on <table>
      (检测到文件上传但<表>的写入权限已禁用)
    • 推理: 文件上传使用PATCH方法,需要写入权限
    • 修复方案: 启用
      write: true
  • 如果
    write: true
    read: false
    → 检查结果:
    • 严重程度:
      warning
    • 标题:
      Write enabled without read on <table>
      (<表>启用了写入权限但未启用读取权限)
    • 推理: 用户可以修改他们无法查看的记录,这很不寻常且可能是无意的
    • 修复方案: 启用
      read: true
  • 如果
    write: true
    但代码中没有写入模式 → 检查结果:
    • 严重程度:
      info
    • 标题:
      Write enabled but not used for <table>
      (<表>的写入权限已启用但未被使用)
    • 推理: 代码中未发现更新操作 — 权限可能过于宽松
    • 修复方案: 如果不需要,考虑禁用
      write
  • 如果匹配 →
    pass
G. 删除权限
delete
是否设置正确?
  • 在服务代码中搜索DELETE模式:DELETE方法使用(
    method: 'DELETE'
    )、删除函数(
    delete<TableName>
  • 如果代码删除记录但
    delete: false
    → 检查结果:
    • 严重程度:
      critical
    • 标题:
      Missing delete permission for <table>
      (<表>缺少删除权限)
    • 推理: 代码在此表中删除记录,但权限未授予删除访问权限
    • 修复方案: 启用
      delete: true
  • 如果
    delete: true
    但代码中没有删除模式 → 检查结果:
    • 严重程度:
      info
    • 标题:
      Delete enabled but not used for <table>
      (<表>的删除权限已启用但未被使用)
    • 推理: 代码中未发现删除操作 — 权限可能过于宽松
    • 修复方案: 如果不需要,考虑禁用
      delete
  • 如果匹配 →
    pass
H. Append(目标表检查)
此表是否需要
append: true
?Append权限在目标表上是必需的 — 即其他表通过查找列链接到的表。
  • 检查步骤3.2中的反向目标映射:此表是否被任何具有
    create
    write
    权限的其他表引用为查找目标?
  • 同时在服务代码中搜索对此表实体集的
    @odata.bind
    引用(例如:
    /<entity_set>(
  • 如果此表出现在反向目标映射中(即具有创建/写入权限的另一个表具有指向此表的查找列),但
    append: false
    → 检查结果:
    • 严重程度:
      critical
    • 标题:
      Missing append on <table>
      (<表>缺少Append权限)
    • 推理:
      <source_table>
      具有指向此表的查找列
      <column>
      ,并在创建/写入时设置它。目标表需要Append权限,以便记录可以链接到它。用户将看到“您没有权限关联或取消关联”
    • 修复方案: 启用
      append: true
  • 如果
    append: true
    且有合理依据 →
    pass
  • 如果
    append: true
    但此表出现在反向目标映射中且代码未将其引用为查找目标 → 检查结果:
    • 严重程度:
      info
    • 标题:
      Append enabled but not needed on <table>
      (<表>的Append权限已启用但不需要)
    • 推理: 没有具有创建/写入权限的其他表具有指向此表的查找列 — Append权限可能不必要
    • 修复方案: 如果不需要,考虑禁用
      append
H2. AppendTo(源表检查)
此表是否需要
appendto: true
?AppendTo权限在源表上是必需的 — 即包含链接到其他记录的查找列的表。
  • 检查步骤3.2中的源映射:此表是否有查找列?
  • 同时在服务代码中搜索对此表的创建/更新调用中的
    @odata.bind
    模式
  • 如果此表具有查找列(在源映射或代码中)且启用了
    create
    write
    ,但
    appendto: false
    → 检查结果:
    • 严重程度:
      critical
    • 标题:
      Missing appendto on <table>
      (<表>缺少AppendTo权限)
    • 推理: 此表在创建/写入时设置指向
      <target_table>
      的查找列
      <column>
      ,这需要AppendTo权限。用户将看到“您没有权限关联或取消关联”
    • 修复方案: 启用
      appendto: true
  • 如果
    appendto: true
    且有合理依据 →
    pass
I. 父链完整性
如果权限具有父范围(
756150003
):
  • 验证
    parententitypermission
    引用了存在的有效权限ID
  • 验证
    parentrelationship
    是有效的Dataverse关系(如果API可用,使用步骤3.3的结果)
  • 如果损坏 → 检查结果:
    • 严重程度:
      critical
    • 标题:
      Broken parent chain for <permission>
      (<权限>的父链损坏)
    • 推理: 父权限引用无效 — 此权限将不会授予任何访问权限
    • 修复方案: 更正父权限ID和/或关系名称
  • 如果有效 →
    pass
J. $expand相关表覆盖
此表是否通过另一个表的查询中的
$expand
获取?
  • 检查步骤2.3中的
    $expand
    分析(在站点源代码中搜索
    $expand
    buildExpandClause
    ExpandOption
  • 如果此表从另一个表扩展获取,但没有针对同一Web角色的
    read: true
    表权限 → 检查结果:
    • 严重程度:
      critical
    • 标题:
      Missing read permission for expanded table <table>
      (扩展表<表>缺少读取权限)
    • 推理: 此表在
      <service_file>
      中通过
      <parent_table>
      $expand
      获取,但没有读取权限。Power Pages会对查询中的每个实体强制执行表权限。
    • 修复方案: 为同一Web角色创建具有
      read: true
      的表权限。对于集合值扩展(一对多),使用带有关系名称的父范围。对于单值扩展(引用数据的查找),使用具有只读访问权限的全局范围。
  • 如果覆盖正确 →
    pass
K. 记录结果并完成
所有检查完成后,通过
TaskUpdate
将表的任务标记为
completed

4.4 Cross-Table Validation

4.4 跨表验证

After all per-table audits are complete, run these cross-table checks:
  1. Append/AppendTo consistency: Using the source map and reverse target map from Step 3.2, verify: (a) every source table (with lookups and create/write) has
    appendto: true
    , (b) every target table in the reverse map has
    append: true
    , (c) no table has
    appendto: true
    without lookup columns in the source map, (d) no table has
    append: true
    without being in the reverse target map
  2. $expand coverage: For every
    $expand
    usage, verify the expanded table has
    read: true
  3. Parent chain completeness: For every Parent scope permission, verify the parent permission exists and is valid
  4. Web role consistency: If two related tables (e.g., parent and child) are accessed by the same feature, verify they share the same web role assignment
Use
TaskList
to review all completed audits, then mark the "Compile audit findings" task as
in_progress
and proceed to Step 5.

所有单表审计完成后,运行这些跨表检查:
  1. Append/AppendTo一致性:使用步骤3.2中的源映射和反向目标映射,验证:(a) 每个具有查找列和创建/写入权限的源表都有
    appendto: true
    ,(b) 反向映射中的每个目标表都有
    append: true
    ,(c) 没有表在源映射中没有查找列的情况下具有
    appendto: true
    ,(d) 没有表在未出现在反向目标映射中的情况下具有
    append: true
  2. $expand覆盖:对于每个
    $expand
    用法,验证扩展表具有
    read: true
  3. 父链完整性:对于每个父范围权限,验证父权限存在且有效
  4. Web角色一致性:如果两个相关表(例如父表和子表)被同一功能访问,验证它们共享相同的Web角色分配
使用
TaskList
查看所有已完成的审计,然后将“Compile audit findings”任务标记为
in_progress
并继续步骤5。

Step 5: Generate Report

步骤5:生成报告

5.1 Determine Output Location

5.1 确定输出位置

  • If working in context of a website (project root with
    powerpages.config.json
    exists): write to
    <PROJECT_ROOT>/docs/permissions-audit.html
  • Otherwise: write to the system temp directory
  • 如果在网站上下文中工作(存在带有
    powerpages.config.json
    的项目根目录):写入
    <PROJECT_ROOT>/docs/permissions-audit.html
  • 否则:写入系统临时目录

5.2 Prepare Data

5.2 准备数据

Do NOT generate HTML manually or read/modify the template yourself. Use the
render-audit-report.js
script which mechanically reads the template and replaces placeholder tokens with your data.
Write a temporary JSON data file (e.g.,
<OUTPUT_DIR>/audit-data.json
) with these keys:
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 */]
}
FINDINGS_DATA format:
json
{
  "id": "f1",
  "severity": "critical",
  "title": "Missing permission for cra5b_product",
  "table": "cra5b_product",
  "scope": null,
  "permission": null,
  "reasoning": "The table cra5b_product is referenced in src/services/productService.ts with GET requests to /_api/cra5b_products, but no table permission exists for this table.",
  "fix": "Create a table permission with Global scope and read-only access for the Anonymous Users role.",
  "details": "Referenced in: src/services/productService.ts (line 23), src/components/ProductList.tsx (line 45)"
}
  • severity
    : One of
    critical
    ,
    warning
    ,
    info
    ,
    pass
  • table
    : The table logical name this finding relates to (or
    null
    for general findings)
  • scope
    : The current scope if applicable (numeric code or friendly name), or
    null
  • permission
    : The permission name if this finding is about an existing permission, or
    null
  • reasoning
    : Detailed explanation of why this is an issue — reference specific code files, line patterns, or Dataverse metadata
  • fix
    : Actionable suggestion for how to resolve the issue (or
    null
    for
    pass
    findings)
  • details
    : Additional context like file references, column names, or relationship details
INVENTORY_DATA format:
json
{
  "name": "Product - Anonymous Read",
  "table": "cra5b_product",
  "scope": "Global",
  "roles": ["Anonymous Users"],
  "read": true,
  "create": false,
  "write": false,
  "delete": false,
  "append": true,
  "appendto": false
}
请勿手动生成HTML或自行读取/修改模板。 使用
render-audit-report.js
脚本,该脚本会自动读取模板并将占位符标记替换为您的数据。
编写临时JSON数据文件(例如:
<OUTPUT_DIR>/audit-data.json
),包含以下键:
json
{
  "SITE_NAME": "站点名称(来自powerpages.config.json或文件夹名称)",
  "AUDIT_DESC": "Contoso门户的表权限安全审计",
  "SUMMARY": "审计结果的2-3句话摘要",
  "FINDINGS_DATA": [/* 检查结果对象数组 */],
  "INVENTORY_DATA": [/* 当前权限对象数组 */]
}
FINDINGS_DATA格式:
json
{
  "id": "f1",
  "severity": "critical",
  "title": "Missing permission for cra5b_product",
  "table": "cra5b_product",
  "scope": null,
  "permission": null,
  "reasoning": "The table cra5b_product is referenced in src/services/productService.ts with GET requests to /_api/cra5b_products, but no table permission exists for this table.",
  "fix": "Create a table permission with Global scope and read-only access for the Anonymous Users role.",
  "details": "Referenced in: src/services/productService.ts (line 23), src/components/ProductList.tsx (line 45)"
}
  • severity
    : 可选值为
    critical
    warning
    info
    pass
  • table
    : 此检查结果相关的表逻辑名称(或对于一般检查结果为
    null
  • scope
    : 适用的当前范围(数字代码或友好名称),或
    null
  • permission
    : 如果此检查结果是关于现有权限,则为权限名称,否则为
    null
  • reasoning
    : 详细解释为什么这是一个问题 — 引用特定代码文件、行模式或Dataverse元数据
  • fix
    : 解决问题的可行建议(对于
    pass
    检查结果为
    null
  • details
    : 附加上下文,如文件引用、列名称或关系详细信息
INVENTORY_DATA格式:
json
{
  "name": "Product - Anonymous Read",
  "table": "cra5b_product",
  "scope": "Global",
  "roles": ["Anonymous Users"],
  "read": true,
  "create": false,
  "write": false,
  "delete": false,
  "append": true,
  "appendto": false
}

5.3 Render the HTML File

5.3 渲染HTML文件

Run the render script (it creates the output directory if needed):
powershell
node "${CLAUDE_PLUGIN_ROOT}/scripts/render-audit-report.js" --output "<OUTPUT_PATH>" --data "<DATA_JSON_PATH>"
Delete the temporary data JSON file after the script succeeds.
运行渲染脚本(它会在需要时创建输出目录):
powershell
node "${CLAUDE_PLUGIN_ROOT}/scripts/render-audit-report.js" --output "<OUTPUT_PATH>" --data "<DATA_JSON_PATH>"
脚本成功后删除临时数据JSON文件。

5.4 Open in Browser

5.4 在浏览器中打开

Open the generated HTML file in the user's default browser.

在用户的默认浏览器中打开生成的HTML文件。

Step 6: Present Findings & Track

步骤6:呈现结果与跟踪

6.1 Record Skill Usage

6.1 记录Skill使用情况

Reference:
${CLAUDE_PLUGIN_ROOT}/references/skill-tracking-reference.md
Follow the skill tracking instructions in the reference to record this skill's usage. Use
--skillName "AuditPermissions"
.
参考:
${CLAUDE_PLUGIN_ROOT}/references/skill-tracking-reference.md
按照参考中的Skill跟踪说明记录此Skill的使用情况。使用
--skillName "AuditPermissions"

6.2 Present Summary

6.2 呈现摘要

Present a summary to the user:
  1. Critical findings count — these need immediate attention
  2. Warning findings count — should be addressed
  3. Report location — where the HTML file was saved
  4. Ask the user using
    AskUserQuestion
    : "Would you like me to fix any of these issues? I can create or update table permissions to resolve the critical and warning findings."
If the user wants fixes applied:
  • For 403 / Web API access issues (missing table permissions, missing CRUD flags, incorrect scope, missing append/appendto, missing
    $expand
    read coverage — any finding that would result in a 403 Forbidden response from the Power Pages Web API): Spawn the table-permissions-architect agent using the
    Agent
    tool. Pass it a prompt that includes the specific tables, the required CRUD flags, scope recommendations, and relationship details from the audit findings. The agent will analyze the site, propose a permissions plan, and create the correct table permission YAML files after user approval. Example prompt:
    "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>."
  • For non-permission issues (e.g., unused permissions, scope narrowing suggestions, informational findings): explain what manual changes are needed or suggest running
    /integrate-webapi
    so the Web API settings architect can address site-setting-level issues.

向用户呈现摘要:
  1. 关键检查结果数量 — 这些需要立即关注
  2. 警告检查结果数量 — 应该得到解决
  3. 报告位置 — HTML文件的保存位置
  4. 询问用户 使用
    AskUserQuestion
    :“您希望我修复这些问题中的任何一个吗?我可以创建或更新表权限来解决关键和警告检查结果。”
如果用户希望应用修复:
  • 对于403 / Web API访问问题(缺少表权限、缺少CRUD标志、范围不正确、缺少append/appendto权限、缺少
    $expand
    读取覆盖 — 任何会导致Power Pages Web API返回403 Forbidden响应的检查结果):使用
    Agent
    工具生成table-permissions-architect代理。向其传递包含特定表、所需CRUD标志、范围建议和审计结果中关系详细信息的提示。代理将分析站点,提出权限计划,并在用户批准后创建正确的表权限YAML文件。示例提示:
    "根据审计结果为以下表创建表权限:<table1>需要全局范围且read:true;<table2>需要在<parent_table>下的父范围且read:true, create:true, append:true;<table3>需要为来自<source_table>的查找启用appendto:true。站点项目根目录是<PROJECT_ROOT>。"
  • 对于非权限问题(例如:未使用的权限、范围缩小建议、信息性检查结果):解释需要进行哪些手动更改,或建议运行
    /integrate-webapi
    以便Web API设置架构师可以解决站点设置级别的问题。

Critical Constraints

关键约束

  • Read-only analysis: This skill only reads existing configuration and code. It does NOT modify any files unless the user explicitly asks to fix issues.
  • Deterministic API calls: Always use the Node.js scripts (
    query-table-lookups.js
    ,
    query-table-relationships.js
    ) for Dataverse API queries — never use inline PowerShell
    Invoke-RestMethod
    calls.
  • No questions during analysis: Autonomously gather all data, run checks, and present findings. Only ask the user at the end about fixing issues.
  • Security: Never log or display auth tokens. The scripts handle token acquisition internally via
    getAuthToken()
    .
  • Graceful degradation: If Dataverse API scripts fail (exit code 1), skip API-dependent checks (H/H2 append/appendto validation, I parent chain integrity) and note in the report which checks were skipped.
  • 只读分析:此Skill仅读取现有配置和代码。除非用户明确要求修复问题,否则它不会修改任何文件。
  • 确定性API调用:始终使用Node.js脚本(
    query-table-lookups.js
    query-table-relationships.js
    )进行Dataverse API查询 — 永远不要使用内联PowerShell
    Invoke-RestMethod
    调用。
  • 分析过程中不提问:自主收集所有数据,运行检查,并呈现结果。仅在最后询问用户是否需要修复问题。
  • 安全性:永远不要记录或显示身份验证令牌。脚本通过
    getAuthToken()
    内部处理令牌获取。
  • 优雅降级:如果Dataverse API脚本失败(退出代码1),请跳过依赖API的检查(H/H2 append/appendto验证、I父链完整性),并在报告中注明跳过了哪些检查。